Search code examples
laraveldatabase-designlaravel-migrations

Current model is incompatible with old migrations


I have following sitation (I will describe it as history line):

  1. I setup project witch User model (and users table) with migration file A
  2. After some time i add user_modules table many-to-many and I was force to initialize this array during schama update in migration file B. I do it by

    User::chunk(100, function($users) {
        foreach ($users as $user) {
            $user->userModule()->create();
        }
    });
    
  3. After some time i need to update User model and table by add soft-delete (column delete_at) in migration file C and field $dates=['deleted_at'] in User model.
  4. Then I develop system and add more migrations but at some point new developer join to our team and he must build DB schema from scratch so he run php artisan:migrate but he get error in migration file B:

[Illuminate\Database\QueryException (42S22)]
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'users.deleted_at' in 'where clause' (SQL: select * from users where users.deleted_at is null order by users.id asc limit 100 off set 0)

So the current User model is incompatible witch migration file B

How to deal with that situation?

Where I made mistake and what to do to prevent such situation in future?


Solution

  • This is because of Soft Deletes. When you add the trait SoftDeletes to a model, it will automatically add where users.deleted_at is null to all queries. The best way to get around this is to add withTrashed() to your query in migration B.

    To do this, change your query in migration B to look like the following. This should remove the part where it's trying to access the non existent deleted_at column. This migration, after all, is not aware that you want to add soft deletes later on, so accessing all users, including those that are trashed, makes perfect sense.

    User::withTrashed()->chunk(100, function($users) {
        foreach ($users as $user) {
            $user->userModule()->create();
        }
    });
    

    You could always comment out the SoftDelete trait on the user model before running the migrations also, but that's a temporary fix since you'll need to explain it to all future developers. Also, it can be very handy to run php artisan migrate:fresh sometimes. You don't want to have to remember to comment out the trait each time, so adding withTrashed() seems like the most desirable solution to me.

    As a final note, I highly suggest NOT adding seeds to your migrations. Migrations should ONLY be used for schema changes. In cases like this, I would use a console command, or a combination of console commands.

    For example, you could make a console command that gets triggered by php artisan check:user-modules. Within this command, you could have the following which will create a user module only if one does not yet exist.

    User::chunk(100, function($users) {
        foreach ($users as $user) {
            if (!$user->userModule()->exists()) {
                $user->userModule()->create();
            }
        }
    });
    

    You should be able to run this command at any time since it won't overwrite existing user modules.