问题
I have a Laravel project where I would like to insert above 900 cities into database as database seeding.
For example I could do it this way:
$state_id = State::whereName('state name')->pluck('id');
$city = new City();
$city->name = 'my city name';
$city->state_id = $state_id;
$city->save();
and in my City model I have defined save as:
public function save(array $options = array()) {
$this->url = $this->createUrl($this->name);
parent::save($options);
}
so it creates also url for the city.
I can put 900 times such block of codes but there is one problem - it will run in separate queries, so it will take above 30 seconds to insert those data to database.
I can do it for example this way:
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
],
]);
and this way I can insert many records in one SQL query but in my opinion it's not very nice solution - I need to manually set all database fields but it takes only 3-4 seconds to insert all the data into database.
The question is - is it possible to create models and using some magic method return ready PHP array to use it in multpile inserts ( I read that Eloquent cannot be used to insert multiple records in one query) ?
I think much better would be code something like this:
$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,
]);
回答1:
Instead of your saveFake() function you could do:
$city->attributesToArray()
This will return all the attributes that have to be stored in the table.
You could add these to an array and put it in an insert function.
This would result in something like this:
$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);
回答2:
$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
returns the "raw" underlying attributes that are inserted into the database.
Keep in mind that this will bypass Eloquent events like saving/creating.
Also keep in mind that if $data
becomes to big, you might run into memory issues. Use something like array_chunk()
to split it up if that's the case.
来源:https://stackoverflow.com/questions/25445607/database-seeding-insert-multiple-records-in-one-query