Core points
Version control systems are invaluable for tracking changes in code, especially when working in teams. However, most applications contain more than just application code. Managing changes to databases has been more challenging, especially when adding new features that require changing patterns.
Suppose you are working on a module and realize that a database table requires an extra column. You might want to open a database query tool or command line and simply add that column. However, this does not leave a record of changes like version-controlled application code. This can be exacerbated when working in a team – if colleagues extract your code changes without running the same database update, then their application version will most likely crash. This becomes even more problematic when you release product updates, as this can break the app not just for your colleagues, but for your users.
One solution is to use migration to transfer the responsibility for creating and modifying database schemas into your code. This way, changes can be managed together with the rest of the application, and we can use features we are accustomed to in version control (such as being able to compare versions and keep audit trails) to handle database changes. It also allows these changes to be seamlessly integrated into the version, as they can be part of the same branch or tag.
Many major frameworks have their own migration implementations, but for those that don't - if you don't use any - you can use Ladder.
Ladder Introduction
Ladder is a tool for creating, running, and managing database migrations. Migration is just a PHP class, so version control can be checked in with the rest of the application's code.
You can make as many changes to the schema in a single migration, although it is best to limit the migration to a single table or feature.
Migrations are always run in order. So, suppose you write a migration to create the products table and create a new migration a few weeks later to add an extra column to it. Trying to run the former before the latter will produce an error. Ladder solves this problem by numbering migrations in sequence and storing records (and time) of running migrations in the database itself.
Installation
Ladder can be downloaded from Bitbucket or through Composer. However, it takes several steps to get up and running.
The easiest way (although admits to being less elegant) is to download or clone it from the project root and place it in a directory called ladder. The problem with using Composer is that running composer update will overwrite your configuration file.
There are five configuration files, each of which needs to be created manually; the easiest way is to copy the provided example:
cp ladder/config/config.php.example ladder/config/config.php cp ladder/config/database.php.example ladder/config/database.php cp ladder/config/diff.php.example ladder/config/diff.php cp ladder/config/editor.php.example ladder/config/editor.php cp ladder/config/table.php.example ladder/config/table.php
You may just need to modify database.php (which contains database connection details) and editor.php, where you can specify the preferred text editor, and choose to automatically open a new migration when created.
Create migration
Let's start by creating a migration to create the users table.
On the command line:
php ladder/ladder.php create create_users_table
This creates a file named ladder/migrations/00001_create_users_table, which by default contains a basic structure with commented out example calls. If you have set auto-edit to true in ladder/config/editor.php, this file will be opened immediately in the specified text editor. You can call migrations at will, but it will be helpful to be able to see what the migration does from the file name at a glance.
Here is what the file might look like (I deleted the commented out line of code for clarity):
class Create_Users_Table_Migration_00001 extends Migration { protected $min_version = '0.8.1'; public function up() { $this->create_table('users') ->column('email', 'varchar', array('limit' => 128, 'null' => FALSE)) ->column('password', 'varchar', array('limit' => 32, 'null' => FALSE)); } public function down() { $this->table('users')->drop(); } }
Call the up() method when the migration is run, and the down() method when the migration is rolled back - so it always needs to perform the opposite operation to the up() method. In this example, the up() method creates a table named users, and the down() method deletes it.
Thecreate_table() method returns a reference to the new table, while the Table class has a column() method to add a new column. It has a smooth interface so you can link them together to create multiple columns at the same time. You will notice that there is no ID column - this is automatically created - i.e. an auto-incremental integer primary key named id.
table() returns a reference to the table, but it also creates the table if the table does not exist yet - so you can safely change the create_table() call to table()
Let's create another migration, this time creating roles table:
php ladder/ladder.php create create_roles_table
File itself:
class Create_Roles_Table_Migration_00002 extends Migration { protected $min_version = '0.8.1'; public function up() { $this->table('roles') ->column('nme', 'varchar', array('limit' => 128, 'null' => FALSE)); } public function down() { $this->table('roles')->drop(); } }
Now you need to actually run the migration. To do this:
php ladder/ladder.php migrate
If you look at the database, you will find four tables:
migrations are created automatically to track which migrations have been run migrations_kvdata is also created for you, and your migrations can be used for any key-value storage users and roles are the tables we just added.
However, if you pay close attention to roles migration, you will notice that it creates a column called nme instead of name. At this point, we can fix this by "undoing" the migration, modifying the class, and running it again. To roll back the migration:
cp ladder/config/config.php.example ladder/config/config.php cp ladder/config/database.php.example ladder/config/database.php cp ladder/config/diff.php.example ladder/config/diff.php cp ladder/config/editor.php.example ladder/config/editor.php cp ladder/config/table.php.example ladder/config/table.php
Number 2 indicates the migration to be rolled back to - it is the prefix of the migration file name, without leading zeros.
Now you can simply make the correction and run the migration again:
php ladder/ladder.php create create_users_table
Because the migrations table has stored records of running and unrun content, you don't need to worry that your first migration will be rerun.
There is also a faster way to use reapply:
class Create_Users_Table_Migration_00001 extends Migration { protected $min_version = '0.8.1'; public function up() { $this->create_table('users') ->column('email', 'varchar', array('limit' => 128, 'null' => FALSE)) ->column('password', 'varchar', array('limit' => 32, 'null' => FALSE)); } public function down() { $this->table('users')->drop(); } }
This will call the second migration's down() method at once, and then its up() method.
Now let's say after a while, we develop a new feature that requires the users table to contain a status field. To do this, we need to create a new migration:
php ladder/ladder.php create create_roles_table
The migration will look like this:
class Create_Roles_Table_Migration_00002 extends Migration { protected $min_version = '0.8.1'; public function up() { $this->table('roles') ->column('nme', 'varchar', array('limit' => 128, 'null' => FALSE)); } public function down() { $this->table('roles')->drop(); } }
As before, run with the following command:
php ladder/ladder.php migrate
Now, if you check your database, you will see that the users table has a new column.
Database seeding
In addition to writing migrations to manage database schemas, you can also use Ladder to pre-populate (i.e. seed) databases.
For example, you can extend the create_users_table migration to create a default root user account:
php ladder/ladder.php remove 2
Note that we are using the set() and get() methods to take advantage of Ladder's key-value store, storing the user ID so that we can refer to it later.
You can also import data from migrated CSV files, for example:
php ladder/ladder.php migrate
To update instead of inserting CSV data, you can do this:
php ladder/ladder.php reapply 2
In this case, the send parameter means we want to run UPDATE, and the third parameter contains a list of key fields to determine which records to update.
Add other databases
You will notice that the default database configuration file ladder/config/database.php demonstrates how to add additional database connections; perhaps for staging or real-time databases. For example:
php ladder/ladder.php create add_status_to_users_table
You can specify the connection to use like this:
class Add_Status_To_Users_Table_Migration_00003 extends Migration { protected $min_version = '0.8.1'; public function up() { $this->table('users') ->column('status', 'integer', array('null' => FALSE, 'default' => 0)); } public function down() { $this->table('users')->drop_column('status'); } }
Other commands
Status
You can use the status command to get the database status:
php ladder/ladder.php migrate
Example output: (The example output is omitted here because the original document does not provide specific output content)
diff and diff-save
(The descriptions of the diff and diff-save commands are omitted here, as these commands have been described in detail in the original document)
version
(The description of the version command is omitted here, because the command has been described in detail in the original document)
Summary
This article introduces Ladder, used to maintain database schema and pre-populate data. (The summary part is omitted here, because the original document has summarized Ladder)
FAQs about database versioning and Ladder migration
(The FAQ section is omitted here, as the original document already provides detailed FAQ)
The above is the detailed content of Database Versioning with Ladder Migrations. For more information, please follow other related articles on the PHP Chinese website!