DEV Community

Cover image for Modify tables and records in real time with Laravel Population
Capsules Codes
Capsules Codes

Posted on • Originally published at capsules.codes

Modify tables and records in real time with Laravel Population

TL;DR: How to quickly and easily manipulate your database tables and records using the Laravel Population package.

 
 

A sample Laravel project can be found on this Github Repository.

 
 

Laravel Population is a Laravel package dedicated to managing database migrations and records. It significantly simplifies the process of modifying tables, eliminating the need to create new migrations and seeders for these changes.

 
 

This package is experimental and in continuous development. It was created with the aim of simplifying database data management during structural modifications. Therefore, it is strongly discouraged to use it exclusively in a production environment.

 
 

This article provides an example of using the Laravel Population package by creating a Laravel project called 'template,' adding Users through a Seeder, and then splitting the name attribute into two separate attributes, first_name and last_name. The steps are as follows:

 
 

Create a Laravel project named template.

 

 composer create-project laravel/laravel template
Enter fullscreen mode Exit fullscreen mode

 
 

Install the laravel-population package into the template project.

 

cd template

composer require --dev capsulescodes/laravel-population
Enter fullscreen mode Exit fullscreen mode

 
 

Create the database for the template project and make the necessary changes to the environment variables.

 

mysql -u <username> -p <password> -e "CREATE DATABASE template"
Enter fullscreen mode Exit fullscreen mode

 
 

.env

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=template
DB_USERNAME=root
DB_PASSWORD=
Enter fullscreen mode Exit fullscreen mode

 
 

Modify the DatabaseSeeder as well as the 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();
    }
}
Enter fullscreen mode Exit fullscreen mode

 
 

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 ] );
    }
}
Enter fullscreen mode Exit fullscreen mode

 
 

Run the migrations and 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.
Enter fullscreen mode Exit fullscreen mode

 
 

The project preparation is complete. The Laravel project has been created, and its database now includes users with their first and last names stored in the name column.

 
 

By checking with the php artisan tinker command and calling User::all()->toJson(JSON_PRETTY_PRINT), we can examine the users records.

 

php artisan tinker

> User::all()->toJson( JSON_PRETTY_PRINT )
Enter fullscreen mode Exit fullscreen mode
[
      {
          "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"
      },
      ...
]
Enter fullscreen mode Exit fullscreen mode

 
 

In order for the Laravel Population package to recognize the migrations it should observe, these migrations must include the public name property, typically injected directly as a parameter in the Schema::create() method. It is crucial to highlight this variable so that the package can identify the migration and its associated table.

 

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 );
    }
};
Enter fullscreen mode Exit fullscreen mode

 
 

It is now time to remove the name column and add two new columns, first_name and 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 );
    }
};
Enter fullscreen mode Exit fullscreen mode

 
 

Modifying the User model is also necessary by removing name and adding first_name and last_name to the $fillable array.

 

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' ];
}
Enter fullscreen mode Exit fullscreen mode

 
 

The populate command can be executed :

 

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                                                 
 └──────────────────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Laravel Population will list the modified migrations, along with specific details of the changes made to each migration.

 
 

A confirmation will be requested to decide whether the table modifications should be applied.

 

  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                                                                                
 └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

 
 

You will be asked to provide a data population formula for each new column. The uniqueness of this formula is that it allows you to access both the attribute and the model. Let's take the first User as an example.

{
    "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"
}
Enter fullscreen mode Exit fullscreen mode

 
 

We want to separate Alexandro from Schinner. This involves splitting the string into an array of strings using the PHP explode() method :

 

$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 ];
Enter fullscreen mode Exit fullscreen mode

 
 

Let's integrate these simplified formulas into the 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.
Enter fullscreen mode Exit fullscreen mode

 
 

Laravel helpers are also available. Therefore, adding Faker data is possible, and the expressions fn() => fake()->firstName() and fn() => fake()->lastName() will work perfectly.

 
 

By checking with the php artisan tinker command and calling User::all()->toJson(JSON_PRETTY_PRINT), we can examine the users records.

 

[
      {
          "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"
      },
      ...
]
Enter fullscreen mode Exit fullscreen mode

 
 

We observe that the users now have an first_name attribute and a last_name attribute containing the former information from the name attribute.

 
 

A copy of the old database is now available in the databases folder within the storage/framework/databases directory.

 
 

If the previous manipulations are unsatisfactory, a command to restore the previous data is available.

 

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.
Enter fullscreen mode Exit fullscreen mode

 
 

By checking with the php artisan tinker command and calling User::all()->toJson(JSON_PRETTY_PRINT), we can examine the users records.

 

[
      {
          "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"
      },
      ...
]
Enter fullscreen mode Exit fullscreen mode

 
 

The old records have been restored. Quickly and easily.

 
 

Glad this helped.

 
 

Find out more on Capsules or X

Top comments (0)