Blog / Modifier tables et données en temps réel avec Laravel Population

Image used for article Modifier tables et données en temps réel avec Laravel Population

Modifier tables et données en temps réel avec Laravel Population




TL;DR: Comment manipuler rapidement et aisément les tables et données de votre base de données en utilisant le package Laravel Population.




Vous trouverez le code source via ce Github Repository. Découvrez en plus sur Capsules ou X.




Laravel Population est un package Laravel dédié à la gestion des migrations et des données de base de données. Il simplifie grandement le processus de modification des tables, éliminant ainsi la nécessité de créer de nouvelles migrations et seeders pour effectuer ces modifications




Ce package est experimental et en développement continu. Il a été créé dans le but de simplifier la gestion des données d'une base de données lors de modifications structurelles. Par conséquent, il est fortement déconseillé de l'utiliser exclusivement en environnement de production.




Cet article propose un exemple d'utilisation du package Laravel Population en créant un projet Laravel template, en ajoutant des Users via un Seeder, puis en séparant l’attribut name en deux attributs distincts, first_name et last_name. Les étapes sont les suivantes :



Créer un projet Laravel nommé template.


 composer create-project laravel/laravel template




Installer le package laravel-population dans le projet template.



cd template

composer require --dev capsulescodes/laravel-population




Créer la base de données du projet template et effectuez les modifications nécessaires dans les variables d’environnement.



mysql -u <username> -p <password> -e "CREATE DATABASE template"




.env


DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=template
DB_USERNAME=root
DB_PASSWORD=




Modifier le DatabaseSeeder ainsi que le UserFactory.



database/seeders/DatabaseSeeder.php


<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use App\Models\User;

class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        User::factory( 10 )->create();
    }
}




dabatase/factories/UserFactory.php


<?php

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;

class UserFactory extends Factory
{
    public function definition(): array
    {
        return [
            'name' => fake()->firstName() . ' ' . fake()->lastName(),
            'email' => fake()->unique()->safeEmail(),
            'email_verified_at' => now(),
            'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi',
            'remember_token' => Str::random( 10 ),
        ];
    }

    public function unverified() : static
    {
        return $this->state( fn() => [ 'email_verified_at' => null ] );
    }
}




Lancer les migrations et les seeds.



php artisan migrate --seed
# Output

INFO  Preparing database.

Creating migration table ............................................................................................................... 28ms DONE

 INFO  Running migrations.

2014_10_12_100000_create_password_reset_tokens_table ................................................................................... 90ms DONE
2019_08_19_000000_create_failed_jobs_table ............................................................................................. 83ms DONE
2019_12_14_000001_create_personal_access_tokens_table ................................................................................. 154ms DONE
2014_10_12_000000_create_users_table.php ............................................................................................... 92ms DONE

INFO  Seeding database.




La préparation du projet est achevée, Le projet Laravel est désormais créé, et sa base de données contient des utilisateurs dont les noms et prénoms sont enregistrés dans la colonne name.



En vérifiant avec la commande php artisan tinker et en appelant User::all()->toJson( JSON_PRETTY_PRINT ) nous pouvons examiner les données des utilisateurs.



php artisan tinker

> User::all()->toJson( JSON_PRETTY_PRINT )
[
      {
          "id": 1,
          "name": "Alexandro Schinner",
          "email": "cummerata.juana@example.com",
          "email_verified_at": "2023-10-21T16:04:11.000000Z",
          "created_at": "2023-10-21T16:04:11.000000Z",
          "updated_at": "2023-10-21T16:04:11.000000Z"
      },
      {
          "id": 2,
          "name": "Silas Blick",
          "email": "bradtke.jarod@example.net",
          "email_verified_at": "2023-10-21T16:04:11.000000Z",
          "created_at": "2023-10-21T16:04:11.000000Z",
          "updated_at": "2023-10-21T16:04:11.000000Z"
      },
      ...
]




Pour permettre au package Laravel Population de reconnaître les migrations qu'il doit observer, ces migrations doivent inclure la propriété publique name, généralement injectée directement en tant que paramètre dans la méthode Schema::create(). Il est essentiel de mettre en avant cette variable pour que le package puisse identifier la migration et la table associée.



database/migrations/2014_10_12_000000_create_users_table.php


<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public $name = 'users';

    public function up() : void
    {
        Schema::create( $this->name, function( Blueprint $table )
        {
            $table->id();
            $table->string( 'name' );
            $table->string( 'email' )->unique();
            $table->timestamp( 'email_verified_at' )->nullable();
            $table->string( 'password' );
            $table->rememberToken();
            $table->timestamps();
        });
    }

    public function down() : void
    {
        Schema::dropIfExists( $this->name );
    }
};




Il est désormais temps de supprimer la colonne name et d'ajouter deux nouvelles colonnes, first_name et last_name.



database/migrations/2014_10_12_000000_create_users_table.php


<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public $name = 'users';

    public function up() : void
    {
        Schema::create( $this->name, function( Blueprint $table )
        {
            $table->id();
            $table->string( 'first_ame' );
            $table->string( 'last_name' );
            $table->string( 'email' )->unique();
            $table->timestamp( 'email_verified_at' )->nullable();
            $table->string( 'password' );
            $table->rememberToken();
            $table->timestamps();
        });
    }

    public function down() : void
    {
        Schema::dropIfExists( $this->name );
    }
};




La modification du model User est aussi nécessaire, en supprimant name et en ajoutant first_name et last_name dans le tableau $fillable.



app/Models/User.php


<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;

    protected $fillable = [ 'first_name', 'last_name', 'email', 'password' ];

    protected $hidden = [ 'password', 'remember_token' ];

    protected $casts = [ 'email_verified_at' => 'datetime', 'password' => 'hashed' ];
}




La commande populate peut être executée.



php artisan populate
 INFO  Migration changes :

2014_10_12_000000_create_users_table.php .......................................................................................................................... DONE

 INFO  Table 'users' has changes.

  ⇂ delete column : 'name' => type : stringcreate column : 'first_name' => type : stringcreate column : 'last_name' => type : string

 ┌ Do you want to proceed on populating the 'users' table? ─────┐
 │ ○ Yes / ● No                                                 │
 └──────────────────────────────────────────────────────────────┘
  • Laravel Population listera les migrations modifiées, ainsi que les détails spécifiques des modifications apportées à chaque migration.




Une confirmation vous sera demandée pour décider si les modifications de la table doivent être appliquées.



 ┌ Do you want to proceed on populating the 'users' table? ─────┐
 │ Yes                                                          │
 └──────────────────────────────────────────────────────────────┘
 
 ┌ How would you like to convert the records for the column 'first_name' of type 'string'?  'fn( $attribute, $model ) => $attribute' ┐
 │ fn( $attribute, $model ) => $attribute                                                                                            │
 └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘




Il est alors demandé de fournir une formule de population de données pour chaque nouvelle colonne. La particularité de cette formule est qu’elle vous permet d’accéder à la fois à l’attribut et au modèle. Prenons en exemple le premier User.



{
    "id": 1,
    "name": "Alexandro Schinner",
    "email": "cummerata.juana@example.com",
    "email_verified_at": "2023-10-21T16:04:11.000000Z",
    "created_at": "2023-10-21T16:04:11.000000Z",
    "updated_at": "2023-10-21T16:04:11.000000Z"
}




Nous souhaitons séparer Alexandro de Schinner. Cela implique de diviser la chaîne en un tableau de chaînes à l'aide de la méthode PHP explode() :



$firstName = explode( ' ', $user->name )[ 0 ]; // Alexandro

#formule pour `first_name`

fn( $attribute, $model ) => explode( ' ', $model->name )[ 0 ];

$lastName = explode( ' ', $user->name )[ 0 ]; // Schinner

#formule pour `last_name`

fn( $attribute, $model ) => explode( ' ', $model->name )[ 1 ];




Intégrons ces formules simplifiées à l'assistant.



 ┌ Do you want to proceed on populating the 'users' table? ─────┐
 │ Yes                                                          │
 └──────────────────────────────────────────────────────────────┘
 
 ┌ How would you like to convert the records for the column 'first_name' of type 'string'?  'fn( $attribute, $model ) => $attribute' ┐
 │ fn( $a, $b ) => explode( ' ', $b->name )[ 0 ];                                                                                    │
 └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

 ┌ How would you like to convert the records for the column 'last_name' of type 'string'?  'fn( $attribute, $model ) => $attribute' ┐
 │ fn( $a, $b ) => explode( ' ', $b->name )[ 1 ];                                                                                   │
 └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

  INFO  Population succeeded.




Les helpers Laravel sont également disponibles. Par conséquent, l'ajout de données Faker est possible, et les expressions fn() => fake()->firstName() et fn() => fake()->lastName() fonctionneront parfaitement.



En vérifiant avec la commande php artisan tinker et en appelant User::all()->toJson( JSON_PRETTY_PRINT ) nous pouvons examiner les données des utilisateurs.



[
      {
          "id": 1,
          "first_name": "Alexandro",
          "last_name": "Schinner",
          "email": "cummerata.juana@example.com",
          "email_verified_at": "2023-10-21T16:04:11.000000Z",
          "created_at": "2023-10-21T16:24:03.000000Z",
          "updated_at": "2023-10-21T16:24:03.000000Z"
      },
      {
          "id": 2,
          "first_name": "Silas",
          "last_name": "Blick",
          "email": "bradtke.jarod@example.net",
          "email_verified_at": "2023-10-21T16:04:11.000000Z",
          "created_at": "2023-10-21T16:24:03.000000Z",
          "updated_at": "2023-10-21T16:24:03.000000Z"
      },
      ...
]




Nous constatons que les utilisateurs ont désormais un attribut first_name et un attribut last_name contenant les anciennes informations de l'attribut name.



Une copie de l’ancienne base de données est désormais disponible dans le dossier databases du répertoire storage/framework/databases.



Si les manipulations précédentes ne sont pas satisfaisantes, une commande de restauration des données précédentes est disponible.



php artisan populate:rollback
WARN  The rollback command will only set back the latest copy of your database. You'll have to modify your migrations and models manually.

INFO  Database copy successfully reloaded.




En vérifiant avec la commande php artisan tinker et en appelant User::all()->toJson( JSON_PRETTY_PRINT ) nous pouvons examiner les données des utilisateurs.



[
      {
          "id": 1,
          "name": "Alexandro Schinner",
          "email": "cummerata.juana@example.com",
          "email_verified_at": "2023-10-21T16:04:11.000000Z",
          "created_at": "2023-10-21T16:04:11.000000Z",
          "updated_at": "2023-10-21T16:04:11.000000Z"
      },
      {
          "id": 2,
          "name": "Silas Blick",
          "email": "bradtke.jarod@example.net"
          "email_verified_at": "2023-10-21T16:04:11.000000Z",
          "created_at": "2023-10-21T16:04:11.000000Z",
          "updated_at": "2023-10-21T16:04:11.000000Z"
      },
      ...
]




Les anciennes données ont été restaurées. Rapidement et simplement.




Ravi d’avoir pu aider.

v1.4.0

Icône XIcône Github