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' => 'name@domain.com',
                '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!

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' => 'name@domain.com',
            'verified' => true
        )
    );
}

5
e como inserir vários dados?
Sahbaz de

6
@ SuperMario'sYoshi eu penso algo assimDB::table('users')->insert([ ['email' => 'taylor@example.com', 'votes' => 0], ['email' => 'dayle@example.com', '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.


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


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

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.


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


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'
        )
    );
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.