Blog / Modifier tables et données en temps réel avec Laravel Population
7 min - 02 Nov 2023
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 : string
⇂ create column : 'first_name' => type : string
⇂ create column : 'last_name' => type : string
┌ Do you want to proceed on populating the 'users' table? ─────┐
│ ○ Yes / ● No │
└──────────────────────────────────────────────────────────────┘
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.