Wypełnianie bazy danych w pliku migracyjnym Laravel

115

Właśnie uczę się Laravel i mam działający plik migracji, który tworzy tabelę użytkowników. Próbuję wypełnić rekord użytkownika w ramach migracji:

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' => '[email protected]',
                'verified' => true
            )
        );

    });
}

Ale podczas uruchamiania pojawia się następujący błąd php artisan migrate:

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

Dzieje się tak oczywiście dlatego, że Artisan nie utworzył jeszcze tabeli, ale cała dokumentacja wydaje się mówić, że istnieje sposób wykorzystania Fluent Query do wypełnienia danych w ramach migracji.

Czy ktoś wie jak? Dzięki!

Adam Hopkinson
źródło

Odpowiedzi:

215

Nie umieszczaj DB :: insert () wewnątrz Schema :: create (), ponieważ metoda create musi zakończyć tworzenie tabeli, zanim będziesz mógł wstawić rzeczy. Spróbuj tego zamiast tego:

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' => '[email protected]',
            'verified' => true
        )
    );
}
BenjaminRH
źródło
5
i jak wstawić wiele danych?
Sahbaz
6
@ SuperMario'sYoshi, myślę coś takiegoDB::table('users')->insert([ ['email' => '[email protected]', 'votes' => 0], ['email' => '[email protected]', 'votes' => 0] ]);
Денис
80

Wiem, że to stary post, ale ponieważ pojawia się w wyszukiwarce Google, pomyślałem, że podzielę się tutaj wiedzą. @ erin-geyer wskazał, że mieszanie migracji i seederów może powodować bóle głowy, a @justamartin odpowiedział, że czasami chcesz / potrzebujesz danych, które mają być wypełnione w ramach twojego wdrożenia.

Pójdę o krok dalej i powiem, że czasami pożądane jest konsekwentne wdrażanie zmian danych, aby można było na przykład wdrożyć do przemieszczania, zobaczyć, że wszystko jest w porządku, a następnie wdrożyć do produkcji z pewnością tych samych wyników (i nie musisz pamiętać o ręcznym wykonywaniu kroków).

Jednak oddzielenie nasion i migracji ma wartość, ponieważ są to dwie powiązane, ale odrębne kwestie. Nasz zespół poszedł na kompromis, tworząc migracje, które wywołują seedery. To wygląda następująco:

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

Pozwala to na jednorazowe wykonanie seeda, tak jak podczas migracji. Możesz także zaimplementować logikę, która zapobiega lub wzmacnia zachowanie. Na przykład:

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

Spowoduje to oczywiście warunkowe wykonanie siewnika, jeśli jest mniej niż 10 SomeModels. Jest to przydatne, jeśli chcesz uwzględnić siewnik jako standardowy siewnik, który wykonywał się, gdy dzwonisz, artisan db:seedjak również podczas migracji, aby nie „podwoić się”. Możesz również utworzyć siewnik odwrotny, aby wycofanie działało zgodnie z oczekiwaniami, np

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

Drugi parametr --forcejest wymagany, aby siewnik mógł pracować w środowisku produkcyjnym.

darrylkuhn
źródło
2
To zdecydowanie najlepsza odpowiedź. Utrzymywany kod, który oddziela problemy!
helsont
18
Ostrożnie rozważałbym długoterminowe konsekwencje wywoływania seederów ze skryptów migracji. Skrypty migracji są wersjonowane według daty / godziny, podczas gdy seedery zazwyczaj nie. Podczas programowania potrzeby siewnika często się zmieniają, co skutkuje możliwością wersjonowanych skryptów migracji obsługujących niewersjonowane seedery - przełamanie idempotencji. Innymi słowy, uruchamianie tego samego zestawu skryptów migracji każdego dnia może przynieść różne rezultaty.
originalbryan
2
Minęło trochę czasu, odkąd to opublikowałem i chciałem podzielić się naszymi doświadczeniami z użyciem tej techniki. Ogólnie to zadziałało dla nas i gdybym musiał to zrobić jeszcze raz, zrobiłbym to. To powiedziawszy, jest jedna rzecz, o której należy pamiętać. @originalbryan ma dokładnie rację, a konsekwencją jest to, że czasami napotykamy sytuacje, w których migracje przerywają się podczas uruchamiania nowej bazy danych, ponieważ w miarę uruchamiania migracji, siewnik (i model) są bardziej aktualne niż baza danych (ponieważ możemy wysiewać przed pełną aktualizacją schematu). W takim przypadku aktualizujemy starą migrację, aby rozwiązać problem.
darrylkuhn
@darrylkuhn Słyszałem, że aktualizowanie starych plików migracyjnych nie jest dobrą praktyką - zamiast aktualizować stare pliki, należy utworzyć nowy plik migracyjny - to jest „workflow” dla plików migracyjnych według projektu
Kamil Kiełczewski
2
Cały język Laravela sugeruje, że siewnik służy do testowania danych, więc myślę, że należy o tym pamiętać podczas projektowania. Ważne jest, aby odróżnić dane, które są częścią aplikacji, od danych testowych, a uwzględnienie wymaganych danych bezpośrednio w migracji sprawia, że ​​to rozróżnienie jest bardzo wyraźne.
Brettins
13

Oto bardzo dobre wyjaśnienie, dlaczego korzystanie z bazy danych Laravel Seeder jest lepsze niż korzystanie z migracji: http://laravelbook.com/laravel-database-seeding/

Chociaż postępowanie zgodnie z instrukcjami w oficjalnej dokumentacji jest o wiele lepszym pomysłem, ponieważ implementacja opisana w powyższym linku wydaje się nie działać i jest niekompletna. http://laravel.com/docs/migrations#database-seeding

Erin Geyer
źródło
1
Zgadzam się z tobą Erin. Nie mieszaj migracji z danymi źródłowymi, ponieważ jest wysoce prawdopodobne, że chcesz umieścić część danych w środowisku programistycznym, ale nie w środowisku produkcyjnym.
Daniel Vigueras,
18
Dobra uwaga, ale są sytuacje, w których pewne dane muszą istnieć w środowisku produkcyjnym. Na przykład pierwszy domyślny użytkownik administracyjny musi istnieć, aby klient mógł zalogować się po raz pierwszy, muszą istnieć pewne wstępnie ustawione role autoryzacji, niektóre dane logiki biznesowej również mogą być wymagane natychmiast. Dlatego uważam, że do migracji należy dodać obowiązkowe dane (aby można było zwiększać / zmniejszać także rekordy danych poprzez oddzielne migracje), ale nasiona można pozostawić do rozwoju.
JustAMartin
Mała uwaga; link do wysiewu bazy danych jest teraz: laravel.com/docs/5.3/seeding
magikMaker
3

To powinno zrobić, co chcesz.

public function up()
{
    DB::table('user')->insert(array('username'=>'dude', 'password'=>'z19pers!'));
}
struny 28
źródło
1

Innym czystym sposobem jest zdefiniowanie prywatnej metody, która tworzy instancję i utrzymuje odpowiedni Model.

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();
    }
}

Dzięki temu rozwiązaniu pola znaczników czasu będą generowane przez Eloquent.

EDYCJA: lepiej jest użyć systemu siewników do odróżnienia generowania struktury bazy danych i populacji bazy danych.

Maximilien DI DIO
źródło
Podoba mi się ten ... serwer obsługuje dokładnie to, co musiałem zrobić, domyślnie dodaj kilka ról użytkowników podczas migracji. Musisz zaimportować model lub odnieść się bezpośrednio do niego $model = new App\UserRoles();, ale poza tym ... idealnie!
FAB
1

Wypróbowałem tę metodę wstawiania DB, ale ponieważ nie używa ona modelu, zignorowała ona powolną cechę, którą miałem na modelu. Tak więc, biorąc pod uwagę, że model dla tej tabeli istnieje, po migracji doszedłem do wniosku, że model będzie dostępny do wstawiania danych. I wymyśliłem to:

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',
            ],
        );
    }

To działało poprawnie, a także uwzględniono cechę powolną w moim modelu, aby automatycznie wygenerować informacje o tym wpisie, i również używa sygnatur czasowych. NB. Dodanie identyfikatora nie było konieczne, jednak w tym przykładzie chciałem mieć określone identyfikatory dla moich kategorii. Przetestowano pracę na Laravel 5.8

Andrew Arscott
źródło
0

Jeśli masz już wypełnione kolumny i dodałeś nowe lub chcesz wypełnić starą kolumnę nowymi wartościami pozorowanymi, zrób to:

public function up()
{
    DB::table('foydabars')->update(
        array(
            'status' => '0'
        )
    );
}
CodeToLife
źródło