Migrations – Mantendo o banco de dados atualizado

Migrations – Mantendo o banco de dados atualizado

26/11/2019 2 Por Jonathan Lamim

Em algum momento durante o desenvolvimento ou pós-desenvolvimento de uma aplicação, pode ser necessário fazer mudanças na estrutura do banco de dados, alterando a estrutura original. Quando precisamos fazer isso com arquivos de código-fonte, podemos usar ferramentas de controle de versão (como o Git, por exemplo), para manter os arquivos atualizados para todosos envolvidos.

Para fazer isso com banco de dados no CodeIgniter temos as Migrations, que são uma forma conveniente para que você possa alterar seu banco de dados de forma estruturada e organizada.

Como as migrations são executadas

Cada migration é composta por dois métodos, up() e down(), que são executados para upgrade e downgrade, respectivamente. Quando você cria a primeira migration, e atualizaas configurações para que ela seja executada, o CodeIgniter automaticamente cria uma tabela adicional chamada migrations. Essa tabela armazena um único registro, que é a última migration executada. Essa informação serve como referência para o CodeIgniter definir se vai ser executado um upgrade ou um downgrade.

Ao criar uma outra migration e atualizar as configurações informando que essa é a que deve ser executada, o CodeIgniter vai verificar pelo parâmetro migration_type se a nova migration é posterior ou anterior à que está armazenada na tabela migrations . Se for anterior, então ele vai executar o método down() da migrate salva no banco de dados, e depois vai atualizar a informação da migrate para a que foi informada no parâmetro migration_version. Mas se ela for posterior, então ele executa o método up() da nova migrate e atualiza a informação no banco de dados.

Configurações

As configurações das migrations são feitas em application/config/migration.php. Nesse arquivo são setadas as informações necessárias para as migrations, conforme a lista a seguir:

  • migration_enabled ─ Ativa ou desativa as migrations.
    • Valor padrão: FALSE
    • Opções: TRUE ou FALSE
  • migration_path ─ Caminho para o diretório onde se encontram os arquivos com a atualização do schema.
    • Valor padrão: APPPATH.'migrations/'
  • migration_version ─ Versão atual do banco de dados utilizada.
    • Valor padrão: 0
  • migration_table ─ Nome da tabela usada para armazenar o número da versão do schema.
    • Valor padrão: migrations
  • migration_auto_latest ─ Ativa ou desativa a execução automática das migrations.
    • Valor padrão: FALSE
    • Opções: TRUE ou FALSE
  • migration_type ─ Tipo de identificador usado nos nomes dos arquivos.
    • Valor padrão: timestamp
    • Opções: timestamp ou sequential

Agora que você já sabe como funcionam as migrations no CodeIgniter é hora de fazer uma implementação prática usando esse recurso.

Criando a aplicação

Crie uma nova aplicação com o CodeIgniter e um banco de dados para seguir com o tutorial. Lembre-se de atualizar o arquivo application/config/database.php com as informações da sua conexão ao banco de dados.

Configurando a execução automática das migrations

Por meio das configurações feitas em application/config/migration.php, é possível fazer com que a atualização do schema do banco de dados seja feita sem a necessidade de executar o código manual em algum controller.

As configurações necessárias para que isso funcione são as que veremos a seguir.

Adicione o carregamento da library Migration ao autoload, em application/config/autoload.php:

$autoload['libraries'] = array('migration');

Em seguida, atualize o arquivo application/config/migration.php, conforme mostrado a seguir:

$config['migration_enabled'] = TRUE;
$config['migration_auto_latest'] = TRUE;
$config['migration_version'] = '20150217164137';

Com os parâmetros migration_enabled e migration_auto_latest setados como TRUE, e a library Migration sendo carregada automaticamente no autoload, o processo de atualização do banco de dados, seja para upgrade ou downgrade, acontecerá automaticamente ao carregar a aplicação.

O parâmetro migration_version pode ser setado de duas formas, dependendo do que foi passado para o parâmetro migration_type:

Forma #1

$config['migration_type']  = 'sequencial';

Se migration_type for sequencial, o nome do arquivo deve ser um número inteiro sequencial (1, 2, 3, 4 etc.). Por exemplo: 001_add_usuarios.php.

Forma #2

$config['migration_type']  = 'timestamp';

Se migration_type for timestamp, o nome do arquivo deve ser no formato YYYYMMDDHHIISS. Por exemplo: 20191126164137_add_usuarios.php.

O uso do tipo timestamp faz com que seja mais fácil saber de quando exatamente é a alteração a ser executada, pois ele traz a data como identificação do nome do arquivo. Já o tipo sequencial não dá essa possibilidade, mas torna possível saber quantas alterações no banco foram feitas.

Você deve fazer a escolha conforme o que achar mais viável para a aplicação, e não alterar esse valor posteriormente. Isso porque pode afetar a lógica de execução dos upgrades e downgrades, tendo em vista que o CodeIgniter utiliza esse parâmetro como base para definição de qual método deve executar.

Criando o arquivo de atualização do banco de dados

O arquivo com as instruções sobre a migração deve ser criado em application/migrations/, e o seu nome deve ter o prefixo conforme o parâmetro migration_type, informado nas configurações.

Tomando por base a configuração apresentada anteriormente, o nome do arquivo ficaria da seguinte forma: 20191126164137_add_usuarios.php

Após salvar o arquivo, adicione o conteúdo a seguir:

defined('BASEPATH') OR exit('No direct script access allowed');

class Migration_Add_usuarios extends CI_Migration {

    public function up()
    {
        $this->dbforge->add_field(array(
            'id' => array(
                'type' => 'INT',
                'constraint' => 10,
                'unsigned' => TRUE,
                'auto_increment' => TRUE
            ),
            'name' => array(
                'type' => 'VARCHAR',
                'constraint' => '100',
            ),
            'date' => array(
                'type' => 'DATETIME',
                'null' => FALSE,
            ),
        ));
        $this->dbforge->add_key('id', TRUE);
        $this->dbforge->create_table('usuarios');
    }

    public function down(){
        $this->dbforge->drop_table('usuarios'); 
    }
}

Veja que o nome da classe é Migration_Add_usuarios. Ou seja, o nome do arquivo sem o prefixo timestamp, mas com Migration. Além disso, ela é estendida da classe CI_Migration para que possa herdar suas funcionalidades.

Dentro da classe temos os métodos up() e down(), respectivamente upgrade e downgrade do schema. Eles serão executados conforme a requisição, ficando essa verificação e execução por conta do CodeIgniter.

No método up() usamos $this->dbforge->add_field() para informar os campos que devem ser adicionados à tabela comments, criada pelo método $this->dbforge->create_table(). O método $this->dbforge->add_field() recebe como parâmetro um array multidimensional, contendo todos os campos da tabela que deverá ser criada ao executar a migration.

Além dos campos, deve ser criada também uma chave na tabela. Isso é feito com o método $this->dbforge->add_key(), que recebe dois parâmetros. O primeiro é o nome da chave, e o segundo é um booleano que determina se ela deve ou não ser a chave primária.

No método down(), é executado apenas a remoção da tabela usuarios, pelo método $this->dbforge->drop_table(), que recebe como parâmetro o nome da tabela a ser removida. A partir desse momento o CodeIgniter já está gerenciando automaticamente as atualizações no banco de dados a partir das migrations.

Assim, qualquer coisa que você precise modificar na estrutura, basta seguir a sequência de passos:

  1. Atualizar o parâmetro migration_version nas configurações, para que o CodeIgniter saiba qual arquivo de atualização executar;
  2. Criar o arquivo com as instruções de atualização no diretório application/migrations.

Configurando a execução manual de migrations

Para que as migrations sejam executadas manualmente, é preciso desativar a execução automática setando FALSE para o parâmetro migration_auto_latest nas configurações. Ao setar FALSE para esse parâmetro, você precisará executar a migration por meio de uma chamada de método em algum de seus controllers.

Abra o arquivo application/controllers/Welcome.php e atualize o código do método index() conforme o código a seguir:

public function index()
{
    if ($this->migration->current() === FALSE){
        show_error($this->migration->error_string());
    }else{
        $this->load->view('welcome_message');
    }
}

Essa alteração faz com que a atualização do banco de dados seja executada quando o método index() do controller Welcome for executado. Se a migration for executada com sucesso, a tela padrão do CodeIgniter será carregada. Caso contrário, será exibida uma mensagem com o erro ocorrido, que é obtido usando o método $this->migration->error_string().

Para testar a execução manual de uma migration altere o parâmetro de configuração migration_auto_latest conforme explicado anteriormente. Em seguida, altere migration_version para 20191126174212. Feito isso, crie um novo arquivo de migration chamado 20191126174212_add_uausrios_field_email.php.

Coloque nesse arquivo o seguinte código:

defined('BASEPATH') OR exit('No direct script access allowed');

class Migration_Add_usuarios_field_email extends CI_Migration {

    public function up()
    {
        $this->dbforge->add_column(
        'usuarios',
        array(
            'email' => array(
                'type' => 'VARCHAR',
                'constraint' => '100',
            )
        ));
    }

    public function down()
    {
        $this->dbforge->drop_column('usuarios', 'email');
    }

}

Nessa migration, vamos adicionar uma coluna chamada email à tabela usuarios, que foi criada na atualização anterior. No método up(), temos a chamada do método $this->dbforge->add_column(), que recebe como parâmetros o nome da tabela onde a coluna deve ser criada e o array com as informações da coluna, respectivamente.

Já no método down(), que será executado em caso de downgrade, é executado o método $this->dbforge->drop_column(), que remove a coluna criada no método up(). Ele recebe o nome da tabela e o nome da coluna, respectivamente, como parâmetros.

O método down() de toda migration deve ser sempre o oposto das ações do método up(). Na primeira migration criada o método up() criava uma nova tabela, logo o método down() removia a tabela criada. Na segunda migration, o método up() adicionava uma coluna à tabela usuarios, enquanto no método down() essa coluna é removida.

Agora quando o método index() do controller Welcome for acionado, será criada a coluna email na tabela usuarios.

Neste tutorial você aprendeu a usar as migrations, um recurso muito útil para manter a estrutura de banco de dados sempre atualizada no projeto, sem a necessidade de ter de ficar enviando arquivos .sql para toda a equipe, e correndo o risco de alguém esquecer de atualizar (ou até você mesmo).