Migration

Migrations are PowerOrm’s way of propagating changes you make to your models (adding a field, deleting a model, etc.) into your database schema. They’re designed to be mostly automatic, but you’ll need to know when to make migrations, when to run them, and the common problems you might run into.

The Commands

There are several commands which you will use to interact with migrations and PowerOrm’s handling of database schema:

  • migrate, which is responsible for applying migrations, as well as unapplying and listing their status.
  • makemigrations, which is responsible for creating new migrations based on the changes you have made to your models.

You should think of migrations as a version control system for your database schema. makemigrations is responsible for packaging up your model changes into individual migration files - analogous to commits - and migrate is responsible for applying those to your database.

The migration files for each app live in a “migrations” directory inside of that app, and are designed to be committed to, and distributed as part of, its codebase. You should be making them once on your development machine and then running the same migrations on your colleagues’ machines, your staging machines, and eventually your production machines.

Migrations will run the same way on the same dataset and produce consistent results, meaning that what you see in development and staging is, under the same circumstances, exactly what will happen in production.

PowerOrm will make migrations for any change to your models or fields - even options that don’t affect the database - as the only way it can reconstruct a field correctly is to have all the changes in the history, and you might need those options in some data migrations later on (for example, if you’ve set custom validators).

Workflow

Working with migrations is simple. Make changes to your models - say, add a field and remove a model - and then run makemigrations

$ php pmanager.php makemigrations
      Creating Migrations :
        m0002_Auto_20161023_1010.php
         - Create Model Car
         - Create Model Manufacturer
         - Add Field Manufacturer To Car

Your models will be scanned and compared to the versions currently contained in your migration files, and then a new set of migrations will be written out. Make sure to read the output to see what makemigrations thinks you have changed - it’s not perfect, and for complex changes it might not be detecting what you expect.

Once you have your new migration files, you should apply them to your database to make sure they work as expected:

$ php pmanager.php migrate
 Perfoming system checks ...
 System check identified 0 issues
 Running migrations:
 Applying \app\migrations\m0001_Initial...OK

The command runs in two stages;

  • first, it synchronizes unmigrated models, and
  • then it runs any migrations that have not yet been applied.

Once the migration is applied, commit the migration and the models change to your version control system as a single commit - that way, when other developers (or your production servers) check out the code, they’ll get both the changes to your models and the accompanying migration at the same time.

Migration files

Migrations are stored as an on-disk format, referred to here as “migration files”. These files are actually just normal PHP files with an agreed-upon object layout, written in a declarative style.

A basic migration file looks like this:

/**Migration file generated by PowerOrm(1.1.0-pre-alpha)*/

namespace app\migrations;

use Eddmash\PowerOrm\Migration\Migration;
use Eddmash\PowerOrm\Migration\Operation\Model as modelOperation;
use Eddmash\PowerOrm\Model\Field as modelField;

class m0001_Initial extends Migration{

    public function getDependency(){
        return [];
    }

    public function getOperations(){
        return [
            modelOperation\CreateModel::createObject(
                [
                    'name'=> 'User',
                    'fields'=>[
                        'firstName'=> modelField\CharField::createObject(['maxLength'=> 30]),
                        'lastName'=> modelField\CharField::createObject(['maxLength'=> 30]),
                        'id'=> modelField\AutoField::createObject(['primaryKey'=> true, 'autoCreated'=> true]),
                    ],
                    'meta'=>[
                        'dbTable'=> 'local_user',
                        'verboseName'=> 'Local Users',
                    ],
                ]
            ),
        ] ;
    }

}

What PowerOrm looks for when it loads a migration file is a subclass of Eddmash\PowerOrm\Migration\Migration called Migration. It then inspects this object for four attributes, only two of which are used most of the time:

  • dependencies, a list of migrations this one depends on.
  • operations, a list of Operation classes that define what this migration does.

The operations are the key; they are a set of declarative instructions which tell PowerOrm what schema changes need to be made. PowerOrm scans them and builds an in-memory representation of all of the schema changes to all apps, and uses this to generate the SQL which makes the schema changes.

That in-memory structure is also used to work out what the differences are between your models and the current state of your migrations; PowerOrm runs through all the changes, in order, on an in-memory set of models to come up with the state of your models last time you ran makemigrations. It then uses these models to compare against the ones in your models directory to work out what you have changed.

You should rarely, if ever, need to edit migration files by hand, but it’s entirely possible to write them manually if you need to. Some of the more complex operations are not autodetectable and are only available via a hand-written migration, so don’t be scared about editing them if you have to.