Como automatizar alterações no banco de dados com Phinx e DevOps
Em tempos que se fala tanto de DevOps quero te mostrar como utilizar o Phinx para efetuar alterações no seu banco de dados à medida que a sua aplicação evolui, possibilitando que você automatize esse processo com uma ferramenta de integração contínua logo em seguida.
Imagine a seguinte situação: no seu ambiente de desenvolvimento você criou sua aplicação em PHP bem como o seu banco de dados. Logo em seguida você o colocou em produção. Até aí está tudo lindo, maravilhoso. Dando continuidade ao seu projeto você percebeu a necessidade de criar uma tabela nova. Ok, sem problemas, no seu ambiente de desenvolvimento você cria a bendita tabela. Mas como você procede para fazer isso em produção?
Talvez você não tenha percebido o problema agora, mas no futuro e provavelmente bem próximo, conforme as demandas forem aumentando, fica complicado administrar essas alterações do banco de dados em produção. E se você tiver um ambiente de homologação então, fazer a mesma alteração no banco de dados 3 vezes, já pensou?
Se você passa por esse tipo de situação quero lhe apresentar o Phinx.
O que é o Phinx?
De acordo com a documentação, o Phinx é uma ferramenta via linha de comando para gerenciamento de migrações de banco de dados. Com ele você poderá escrever scripts PHP para efetuar as alterações que você necessita no seu banco de dados e gerenciar isso via linha de comando.
Alguns frameworks fullstack de PHP tem recurso para migrations, inclusive o do CakePHP é baseado no próprio Phinx, mas vale a pena falar dele ou talvez porque você não esteja usando framework, ou se trata de código legado com um framework sem esse recurso ou simplesmente pela facilidade que o Phinx proporciona.
Instalação
A instalação pode ser feita via composer:
juciellen@cabrera:~/Documentos/projetos/expressive$ php composer.phar require robmorgan/phinx Using version ^0.9.1 for robmorgan/phinx ./composer.json has been updated Loading composer repositories with package information Updating dependencies (including require-dev) Package operations: 3 installs, 0 updates, 0 removals - Installing symfony/filesystem (v3.3.13): Downloading (100%) - Installing symfony/config (v3.3.13): Downloading (100%) - Installing robmorgan/phinx (v0.9.1): Downloading (100%) Writing lock file Generating autoload files
A seguir crie os diretórios db/migrations para armazenar as suas migrations não esquecendo de dar as devidas permissões. Feito isso execute o comando abaixo no terminal:
juciellen@cabrera:~/Documentos/projetos/expressive$ php vendor/bin/phinx init Phinx by CakePHP - https://phinx.org. 0.8.1 created ./phinx.yml
Repare que o arquivo phinx.yml foi criado na raiz do projeto. Esse é o arquivo de configuração do phinx.
Configurando o Phinx
Abaixo um modelo enxuto de phinx.yml.
paths: migrations: %%PHINX_CONFIG_DIR%%/db/migrations environments: default_migration_table: phinxlog default_database: development development: adapter: pgsql host: localhost name: db_test user: SEU_USUARIO pass: SENHA_SEGURA port: 5432 charset: utf8 version_order: creation
Neste arquivo você precisará indicar os diretórios das suas migrations em paths, no nosso caso db/migrations.
Em environments deve conter as configurações para conexão com banco de dados em todos os ambientes em que você executará suas migrations. Eu deixei apenas as configurações do meu ambiente de desenvolvimento mas você pode acrescentar quantos blocos forem necessários, como homolog, production e etc. Eu estou usando Postgres, por isso o adapter é pgsql, mas consulte o manual do phinx para indicar o adapter de acordo com o SGDB que você estiver usando.
Escrevendo migrations
Para criar uma migration você deve executar o seguinte comando no terminal, sempre a partir do diretório que estiver o seu phinx.yml
juciellen@cabrera:~/Documentos/projetos/expressive$ php vendor/robmorgan/phinx/bin/phinx create MinhaPrimeiraMigration
O padrão de nome de migration é CamelCase. O resultado é o seguinte:
Phinx by CakePHP - https://phinx.org. 0.8.1 using config file ./phinx.yml using config parser yaml using migration paths - /home/juciellen/Documentos/projetos/expressive/db/migrations using seed paths using migration base class Phinx\Migration\AbstractMigration using default template created db/migrations/20171124233848_minha_primeira_migration.php
Repare que o arquivo 20171124233848_minha_primeira_migration.php foi criado no diretório db/migrations, que indicamos na configuração. É nesse arquivo que escreveremos nossa migration.
<?php use Phinx\Migration\AbstractMigration; class MinhaPrimeiraMigration extends AbstractMigration { /** * Change Method. * * Write your reversible migrations using this method. * * More information on writing migrations is available here: * http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class * * The following commands can be used in this method and Phinx will * automatically reverse them when rolling back: * * createTable * renameTable * addColumn * renameColumn * addIndex * addForeignKey * * Remember to call "create()" or "update()" and NOT "save()" when working * with the Table class. */ public function change() { } }
Notem que o arquivo já vem com um método chamado change. É dentro desse método que você vai escrever o que vai ocorrer quando a migration for executada (você pode usar o método up para escrever a migration, mais a frente explico a diferença).
Vamos escrever uma migration que cria a tabela users contendo os campos id, name, email, password, active e created.
public function change() { $this->table('users') ->addColumn('name', 'string') ->addColumn('email', 'string', [ 'null' => false ]) ->addColumn('password', 'string', [ 'null' => false ]) ->addColumn('active', 'boolean', [ 'null' => false, 'default' => true ]) ->addColumn('created', 'datetime',['default'=>'CURRENT_TIMESTAMP']) ->create(); } }
Reparem que não criei o campo id, porque o phinx vai fazer isso automaticamente ao executar a migration.
Utilizei alguns métodos que vou explicar melhor:
table -> passando como parâmetro users, que é o nome da tabela que desejo trabalhar.
addColumn -> passando como parâmetros o nome da nova coluna, o tipo e as opções. Em opções é onde você pode indicar se o campo pode ser nulo, definir um valor default e por aí vai.
create -> cria a tabela
Executando migrations
Para executar as migrations você deve executar o comando migrate. Caso você precise executar a migration em mais de um ambiente não esqueça de indicá-lo, no meu caso migrate -e development.
juciellen@cabrera:~/Documentos/projetos/expressive$ php vendor/robmorgan/phinx/bin/phinx migrate -e development Phinx by CakePHP - https://phinx.org. 0.8.1 using config file ./phinx.yml using config parser yaml using migration paths - /home/juciellen/Documentos/projetos/expressive/db/migrations using seed paths using environment development using adapter pgsql using database db_test == 20171124233848 MinhaPrimeiraMigration: migrating == 20171124233848 MinhaPrimeiraMigration: migrated 0.1273s All Done. Took 0.1428s
Após a execução a tabela foi criada, bem como a respectiva sequence. Simples não?
db_test=> \d List of relations Schema | Name | Type | Owner --------+--------------+----------+----------- public | phinxlog | table | jucabrera public | users | table | jucabrera public | users_id_seq | sequence | jucabrera (3 rows) db_test=> \d users Table "public.users" Column | Type | Modifiers ----------+-----------------------------+---------------------------------------------------- id | integer | not null default nextval('users_id_seq'::regclass) name | character varying(255) | not null email | character varying(255) | not null password | character varying(255) | not null active | boolean | not null default true created | timestamp without time zone | not null default now() Indexes: "users_pkey" PRIMARY KEY, btree (id)
O phinx gerencia as migrations executadas utilizando a tabela phinxlog criada por ele automaticamente no seu banco de dados, de modo que uma vez executada a migration, ela não será executada novamente.
Você pode acompanhar isso através do comando status.
juciellen@cabrera:~/Documentos/projetos/expressive$ php vendor/robmorgan/phinx/bin/phinx status -e development Phinx by CakePHP - https://phinx.org. 0.8.1 using config file ./phinx.yml using config parser yaml using migration paths - /home/juciellen/Documentos/projetos/expressive/db/migrations using seed paths using environment development ordering by creation time Status [Migration ID] Started Finished Migration Name ---------------------------------------------------------------------------------- up 20171124233848 2017-11-24 22:41:47 2017-11-24 22:41:47 MinhaPrimeiraMigration
Rollback
Pode ocorrer de você perceber que esqueceu de criar algum campo, por exemplo, e queira desfazer o que acabou de fazer. Sendo assim você pode utilizar o rollback (use com moderação).
Para desfazer a última migration execute o comando rollback -e development.
juciellen@cabrera:~/Documentos/projetos/expressive$ php vendor/robmorgan/phinx /bin/phinx rollback -e development Phinx by CakePHP - https://phinx.org. 0.8.1 using config file ./phinx.yml using config parser yaml using migration paths - /home/juciellen/Documentos/projetos/expressive/db/migrations using seed paths using environment development using adapter pgsql using database db_test ordering by creation time == 20171124233848 MinhaPrimeiraMigration: reverting == 20171124233848 MinhaPrimeiraMigration: reverted 0.0429s All Done. Took 0.0577s
O bacana de escrever a migration no método change utilizando os métodos específicos do phinx (table, addColumn, etc) é que em caso de rollback o phinx saberá exatamente como desfazer a ação. Se usarmos o método up ao invés do change, em caso de rollback se precisarmos desfazer o que foi definido no up vamos precisar escrever as instruções para isso no método down.
Após o rollback, meu banco não conterá mais a tabela users criada anteriormente.
db_test=> \d List of relations Schema | Name | Type | Owner --------+----------+-------+----------- public | phinxlog | table | jucabrera
Como o change é invocado também no rollback, não o utilize em migrations de inserções de dados por exemplo, apenas para migrations de alteração de estrutura de tabelas.
Caso você precise dar rollback em várias migrations passe o parametro -t CÓDIGO_DA_MIGRATION_LIMITE
Utilizando SQL
Você também tem a opção de escrever uma instrução em SQL utilizando o método execute para casos em você não queira (ou não possa) utilizar os métodos onde toda a lógica de execução de comandos no banco de dados está abstraída. Isso ocorre por exemplo quando você quer executar comandos relacionados a dados e não à estrutura (INSERT, UPDATE, DELETE, SELECT).
public function up() { $this->execute("INSERT INTO users (name, email, password) VALUES ('teste','teste@teste.com','senha_segura')"); }
Notem que utilizei o método up e não o change. Conforme mencionei acima, se você escrever no change e der rollback, essa instrução será executada novamente duplicando os dados.
Alertas
- Cuidado com os comandos que você escreve e o ambiente em que está executando a migration. Há chances de você executar DELETE ou UPDATE sem WHERE em produção. Se você não quer correr esse risco, remova o bloco production do seu phinx.yml.
- Se você estiver utilizando Git (espero que sim) não esqueça de colocar o phinx.yml no .gitignore senão já sabe né? Seus dados de acesso ao banco de dados estarão expostos.
- Use nomes intuitivos para suas migrations, para que pelo nome você rapidamente consiga localizar o que você precisa.
Conclusão
Não disse que o phinx era simples? Você não precisa ter um conhecimento amplo dessa biblioteca para conseguir utilizá-la.
Com isso o seu processo de atualização do banco de dados nos outros ambientes vai ficar muito mais simples.
Além disso outro fator super importante é conseguir utilizá-lo no seu processo de integração contínua (ou Continuous Integration – CI).
Para saber mais consulte a documentação do phinx nos links:
http://docs.phinx.org/en/latest/
https://book.cakephp.org/3.0/en/phinx.html
About author
Você pode gostar também
Descubra o Redis: a solução open source para armazenamento de dados
Administradores de sistemas e desenvolvedores provavelmente em algum momento já precisaram de alguma solução para armazenar dados temporários – como token de sessão – que sejam acessíveis de um ponto
Alavanque seu negócio com a cultura DevOps: saiba como implementar
Estamos caminhando para fim de 2021 e muitas empresas ainda precisam se recuperar dos efeitos da pandemia e alavancar. Enquanto isso, outras se mantêm fortes e em crescimento, como as
Como configurar um repositório Yum local com Nexus Sonatype
Administradores de sistemas, sempre que operam em ambiente GNU/Linux, realizam a instalação de pacotes pré-compilados localizados em repositórios remotos. Para distribuições baseadas em Red Hat, fazemos uso de repositórios Yum