Посещение базы данных – вставка нескольких записей в один запрос

У меня есть проект Laravel, в который я хотел бы добавить более 900 городов в базу данных в качестве посева базы данных.

Например, я мог бы сделать это следующим образом:

$state_id = State::whereName('state name')->pluck('id'); $city = new City(); $city->name = 'my city name'; $city->state_id = $state_id; $city->save(); 

и в моей модели города я определил сохранение как:

 public function save(array $options = array()) { $this->url = $this->createUrl($this->name); parent::save($options); } 

поэтому он также создает URL для города.

Я могу поместить в 900 раз такой блок кодов, но есть одна проблема – он будет запускаться в отдельных запросах, поэтому для вставки этих данных в базу данных потребуется более 30 секунд.

Я могу сделать это, например, следующим образом:

 DB::table('cities')->insert( [ [ 'name' => 'City name', 'url' => Slug::create('City name'), 'created_at' => $now, 'updated_at' => $now, 'state_id' => $state_id ], [ 'name' => 'City name 2', 'url' => Slug::create('City name 2'), 'created_at' => $now, 'updated_at' => $now, 'state_id' => $state_id ], ]); 

и таким образом я могу вставить много записей в один SQL-запрос, но, на мой взгляд, это не очень хорошее решение – мне нужно вручную установить все поля базы данных, но для вставки всех данных в базу данных требуется всего 3-4 секунды.

Вопрос в том, возможно ли создавать модели и использовать какой-либо магический метод, чтобы готовый PHP-массив использовать его в многопильных вставках (я читал, что Eloquent нельзя использовать для вставки нескольких записей в один запрос)?

Я думаю, что гораздо лучше будет код вроде этого:

 $state_id = State::whereName('state name')->pluck('id'); $city = new City(); $city->name = 'my city name'; $city->state_id = $state_id; $city1 = $city->saveFake(); // magic method that returns complete array $city = new City(); $city->name = 'my city name'; $city->state_id = $state_id; $city2 = $city->saveFake(); // magic method that returns complete array DB::table('cities')->insert( [ $city1, $city2, ]); 

Вместо функции saveFake () вы можете:

 $city->attributesToArray() 

Это вернет все атрибуты, которые должны быть сохранены в таблице.

Вы можете добавить их в массив и поместить в функцию вставки.

Это приведет к чему-то вроде этого:

 $state_id = State::whereName('state name')->pluck('id'); $cities = array(); $city = new City(); $city->name = 'my city name'; $city->state_id = $state_id; $cities[] = $city->attributesToArray(); $city = new City(); $city->name = 'my city name'; $city->state_id = $state_id; $cities[] = $city->attributesToArray(); DB::table('cities')->insert($cities); 
 $cities = []; $cities[] = new City(...); $cities[] = new City(...); $table = with(new City)->getTable(); $data = array_map(function($city) { return $city->getAttributes(); }, $cities); DB::table($table)->insert($data); 

getAttributes возвращает «необработанные» базовые атрибуты, которые вставляются в базу данных.

Имейте в виду, что это приведет к обходу событий Eloquent, таких как сохранение / создание.

Также имейте в виду, что если $data станет большим, вы можете столкнуться с проблемами памяти. Используйте что-то вроде array_chunk() чтобы разделить его, если это так.