Заполнение базы данных — вставка нескольких записей в один запрос

У меня есть проект 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,
    ]);

person Marcin Nabiałek    schedule 22.08.2014    source источник


Ответы (2)


Вместо вашей функции 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);
person PaulusDixit    schedule 22.08.2014
comment
Боюсь, это не работает. Он будет возвращать только name и state_id в массиве, а не другие поля (created_at, modified_at или url, которые вычисляются на основе name в функции save) - person Marcin Nabiałek; 22.08.2014
comment
Вы можете добавить их вручную в свою модель. Просто добавьте attributeToArray() и добавьте поля, которые вы хотите добавить вручную. И вызовите родительскую функцию. - person PaulusDixit; 22.08.2014
comment
Дело в том, что я не хочу добавлять их вручную. Если бы я хотел, я бы использовал метод, который я показал в своем вопросе, где мне даже не нужно создавать модель - person Marcin Nabiałek; 22.08.2014
comment
Если вам нужны все переменные, вы можете использовать toArray(). Вы уже реализовали мутатор для своего URL? как 2_ - person PaulusDixit; 22.08.2014

$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(), чтобы разделить его, если это так.

person Andreas    schedule 26.08.2014