Preenchendo um banco de dados em um arquivo de migração Laravel

115

Estou aprendendo o Laravel e tenho um arquivo de migração de trabalho criando uma tabela de usuários. Estou tentando preencher um registro de usuário como parte da migração:

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

    });
}

Mas estou recebendo o seguinte erro ao executar php artisan migrate:

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

Obviamente, isso ocorre porque o Artisan ainda não criou a tabela, mas toda a documentação parece dizer que existe uma maneira de usar o Fluent Query para preencher dados como parte de uma migração.

Alguem sabe como? Obrigado!

Adam Hopkinson
fonte

Respostas:

215

Não coloque o DB :: insert () dentro do Schema :: create (), porque o método create tem que terminar de fazer a tabela antes que você possa inserir coisas. Em vez disso, tente isto:

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
fonte
5
e como inserir vários dados?
Sahbaz de
6
@ SuperMario'sYoshi eu penso algo assimDB::table('users')->insert([ ['email' => '[email protected]', 'votes' => 0], ['email' => '[email protected]', 'votes' => 0] ]);
Денис
80

Sei que este é um post antigo, mas como surgiu em uma pesquisa no google, pensei em compartilhar alguns conhecimentos aqui. @ erin-geyer apontou que misturar migrações e seeders pode criar dores de cabeça e @justamartin respondeu que às vezes você deseja / precisa que os dados sejam preenchidos como parte de sua implantação.

Eu daria um passo adiante e diria que às vezes é desejável ser capaz de implementar alterações de dados de forma consistente para que você possa, por exemplo, implantar na preparação, ver se tudo está bem e, em seguida, implantar na produção com a confiança dos mesmos resultados (e não precisa se lembrar de executar alguma etapa manual).

No entanto, ainda há valor em separar a semente e a migração, pois essas são duas preocupações relacionadas, mas distintas. Nossa equipe comprometeu-se criando migrações que chamam semeadores. Isso se parece com:

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

Isso permite que você execute uma semente uma vez, exatamente como uma migração. Você também pode implementar a lógica que impede ou aumenta o comportamento. Por exemplo:

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

Isso obviamente executaria condicionalmente seu semeador se houver menos de 10 SomeModels. Isso é útil se você deseja incluir o semeador como um semeador padrão que é executado quando você chama artisan db:seede também quando migra para que você não "duplique". Você também pode criar um semeador reverso para que os rollbacks funcionem conforme o esperado, por exemplo

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

O segundo parâmetro --forceé necessário para permitir que o semeador seja executado em um ambiente de produção.

Darrylkuhn
fonte
2
Esta é de longe a melhor resposta. Código sustentável que separa as preocupações!
helsont
18
Eu teria o cuidado de considerar as implicações de longo prazo de chamar semeadores de scripts de migração. Os scripts de migração são versionados por data / hora, enquanto os seeders normalmente não. Durante o desenvolvimento, o semeador precisa frequentemente mudar, resultando na possibilidade de scripts de migração com versão executando semeadores sem versão - quebrando a idempotência. Em outras palavras, a execução do mesmo conjunto de scripts de migração diariamente pode gerar resultados diferentes.
originalbryan
2
Já faz um tempo que postei isso e gostaria de oferecer nossa experiência no uso dessa técnica. No geral, tem funcionado bem para nós e se eu tivesse que fazer tudo de novo, eu o faria. Dito isso, há uma pegadinha a ter em conta. @originalbryan está exatamente certo e a consequência é que ocasionalmente nos deparamos com situações em que as migrações são interrompidas ao girar um banco de dados novo, porque à medida que as migrações são executadas, o semeador (e o modelo) são mais atualizados do que o banco de dados (já que podemos semear antes que o esquema seja totalmente atualizado). Quando isso acontece, atualizamos a migração antiga para resolver o problema.
darrylkuhn
@darrylkuhn Ouvi dizer que não é uma boa prática atualizar arquivos de migração antigos - em vez de atualizar arquivos antigos, você deve criar um novo arquivo de migração - isso é "fluxo de trabalho" para arquivos de migração por design
Kamil Kiełczewski
2
Toda a linguagem do Laravel implica que um semeador é para dados de teste, então acho que isso deve ser mantido em mente com o design. É importante distinguir entre os dados que fazem parte do aplicativo e os dados de teste, e incluir os dados necessários diretamente em uma migração torna essa distinção muito clara.
Brettins de
13

Aqui está uma explicação muito boa de porque usar o Database Seeder do Laravel é preferível a usar Migrations: http://laravelbook.com/laravel-database-seeding/

No entanto, seguir as instruções na documentação oficial é uma ideia muito melhor porque a implementação descrita no link acima não parece funcionar e está incompleta. http://laravel.com/docs/migrations#database-seeding

Erin Geyer
fonte
1
Eu concordo com você Erin. Não misture migrações com dados de propagação porque é altamente provável que você gostaria de propagar alguns dados em seu ambiente de desenvolvimento, mas não em seu ambiente de produção.
Daniel Vigueras,
18
Bom ponto, mas existem algumas situações em que alguns dados devem existir no ambiente de produção. Por exemplo, o primeiro usuário administrador padrão deve existir para que o cliente possa fazer login pela primeira vez, algumas funções de autorização predefinidas devem existir, alguns dados de lógica de negócios também podem ser necessários imediatamente. Portanto, acho que os dados obrigatórios devem ser adicionados às migrações (para que você também possa ativar / desativar os registros de dados por meio de migrações separadas), mas as sementes podem ser deixadas para desenvolvimento.
JustAMartin
Uma pequena nota; o link para a propagação do banco de dados agora é: laravel.com/docs/5.3/seeding
magikMaker
3

Isso deve fazer o que você quiser.

public function up()
{
    DB::table('user')->insert(array('username'=>'dude', 'password'=>'z19pers!'));
}
strings28
fonte
1

Outra maneira limpa de fazer isso é definir um método privado que cria instância e persiste o modelo em questão.

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

Com esta solução, os campos de timestamps serão gerados pelo Eloquent.

EDITAR: é melhor usar o sistema semeador para distinguir a geração da estrutura do banco de dados e a população do banco de dados.

Maximilien DI DIO
fonte
Eu gosto deste ... ele serve exatamente o que eu precisava fazer, adicionar algumas funções de usuário por padrão na migração. Preciso ter certeza de importar o modelo ou se referir diretamente a ele $model = new App\UserRoles();, mas além disso ... perfeito!
FAB
1

Tentei esse método de inserção de DB, mas como ele não usa o modelo, ele ignorou uma característica lenta que eu tinha no modelo. Então, dado que o modelo para esta tabela existe, assim que ele fosse migrado, imaginei que o modelo estaria disponível para ser usado para inserir dados. E eu vim com isso:

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

Isso funcionou corretamente e também levou em consideração o traço sluggable em meu modelo para gerar automaticamente um slug para esta entrada, e usa os carimbos de data / hora também. NB. Adicionar o ID não era necessário, no entanto, eu queria IDs específicos para minhas categorias neste exemplo. Testado trabalhando no Laravel 5.8

Andrew Arscott
fonte
0

Se você já preencheu colunas e adicionou uma nova ou deseja preencher a coluna antiga com novos valores fictícios, faça o seguinte:

public function up()
{
    DB::table('foydabars')->update(
        array(
            'status' => '0'
        )
    );
}
CodeToLife
fonte