Como criar/atualizar tabelas e dados com DataPatch e SchemaPatch no Magento 2

Lucas Teixeira dos Santos Santana - Oct 15 '21 - - Dev Community

Contextualizando

A partir da versão 2.3 do Magento foi introduzido Schema Patch (pacote de esquemas) e Data Patch (pacote de dados), uma nova forma para os módulos aplicar mudança nos esquemas e dados das tabelas.

Diferente das configurações declarativas (arquivo db_schema.xml), a classe do tipo Schema Patch ou Data Patch são executadas apenas uma vez. Uma lista dos pacotes já executadas são armazenadas na tabela patch_list no banco de dados.

Quando usar os pacotes de dados e esquemas?

Os pacotes podem ser executados apenas uma vez, e não dependem do atributo setup_version do arquivo module.xml.

A classe Schema Patch entrou pra substituir as classes de Install Schema e Upgrade Schema na alteração dos esquemas das tabelas de módulos que não devem ser alterados diretamente (como módulos do Magento, de terceiros, entre outros). Com esta classe também é possível criar tabelas, mas para a criação de tabelas recomendo que utilize as configurações declarativas (arquivo db_schema.xml). Saiba como criar uma tabela personalizada no post "Como criar tabelas personalizadas no banco de dados com db_schema.xml no Magento 2".

A classe Data Patch entrou pra substituir as classes de Install Data e Upgrade Data na inserção, remoção e alteração de dados nas tabelas.


Código para a criação de classes SchemaPatch e DataPatch

Schema Patch

A classe Schema Patch deve seguir a estrutura de pastas \{Vendor}\{Module}\Setup\Patch\Schema\{SchemaPatchName} e implementar a interface \Magento\Framework\Setup\Patch\SchemaPatchInterface, que obriga a classe a implementar os métodos getAliases(), apply(), getDependencies() e getVersion().

Caso o módulo seja desinstalado, é recomendado a reversão do seu Schema Patch, que é possível ser feita através da interface \Magento\Framework\Setup\Patch\PatchRevertableInterface e implementando o método revert().

<?php

namespace {Vendor}\{Module}\Setup\Patch\Schema;

use Magento\Framework\Setup\Patch\SchemaPatchInterface;
use Magento\Framework\Setup\Patch\PatchRevertableInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use {Vendor}\{Module}\Setup\Patch\Schema\{DependencyName};
use Magento\Framework\DB\Ddl\Table;

class {SchemaPatchName} implements SchemaPatchInterface, PatchRevertableInterface
{
    public const TABLE_NAME = '{table_name}';

    public function __construct(
        private ModuleDataSetupInterface $moduleDataSetup
    ) {
    }

    public function apply(): void
    {
        $this->moduleDataSetup->getConnection()->startSetup();

        // Code here that you want apply in the patch

        // Add new column to table
        if ($this->moduleDataSetup->getConnection()->isTableExists(self::TABLE_NAME)) {
            // Add new column to table
            $this->moduleDataSetup->getConnection()->addColumn(
                $this->moduleDataSetup->getTable(self::TABLE_NAME),
                '{new_column}',
                [
                    'type' => Table::TYPE_{NAME},
                    'nullable' => {true/false},
                    'length' => '{length}',
                    'comment'  => '{column comment}',
                    'after' => '{column_name}'
                ]
            );

            // Add new foreign key to table
            $this->moduleDataSetup->getConnection()->addForeignKey(
                '{CURRENT_TABLE_NAME}_{COLUMN_NAME}_{REFERENCE_TABLE_NAME}_{REFERENCE_COLUMN_NAME}',
                self::TABLE_NAME,
                '{column_name}',
                '{reference_table_name}',
                '{reference_column_name}',
                '{TRIGGER FOREIGN VALUE}',
            );

            // Add new index column to table
            $this->moduleDataSetup->getConnection()->addIndex(
                self::TABLE_NAME,
                '{CURRENT_TABLE_NAME}_{COLUMN_NAME}',
                '{column_name}',
                '{index_value}'
            );

            // Change column to table
            $this->moduleDataSetup->getConnection()->changeColumn(
                $this->moduleDataSetup->getTable(self::TABLE_NAME),
                '{old_column_name}',
                '{new_column_name}',
                [
                    'type' => Table::TYPE_{NAME},
                    'length' => '{length}',
                    'nullable' => {true/false},
                    'comment' => '{column comment}'
                ]
            );

            // Remove column from table
            $this->moduleDataSetup->getConnection()->dropColumn(
                $this->moduleDataSetup->getTable('{table_name}'),
                '{column_name}'
            );

            // Remove table
            $this->moduleDataSetup->getConnection()->dropTable(
                $this->moduleDataSetup->getTable('{table_name}')
            );
        }

        $this->moduleDataSetup->getConnection()->endSetup();
    }

    public static function getDependencies(): array
    {
        return [
            {DependencyName}::class,
            {DependencyName}::class,
            {DependencyName}::class
        ];
    }

    public function getAliases(): array
    {
        return [];
    }

    public function getVersion(): string
    {
        return '{version_number}';
    }

    public function revert(): void
    {
        $this->moduleDataSetup->getConnection()->startSetup();
        // Code here that you want revert in the patch
        $this->moduleDataSetup->getConnection()->endSetup();
    }
}
Enter fullscreen mode Exit fullscreen mode
  • getAliases(): método onde é definido o alias para o Schema Patch, caso um Schema Patch sofra uma alteração no nome e não seja necessário a execução do mesmo novamente, seu alias pode ser retornado nessa função dentro de um array
  • apply(): método que deve ter a execução principal, onde os esquemas serão modificados, adicionados ou removidos
  • getDependencies(): método que pode conter as dependências em forma de array com classes do Magento que serão executados antes do Schema Patch atual. Entretanto se o array retornado for vazio significa que o Schema Patch é independente de qualquer atualização
  • getVersion(): método que retornar em formato de string o número da versão. Se a versão do módulo no banco de dados é maior que a versão especificado no Data Patch, o método apply() não será executado. Caso a versão for menor ou igual, será executado

Data Patch

A classe Data Patch deve seguir a estrutura de pastas \{Vendor}\{Module}\Setup\Patch\Data\{DataPatchName} e implementar a interface \Magento\Framework\Setup\Patch\DataPatchInterface, que obriga a classe a implementar os métodos getAliases(), apply(), getDependencies() e getVersion().

Caso o módulo seja desinstalado, é recomendado a reversão do seu Data Patch, que é possível ser feita através da interface \Magento\Framework\Setup\Patch\PatchRevertableInterface e implementando o método revert().

<?php

namespace {Vendor}\{Module}\Setup\Patch\Data;

use Magento\Framework\Setup\Patch\DataPatchInterface;
use Magento\Framework\Setup\Patch\PatchRevertableInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use {Vendor}\{Module}\Setup\Patch\Data\{DependencyName};

class {DataPatchName} implements DataPatchInterface, PatchRevertableInterface
{
    public const TABLE_NAME = '{table_name}';

    public function __construct(
        private ModuleDataSetupInterface $moduleDataSetup
    ) {
    }

    public function apply(): void
    {
        $this->moduleDataSetup->getConnection()->startSetup();

        // Code here that you want apply in the patch

        if ($this->moduleDataSetup->getConnection()->isTableExists(self::TABLE_NAME)) {
            $data = [
                '{column_name_1}' => '{value}',
                '{column_name_2}' => 1,
                '{column_name_3}' => {true/false},
                '{column_name_n}' => '0000-00-00'
            ];

            $this->moduleDataSetup->getConnection()->insert(
                $this->moduleDataSetup->getTable(self::TABLE_NAME),
                $data
            );
        }

        $this->moduleDataSetup->getConnection()->endSetup();
    }

    public static function getDependencies(): array
    {
        return [
            {DependencyName}::class
        ];
    }

    public function getAliases(): array
    {
        return [];
    }

    public function getVersion(): string
    {
        return '{version_number}';
    }

    public function revert(): void
    {
        $this->moduleDataSetup->getConnection()->startSetup();

        // Code here that you want revert in the patch

        if ($this->moduleDataSetup->getConnection()->isTableExists(self::TABLE_NAME)) {
            $this->moduleDataSetup->getConnection()->delete(
                $this->moduleDataSetup->getTable(self::TABLE_NAME),
                ['{column_name} = ?' => '{value}']
            );
        }

        $this->moduleDataSetup->getConnection()->endSetup();
    }
}
Enter fullscreen mode Exit fullscreen mode
  • getAliases(): método onde é definido o alias para o Data Patch, caso um Data Patch sofra uma alteração no nome e não seja necessário a execução do mesmo novamente, seu alias pode ser retornado nessa função dentro de um array
  • apply(): método que deve ter a execução principal, onde os dados serão modificados, adicionados ou removidos
  • getDependencies(): método que pode conter as dependências em forma de array com classes do Magento que serão executados antes do Data Patch atual. Entretanto se o array retornado for vazio significa que o Data Patch é independente de qualquer atualização
  • getVersion(): método que retornar em formato de string o número da versão. Se a versão do módulo no banco de dados é maior que a versão especificado no Data Patch, o método apply() não será executado. Caso a versão for menor ou igual, será executado

Finalizando

Valores entre chaves ({test}) devem ser alterados na implementação do código.

Habilitando as alterações

Comando para atualizar os dados do banco de dados e o esquema do banco de dados.

php bin/magento setup:upgrade
Enter fullscreen mode Exit fullscreen mode

Diretórios e Arquivos

Segue a a lista de diretórios e arquivos que devem ser criados.

- app/
  - code/
    - {Vendor}/
        - {Module}/
          - etc/
                        - module.xml
          - Setup/
            - Patch/
              - {Schema/Data}/
                - {ClassName}.php
          - registration.php
          - composer.json
Enter fullscreen mode Exit fullscreen mode
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .