Заполнение базы данных в файле миграции Laravel

115

Я только изучаю Laravel, и у меня есть рабочий файл миграции, создающий таблицу пользователей. Я пытаюсь заполнить запись пользователя в рамках миграции:

public function up()
{
    Schema::create('users', function($table){

        $table->increments('id');
        $table->string('email', 255);
        $table->string('password', 64);
        $table->boolean('verified');
        $table->string('token', 255);
        $table->timestamps();

        DB::table('users')->insert(
            array(
                'email' => 'name@domain.com',
                'verified' => true
            )
        );

    });
}

Но при запуске появляется следующая ошибка php artisan migrate:

SQLSTATE[42S02]: Base table or view not found: 1146 Table 'vantage.users' doesn't exist

Очевидно, это связано с тем, что Artisan еще не создал таблицу, но вся документация, похоже, говорит о том, что есть способ использовать Fluent Query для заполнения данных как части миграции.

Кто-нибудь знает как? Спасибо!

Адам Хопкинсон
источник

Ответы:

215

Не помещайте DB :: insert () внутри Schema :: create (), потому что метод create должен завершить создание таблицы, прежде чем вы сможете вставить материал. Попробуйте вместо этого:

public function up()
{
    // Create the table
    Schema::create('users', function($table){
        $table->increments('id');
        $table->string('email', 255);
        $table->string('password', 64);
        $table->boolean('verified');
        $table->string('token', 255);
        $table->timestamps();
    });

    // Insert some stuff
    DB::table('users')->insert(
        array(
            'email' => 'name@domain.com',
            'verified' => true
        )
    );
}
BenjaminRH
источник
5
а как вставить несколько данных?
Сахбаз
6
@ SuperMario'sYoshi, я думаю, что-то вроде этогоDB::table('users')->insert([ ['email' => 'taylor@example.com', 'votes' => 0], ['email' => 'dayle@example.com', 'votes' => 0] ]);
Денис
80

Я знаю, что это старый пост, но, поскольку он появляется в поиске Google, я подумал, что поделюсь некоторыми знаниями здесь. @ erin-geyer указал, что смешивание миграций и сидеров может создать головную боль, а @justamartin возразил, что иногда вам нужно / нужно, чтобы данные были заполнены как часть вашего развертывания.

Я бы пошел еще дальше и сказал, что иногда желательно иметь возможность последовательно развертывать изменения данных, чтобы вы могли, например, развернуть на промежуточной стадии, убедиться, что все в порядке, а затем развернуть в производственной среде с уверенностью в тех же результатах. (и не нужно помнить, что нужно выполнить какой-то шаг вручную).

Тем не менее, разделение семени и миграции по-прежнему имеет значение, поскольку это две взаимосвязанные, но разные проблемы. Наша команда пошла на компромисс, создав миграции, которые вызывают сидеры. Это выглядит так:

public function up()
{
    Artisan::call( 'db:seed', [
        '--class' => 'SomeSeeder',
        '--force' => true ]
    );
}

Это позволяет вам выполнить семя один раз, как миграцию. Вы также можете реализовать логику, предотвращающую или улучшающую поведение. Например:

public function up()
{
    if ( SomeModel::count() < 10 )
    {
        Artisan::call( 'db:seed', [
            '--class' => 'SomeSeeder',
            '--force' => true ]
        );
    }
}

Это, очевидно, приведет к условному выполнению вашего сидера, если имеется менее 10 SomeModels. Это полезно, если вы хотите включить сидер в качестве стандартного сидера, который запускается как при вызове, artisan db:seedтак и при миграции, чтобы вы не «удваивались». Вы также можете создать обратную сеялку, чтобы откаты работали должным образом, например

public function down()
{
    Artisan::call( 'db:seed', [
        '--class' => 'ReverseSomeSeeder',
        '--force' => true ]
    );
}

Второй параметр --forceнеобходим, чтобы сеялка могла работать в производственной среде.

Даррилкун
источник
2
Это, безусловно, лучший ответ. Поддерживаемый код, разделяющий проблемы!
helsont
18
Я бы внимательно рассмотрел долгосрочные последствия вызова сидов из сценариев миграции. Сценарии миграции версируются по дате и времени, в то время как сеялки обычно нет. Во время разработки потребности сеялки часто меняются, что приводит к возможности версий скриптов миграции, запускающих сеялки без поддержки версий, что нарушает идемпотентность. Другими словами, ежедневное выполнение одного и того же набора сценариев миграции может дать разные результаты.
originalbryan
2
Прошло некоторое время с тех пор, как я опубликовал это, и я хотел поделиться своим опытом использования этой техники. В целом, это сработало для нас, и если бы мне пришлось делать это снова, я бы сделал это. Тем не менее, есть одна проблема, о которой следует знать. @originalbryan совершенно прав, и следствием этого является то, что мы иногда сталкиваемся с ситуациями, когда миграции прерываются при развертывании новой БД, потому что по мере запуска миграций сидер (и модель) более актуальны, чем база данных (поскольку мы можем засеять до полного обновления схемы). Когда это произойдет, мы обновляем старую миграцию, чтобы решить эту проблему.
darrylkuhn
@darrylkuhn Я слышал, что обновлять старые файлы миграции - не лучшая практика - вместо обновления старых файлов вы должны создать новый файл миграции - это «рабочий процесс» для файлов миграции по замыслу
Камил Келчевски
2
Весь язык Laravel подразумевает, что сидер предназначен для тестовых данных, поэтому я думаю, что это следует учитывать при проектировании. Важно различать данные, которые являются частью приложения, и данные тестирования, и включение необходимых данных непосредственно в миграцию делает это различие очень четким.
Бреттинс
13

Вот очень хорошее объяснение того, почему использование Laravel Database Seeder предпочтительнее использования Migrations: http://laravelbook.com/laravel-database-seeding/

Хотя, следование инструкциям в официальной документации - гораздо лучшая идея, потому что реализация, описанная по приведенной выше ссылке, кажется, не работает и является неполной. http://laravel.com/docs/migrations#database-seeding

Эрин Гейер
источник
1
Я согласен с тобой, Эрин. Не смешивайте миграции с исходными данными, потому что очень вероятно, что вы захотите заполнить некоторые данные в своей среде разработки, но не в производственной среде.
Даниэль Вигерас
18
Хороший момент, но есть ситуации, когда некоторые данные должны существовать в производственной среде. Например, должен существовать самый первый администратор по умолчанию, чтобы клиент мог войти в систему в первый раз, должны существовать некоторые предустановленные роли авторизации, некоторые данные бизнес-логики также могут потребоваться немедленно. Таким образом, я думаю, что обязательные данные должны быть добавлены к миграциям (чтобы вы могли также увеличивать / уменьшать записи данных через отдельные миграции), но семена можно оставить для разработки.
JustAMartin
Небольшая заметка; теперь ссылка на
раздачу
3

Это должно делать то, что вы хотите.

public function up()
{
    DB::table('user')->insert(array('username'=>'dude', 'password'=>'z19pers!'));
}
струны28
источник
1

Еще один простой способ сделать это - определить частный метод, который создает экземпляр и сохраняет соответствующую модель.

public function up()
{
    Schema::create('roles', function (Blueprint $table) {
        $table->increments('id');
        $table->string('label', 256);
        $table->timestamps();
        $table->softDeletes();
    });

    $this->postCreate('admin', 'user');
}

private function postCreate(string ...$roles)  {
    foreach ($roles as $role) {
        $model = new Role();
        $model->setAttribute('label', $role);
        $model->save();
    }
}

С помощью этого решения Eloquent будет генерировать поля временных меток.

РЕДАКТИРОВАТЬ: лучше использовать систему сидера, чтобы различать создание структуры базы данных и заполнение базы данных.

Максимилиан Ди Дио
источник
Мне нравится этот ... он обслуживает именно то, что мне нужно сделать, добавляя по умолчанию несколько пользовательских ролей при миграции. Необходимо убедиться, что вы либо импортируете модель, либо ссылаетесь непосредственно на нее $model = new App\UserRoles();, но в остальном ... отлично!
FAB
1

Я попробовал этот метод вставки в БД, но, поскольку он не использует модель, он проигнорировал вялую черту, которая была у меня на модели. Итак, учитывая, что модель для этой таблицы существует, я решил, что после ее переноса модель будет доступна для вставки данных. И я придумал это:

public function up() {
        Schema::create('parent_categories', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name');
            $table->string('slug');
            $table->timestamps();
        });
        ParentCategory::create(
            [
                'id' => 1,
                'name' => 'Occasions',
            ],
        );
    }

Это сработало правильно, а также приняло во внимание свойство sluggable в моей модели для автоматического создания слага для этой записи и также использует временные метки. NB. Добавление идентификатора не было обязательным, однако в этом примере мне нужны были конкретные идентификаторы для моих категорий. Протестировано на Laravel 5.8.

Эндрю Арскотт
источник
0

Если вы уже заполнили столбцы и добавили новые или хотите заполнить старый столбец новыми фиктивными значениями, сделайте следующее:

public function up()
{
    DB::table('foydabars')->update(
        array(
            'status' => '0'
        )
    );
}
CodeToLife
источник