Remplir une base de données dans un fichier de migration Laravel

115

J'apprends juste Laravel et j'ai un fichier de migration fonctionnel créant une table d'utilisateurs. J'essaie de remplir un enregistrement d'utilisateur dans le cadre de la migration:

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

    });
}

Mais j'obtiens l'erreur suivante lors de l'exécution php artisan migrate:

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

C'est évidemment parce qu'Artisan n'a pas encore créé la table, mais toute la documentation semble indiquer qu'il existe un moyen d'utiliser Fluent Query pour renseigner des données dans le cadre d'une migration.

Quelqu'un sait comment? Merci!

Adam Hopkinson
la source

Réponses:

215

Ne mettez pas le DB :: insert () à l'intérieur de Schema :: create (), car la méthode create doit finir de créer le tableau avant de pouvoir insérer des éléments. Essayez plutôt ceci:

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
la source
5
et comment insérer plusieurs données?
Sahbaz
6
@ SuperMario'sYoshi je pense quelque chose comme çaDB::table('users')->insert([ ['email' => '[email protected]', 'votes' => 0], ['email' => '[email protected]', 'votes' => 0] ]);
Денис
80

Je sais que c'est un ancien message, mais comme il apparaît dans une recherche Google, j'ai pensé partager quelques connaissances ici. @ erin-geyer a souligné que mélanger les migrations et les seeders peut créer des maux de tête et @justamartin a répliqué que parfois vous voulez / avez besoin de données à remplir dans le cadre de votre déploiement.

J'irais un peu plus loin et je dirais qu'il est parfois souhaitable de pouvoir déployer les modifications de données de manière cohérente afin que vous puissiez par exemple déployer vers la mise en scène, voir que tout va bien, puis déployer en production avec la confiance des mêmes résultats (et ne pas avoir à vous rappeler d'exécuter une étape manuelle).

Cependant, il est toujours utile de séparer la graine et la migration car ce sont deux préoccupations liées mais distinctes. Notre équipe a fait des compromis en créant des migrations qui appellent des seeders. Cela ressemble à:

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

Cela vous permet d'exécuter une source une seule fois, tout comme une migration. Vous pouvez également implémenter une logique qui empêche ou augmente le comportement. Par exemple:

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

Cela exécuterait évidemment conditionnellement votre semoir s'il y a moins de 10 SomeModels. Ceci est utile si vous souhaitez inclure le semeur en tant que semeur standard qui s'exécute lorsque vous appelez artisan db:seedainsi que lorsque vous migrez afin de ne pas "doubler". Vous pouvez également créer un semoir inversé pour que les restaurations fonctionnent comme prévu, par exemple

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

Le deuxième paramètre --forceest requis pour permettre au semeur de s'exécuter dans un environnement de production.

Darrylkuhn
la source
2
C'est de loin la meilleure réponse. Code maintenable qui sépare les préoccupations!
helsont
18
Je ferais attention de considérer les implications à long terme de l'appel de seeders à partir de scripts de migration. Les scripts de migration sont versionnés par date / heure, contrairement aux seeders. Au cours du développement, les besoins du semeur changent souvent, ce qui entraîne la possibilité que des scripts de migration versionnés exécutent des semeurs non versionnés - brisant l'idempotence. En d'autres termes, l'exécution du même ensemble de scripts de migration au jour le jour peut donner des résultats différents.
originalbryan
2
Cela fait un moment que j'ai posté ceci et je voulais partager notre expérience en utilisant cette technique. Dans l'ensemble, cela a bien fonctionné pour nous et si je devais le refaire, je le ferais. Cela dit, il y a un piège à connaître. @originalbryan a tout à fait raison et la conséquence est que nous rencontrons parfois des situations où les migrations s'interrompent lors de la création d'une nouvelle base de données, car au fur et à mesure que les migrations s'exécutent, le semeur (et le modèle) sont plus à jour que la base de données (car nous pouvons amorcer avant la mise à jour complète du schéma). Lorsque cela se produit, nous mettons à jour l'ancienne migration pour résoudre le problème.
darrylkuhn
@darrylkuhn J'ai entendu dire que ce n'est pas une bonne pratique de mettre à jour les anciens fichiers de migration - au lieu de mettre à jour les anciens fichiers, vous devriez créer un nouveau fichier de migration - c'est un "workflow" pour les fichiers de migration par conception
Kamil Kiełczewski
2
Tout le langage de Laravel implique qu'un semoir est destiné aux données de test, donc je pense que cela devrait être gardé à l'esprit lors de la conception. Il est important de faire la distinction entre les données qui font partie de l'application et les données de test, et inclure les données requises directement dans une migration fait cette distinction très clairement.
Brettins
13

Voici une très bonne explication des raisons pour lesquelles l'utilisation de Database Seeder de Laravel est préférable à l'utilisation de Migrations: http://laravelbook.com/laravel-database-seeding/

Cependant, suivre les instructions de la documentation officielle est une bien meilleure idée car l'implémentation décrite dans le lien ci-dessus ne semble pas fonctionner et est incomplète. http://laravel.com/docs/migrations#database-seeding

Erin Geyer
la source
1
Je suis d'accord avec toi Erin. Ne mélangez pas les migrations avec les données d'amorçage, car il est fort probable que vous souhaitiez amorcer certaines données dans votre environnement de développement, mais pas dans votre environnement de production.
Daniel Vigueras
18
Bon point, mais il existe des situations où certaines données doivent exister dans l'environnement de production. Par exemple, le tout premier utilisateur administrateur par défaut doit exister pour que le client puisse se connecter pour la première fois, certains rôles d'autorisation prédéfinis doivent exister, certaines données de logique métier peuvent également être requises immédiatement. Ainsi, je pense que des données obligatoires devraient être ajoutées aux migrations (afin que vous puissiez également monter / descendre des enregistrements de données via des migrations séparées), mais les graines peuvent être laissées pour le développement.
JustAMartin
Une petite note; le lien vers l'ensemencement de la base de données est maintenant: laravel.com/docs/5.3/seeding
magikMaker
3

Cela devrait faire ce que vous voulez.

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

Une autre façon propre de le faire est de définir une méthode privée qui crée l'instance et persiste le modèle concerné.

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

Avec cette solution, les champs d'horodatage seront générés par Eloquent.

EDIT: il est préférable d'utiliser le système de semoir pour distinguer la génération de la structure de la base de données et la population de la base de données.

Maximilien DI DIO
la source
J'aime celui-ci ... il serveur exactement ce dont j'avais besoin, ajouter quelques rôles d'utilisateur par défaut lors de la migration. Il faut s'assurer d'importer le modèle ou de s'y référer directement $model = new App\UserRoles();, mais à part ça ... parfait!
FAB
1

J'ai essayé cette méthode d'insertion DB, mais comme elle n'utilise pas le modèle, elle a ignoré un trait lent que j'avais sur le modèle. Donc, étant donné que le modèle de cette table existe, dès sa migration, j'ai pensé que le modèle serait disponible pour insérer des données. Et je suis venu avec ceci:

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

Cela a fonctionné correctement, et a également pris en compte le trait sluggable sur mon modèle pour générer automatiquement un slug pour cette entrée, et utilise également les horodatages. NB. L'ajout de l'ID n'était pas nécessaire, cependant, je voulais des ID spécifiques pour mes catégories dans cet exemple. Testé fonctionnant sur Laravel 5.8

Andrew Arscott
la source
0

Si vous avez déjà rempli des colonnes et en avez ajouté une nouvelle ou si vous souhaitez remplir l'ancienne colonne avec de nouvelles valeurs fictives, procédez comme suit:

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