Migration

Migration is a database version control system, it help our team to manage database schema and stay up to date on newest schema state. Every member are allow to create a new schema version, and migration system will help us sync local schema to newest version.

The Migration Flow

migration

Maybe you are Develop B, and your version is 20150112_AddSakuraTable.

If migration system found your version is not up to date, you may run migration migrate, then migration system will update schema to the newest version: 20150421_AddTableIndex

Migrations are typically paired with the Schema Builder to easily manage the database schema.

Create A New Version

To create a migration version, you may use migration create command in console:

php windwalker migration create InitFlowerTable

You'll see this info:

Migration version: 20150101091434_InitFlowerTable.php created.
File path: /your/project/path/migrations/20150101091434_InitFlowerTable.php

Create to Custom Position

You may also create migration to other position by --dir or --package

## Create to custom directory
php windwalker migration create InitFlowerTable --dir=resources/migrations

## Create to a package's Migration folder
php windwalker migration create InitFlowerTable --package=flower

Writing Migration

This is a new migration file. The up() method will run if your version is lower than latest version. The down() method will run if you migrate to older version.

<?php
// src/resources/migrations/20150101091434_InitFlowerTable.php

use Windwalker\Core\Migration\AbstractMigration;

/**
 * Migration class, version: 20150101091434
 */
class InitFlowerTable extends AbstractMigration
{
    /**
     * Migrate Up.
     */
    public function up()
    {

    }

    /**
     * Migrate Down.
     */
    public function down()
    {

    }
}

Here is an example to create a table:

use Windwalker\Core\Migration\AbstractMigration;
use Windwalker\Core\Migration\Schema;
use Windwalker\Database\Schema\Column;
use Windwalker\Database\Schema\DataType;
use Windwalker\Database\Schema\Key;

class InitFlowerTable extends AbstractMigration
{
    /**
     * Migrate Up.
     */
    public function up()
    {
        $this->createTable(Table::SAKURAS, function(Schema $schema)
        {
            $schema->primary('id')->allowNull(false)->signed(false)->comment('Primary Key');
            $schema->varchar('title')->comment('Title');
            $schema->varchar('alias')->comment('Alias');
            $schema->varchar('url')->comment('URL');
            $schema->text('introtext')->comment('Intro Text');
            $schema->text('fulltext')->comment('Full Text');
            $schema->varchar('image')->comment('Main Image');
            $schema->tinyint('state')->signed(true)->comment('0: unpublished, 1:published');
            $schema->integer('ordering')->comment('Ordering');
            $schema->datetime('created')->comment('Created Date');
            $schema->integer('created_by')->comment('Author');
            $schema->datetime('modified')->comment('Modified Date');
            $schema->integer('modified_by')->comment('Modified User');
            $schema->char('language')->length(7)->comment('Language');
            $schema->text('params')->comment('Params');

            $schema->addIndex('alias(150)');
            $schema->addIndex('language');
            $schema->addIndex('created_by');
        }, true);
    }

    /**
     * Migrate Down.
     */
    public function down()
    {
        $this->drop('flowers');
    }
}

See other schema operations: Table and Schema

Charset and Collation

After Windwalker 3.4, default charset set to utf8mb4, you will unable to add index to varchar(255) since it will make key length too long.

Consider use ->length(190) or limit key length by $schema->addIndex('alias(150)').

If you want to set default charset back to utf8, add:

AbstractMigration::#defaultCharset = AbstractMigration::CHARSET_UTF8;

at first migration class.

Check Status

Use this command to show migration status.

php windwalker migration status
 Status  Version         Migration Name
-----------------------------------------
  down   20141105131929  AcmeInit
  down   20150101091434  InitFlowerTable

Start Migrating

Use migrate command to start migrating:

php windwalker migration migrate

Terminal will show migrating process.

Migration UP the version: 20141105131929_AcmeInit
------------------------------------------------------------
Success

Migration UP the version: 20150101091434_InitFlowerTable
------------------------------------------------------------
Success

Blocked By DEV Mode

Migration will be blocked if your system in prod mode, modify or create .mode file in system root to dev that can allow migration works. Don't forget change back to prod after you updated your system.

p-2016-07-15-002

You can also use command to change mode $ php windwalker system mode [dev|prod]

Run Package Migration

php windwalker migration migrate --package=flower

Migrate to Specific Version

php windwalker migration migrate 20141105131929

If you use a lower version, this action will be downgrade.

Seeding

Windwalker also provides a simple way to help you create fixtures for easy testing. The default seeder class will store in /resources/seeders , the package seeder will store in /src/YourPackage/Seeder.

Default Seeder

Every time after you installed Windwalker, there will be a MainSeeder.php in /resources/seeders:

<?php
// resources/seeders/MainSeeder.php

use Windwalker\Core\Seeder\AbstractSeeder;

class MainSeeder extends AbstractSeeder
{
    public function doExecute()
    {
    }

    public function doClear()
    {
    }
}

If you didn't add --class option, this file will be seeder default entry, we can follow these steps to create our seeders:

Add new Seeder file (Use IDE or code editor)

// resources/seeders/FlowerSeeder.php

/**
 * This class dose not has Namespace
 */
class FlowerSeeder extends \Windwalker\Core\Seeder\AbstractSeeder
{
    public function doExecute()
    {
        $data = array();

        // Create your fake data...

        foreach ($data as $item) {
            $this->db->getWriter()->insertOne('flowers', $item);

            // Or use DataMapper
            FlowerMapper::createOne($item);

            $this->outCounting(); // Show inserted count
        }
    }

    public function doClear()
    {
        // Truncate table
        $this->truncate('flowers');
    }
}

Then call this seeder in default MainSeeder:

class MainSeeder extends AbstractSeeder
{
    public function doExecute()
    {
        // Execute sub seeder 
        $this->execute(FlowerSeeder::class);
        $this->execute(SakuraSeeder::class);
        $this->execute(RoseSeeder::class);

        $this->command->out('Seeder executed.')->out();
    }

    public function doClear()
    {
        // Truncate table
        $this->clear(FlowerSeeder::class);
        $this->clear(SakuraSeeder::class);
        $this->clear(RoseSeeder::class);

        $this->command->out('Database clean.')->out();
    }
}

Now, use seed import command:

php windwalker seed import

You will see this output, it means seeder execute success:

Import seeder FlowerSeeder
  (20) -
  Import completed...

Import seeder SakuraSeeder
  (50) -
  Import completed...

Import seeder RoseSeeder
  (50) -
  Import completed...

Seeder executed.

Package Seeder

Package Seeder is allow to use namespace as class name. Add --package|-p to use package default MainSeeder.

// src/FlowerPackage/Seeder/FlowerSeeder.php

use Windwalker\Core\Seeder\AbstractSeeder;

class FlowerSeeder extends AbstractSeeder
{
    public function doExecute()
    {
    }
}

Run this command to execute package seeder:

## Choose class if your seeder has namespace
php windwalker seed import --class=Flower\Seeder\MySeeder

## Use package default MainSeeder
php windwalker seed import -p=flower

Fake Data Generator

Windwalker includes PHP Faker to help you generate random fake data.

class ArticleSeeder extends AbstractSeeder
{
    public function doExecute()
    {
        // Get Faker
        $faker = \Faker\Factory::create();

        // Get Users in database
        $users = (new DataMapper('users'))->findAll();

        // Article DataMapper
        $articleMapper = new DataMapper('articles');

        // Create articles for every user in database
        foreach ($users as $user) {
            // Every use 10 articles
            foreach (range(1, 10) as $i) {
                // Use Faker to generate fake data
                $article = array(
                    'title'     => $faker->sentence(),
                    'author'    => $user->name,
                    'author_id' => $user->id,
                    'text'      => $faker->paragraphs(3),
                    'created'   => $faker->date()
                );

                $articleMapper->createOne($article);
            }
        }
    }
}

If you found a typo or error, please help us improve this document.