Laravel Migrations — Maintaining sanity
How to not shoot yourself in the foot with table names
Migrations are so cool!
They’re like version-control for your databases … You can see how your database morphed into what it is today from what it was in the beginning, as well as perform operations like rollbacks when something goes awry.
Migrations in Laravel — a crash course
Laravel makes migrations even easier, with a standard approach via migration classes that are generated with the
php artisan make:migration sample_migration
command.
This generates a class in the databases/migrations
directory that looks like
The up
and down
methods are supposed to contain code to perform the migration, and rollback the migration respectively.
But this is stuff you probably already know, and if you don’t, you should definitely check out the latest official docs entry on migrations.
The problem (?)
When creating a table in a migration, you’d have code in the up
function like
Schema::create('sample_migrations', function (Blueprint $table) {// add columns to the users table here});
and when modifying the sample_migrations
table in a different migration, you’d have
Schema::table('sample_migrations', function (Blueprint $table) {// add code to modify columns and their relationships});
In a large project, you could have thousands of migration scripts as your database structure changes over the duration of the project.
Referencing the table name directly like this could pose an issue, as the name of a table is determined by the Laravel Model class associated with it.
What do you do to all the migration scripts when
sample_migrations
is no longer a fitting name for this table? Perhaps find and replace?
or
How does a new member of the team know which model is associated with the
sample_migrations
table?
Fret not! A few alternatives present themselves.
Alternative #1: A ModelTableName trait
In your app/Traits
directory (create one if non-existent), add a ModelTableNameTrait.php
file and place this code in it.
This trait will add a static name()
method to laravel model classes that implement it. This method will get and return the actual table name that the model class represents.
So, SampleMigration::name()
will return sample_migrations
by default, but if you added
protected $table = 'my_sample_migrations';
to the model class to override its default table name, then SampleMigration::name()
would return my_sample_migrations
instead.
To useModelTableNameTrait
in your SampleMigration
model,
Now, theSampleMigration
model would have the name()
static method that can be used anywhere, especially in a migration class like
This makes it immediately obvious that the table being processed is related to the SampleMigration
model, and helps keep our migration scripts sane in the long run.
Alternative #2: extend a BaseModel
If making sure every model uses the ModelTableNameTrait
just doesn’t appeal to you, and you’d rather solve the problem once and for all, I recommend having a BaseModel
class that extends Illuminate\Database\Eloquent\Model
and uses the ModelTableNameTrait
.
If all your models extend that BaseModel
class, they’ll have all its static and non-static properties and methods, which means they’d have the trait too.
Of course, this is a double edged sword, you should be very careful with.
Make sure you don’t add methods or properties that override ones already used in Illuminate\Database\Eloquent\Model
or you’d start wondering why your application behaves funny (or not so funny, tbh).