Skip to content

1. Overview

Timucin edited this page Apr 1, 2025 · 3 revisions

Overview

What It Is

This package is a tool that creates Laravel migration files by inspecting the application's models with the command php artisan implicit-migrations:generate. Even after you change the model classes, you can run the command and generate a migration with the necessary update operations.

How It Works

With the most basic configuration, the implicit-migrations:generate artisan command looks at a Eloquent model and finds necessary information about the table properties such as the table name, primary key etc. Then it goes over the properties of the model and collects the name, type and default value information if provided. With the information collected, it creates a migration file and populates the up() and down() methods with the appropriate definitions.

Implications

For further details, the generator refers to some additional data in the model class which we call "Implications". These can be specified with either annotations or attributes on the class, its properties and methods.

Annotations

Annotations in DocBlocks with the format @<implication-name>(<parameters>) are recognized and interpreted as implications. For example, an annotation like this tells the generator that this integer property corresponds to an UNSIGNED INT column named product_id in the order_items table:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class OrderItem extends Model
{
    /**
     * @Column(unsigned: true)
     */
    public int $product_id;
}

In turn, the generator produces a migration like this:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use App\Models\OrderItem as Source;

return new class extends Migration
{
    public const TABLE_NAME = 'order_items';

    public function getSource(): string
    {
        return Source::class;
    }

    public function tableUp(Blueprint $table): void
    {
        $table->id();
        $table->integer('product_id')->unsigned();
        $table->timestamps();
    }

    public function up(): void
    {
        Schema::create(static::TABLE_NAME, function (Blueprint $table) {
            $this->tableUp($table);
        });
    }

    public function down(): void
    {
        Schema::drop(static::TABLE_NAME);
    }
};

You can find out more on other implications in the Implication Reference section.

PHP Attributes

Another way of specifying implications is using PHP attributes. The very same implications are avaliable as attributes with the same notation. This is the same model definition as above as far as the generator is concerned:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Toramanlis\ImplicitMigrations\Attributes\Column;

class OrderItem extends Model
{
    #[Column(unsigned: true)]
    public int $product_id;
}

The advantage of this option is that your IDE/Editor will recognize the attributes as the classes they are and provide autocompletion and descriptions. The down side is that the classes have to be referenced in the models and now they need to be existent in the production environment too.

The obvious approach to tackling this is just adding the implicit-migrations package to the require instead of require-dev. The neat approach, on the other hand, is to get the attribute classes to the database/migrations/attributes directory of the project by publishing them with the php artisan vendor:publish --tag=implication-attributes command and add "database/attributes/composer.json" to the composer.json file like this:

...
"extra": {
    "merge-plugin": {
        "include": [
            "database/attributes/composer.json"
        ]
    }
}
...

This way, you can have the package in the require-dev section of your composer.json and still have the attribute classes available in production.

Updates

This tool doesn't only work for creating a table for a model. If you change your model and run implicit-migrations:generate again, it will resolve the changes by referring to the already generated migrations (Only the generated migrations that is. See: Manual Migrations) and generate a new migration that applies the changes to the table structure.

For example if you update the above model and add another property to it:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class OrderItem extends Model
{
    /**
     * @Column(unsigned: true)
     */
    public int $product_id;

    public int $order_id;
}

After you run php artisan implicit-migrations:generate having the initial migration above already in place, you will get another migration like this:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use App\Models\OrderItem as Source;

return new class extends Migration
{
    public const TABLE_NAME = 'order_items';

    public function getSource(): string
    {
        return Source::class;
    }

    public function tableUp(Blueprint $table): void
    {
        $table->integer('order_id');
    }

    public function tableDown(Blueprint $table): void
    {
        $table->dropColumn('order_id');
    }

    public function up(): void
    {
        Schema::table(static::TABLE_NAME, function (Blueprint $table) {
            $this->tableUp($table);
        });
    }

    public function down(): void
    {
        Schema::table(static::TABLE_NAME, function (Blueprint $table) {
            $this->tableDown($table);
        });
    }
};