← Voltar ao blog

Como remover um módulo do Magento 2 com segurança

Rodrigo Biazzi

Apagar a pasta de um módulo do Magento leva um segundo. Descobrir uma semana depois que salvar produto no admin dá erro de "class does not exist" leva bem mais tempo. Módulos deixam rastros no banco que apontam para as classes PHP deles, e quando as classes somem, essas referências quebram páginas que parecem não ter relação com o módulo.

Este artigo descreve um processo de remoção que evita essas surpresas. Funciona para módulos instalados com Composer e para módulos em app/code, no Magento Open Source e no Adobe Commerce 2.4.

Por que a remoção quebra coisas

Um módulo raramente vive só na própria pasta. Com o tempo ele escreve em tabelas compartilhadas:

  • Atributos EAV cujo source_model, backend_model ou frontend_model aponta para uma classe do módulo. Quando a classe some, carregar ou salvar produtos, categorias ou clientes com esse atributo falha.
  • Instâncias de widget cujo tipo é uma classe de block do módulo.
  • Páginas e blocks CMS com diretivas de widget que referenciam os blocks do módulo.
  • Pedidos pagos com uma forma de pagamento do módulo. A tela do pedido no admin precisa da classe da forma de pagamento para exibir as informações de pagamento.
  • Layout XML no seu tema que referencia blocks ou templates do módulo.
  • Outros módulos que o declaram no sequence, o exigem no composer.json ou adicionam plugins às classes dele.

O processo abaixo encontra cada um desses antes de o código ir embora.

Passo 1: ache as dependências de código

grep -rn "Vendor_Module" app/code app/design app/etc/config.php vendor/*/*/etc/module.xml
grep -rn "Vendor\\\\Module\\\\" app/code app/design --include=*.php --include=*.xml --include=*.phtml
composer why vendor/module-name

A primeira busca encontra sequences no module.xml e referências em layout pelo nome do módulo. A segunda encontra referências a classes PHP, incluindo plugins e preferences no di.xml. O composer why mostra quais pacotes o exigem. Qualquer resultado precisa ser resolvido antes, removendo aquela dependência ou mantendo o módulo.

Passo 2: ache as dependências de dados

Procure referências às classes do módulo no banco. O curinga % também casa com as barras invertidas dos nomes de classe, o que evita problemas de escape:

SELECT attribute_id, entity_type_id, attribute_code, source_model, backend_model, frontend_model
FROM eav_attribute
WHERE source_model LIKE 'Vendor%Module%'
   OR backend_model LIKE 'Vendor%Module%'
   OR frontend_model LIKE 'Vendor%Module%';

SELECT instance_id, instance_type, title
FROM widget_instance
WHERE instance_type LIKE 'Vendor%Module%';

SELECT block_id, identifier FROM cms_block WHERE content LIKE '%Vendor%Module%';
SELECT page_id, identifier FROM cms_page WHERE content LIKE '%Vendor%Module%';

SELECT method, COUNT(*) FROM sales_order_payment GROUP BY method;

Confira também o catalog_eav_attribute.frontend_input_renderer dos atributos de produto e a configuração do próprio módulo na seção dele em core_config_data. A última query lista os códigos de forma de pagamento nos pedidos. Se o módulo fornece uma forma de pagamento usada em pedidos antigos, removê-lo afeta como esses pedidos aparecem. Nesse caso, mantenha uma versão mínima da classe da forma de pagamento, ou aceite a limitação sabendo disso.

Passo 3: desative primeiro, remova depois

Desativar é reversível. Remover não é. Faça o release da desativação sozinho e deixe rodando em produção por um tempo:

bin/magento module:disable Vendor_Module
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento setup:static-content:deploy -f
bin/magento cache:flush

Isso grava 'Vendor_Module' => 0 no app/etc/config.php, que você commita e faz deploy como qualquer outra mudança. As classes de um módulo desativado ainda estão no sistema de arquivos, então o autoload continua funcionando para os models do EAV. Isso significa que alguns problemas de dados só aparecem depois que o código some. Teste com o código fisicamente removido em ambiente local antes do release final.

Passo 4: limpe os dados

Escreva a limpeza como um data patch num módulo do projeto, para que rode do mesmo jeito em todos os ambientes. Para um atributo que você não precisa mais, remova. Para um que você quer manter com os valores, volte o model para uma classe do core ou para null:

<?php

declare(strict_types=1);

namespace Acme\Cleanup\Setup\Patch\Data;

use Magento\Catalog\Model\Product;
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\Patch\DataPatchInterface;

class RemoveVendorModuleAttributes implements DataPatchInterface
{
    public function __construct(
        private readonly ModuleDataSetupInterface $moduleDataSetup,
        private readonly EavSetupFactory $eavSetupFactory
    ) {
    }

    public function apply(): self
    {
        $eavSetup = $this->eavSetupFactory->create(['setup' => $this->moduleDataSetup]);

        $eavSetup->removeAttribute(Product::ENTITY, 'vendor_badge');
        $eavSetup->updateAttribute(Product::ENTITY, 'vendor_color_group', 'source_model', null);

        return $this;
    }

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

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

O mesmo patch pode apagar instâncias de widget e atualizar conteúdo CMS, ou você pode fazer isso no admin se forem poucos. Mais sobre como escrever patches no guia de declarative schema e data patches.

Passo 5: remova o código

Para um pacote Composer:

composer remove vendor/module-name

O Magento também oferece o bin/magento module:uninstall para módulos instalados via Composer. Ele remove o pacote e, com --remove-data, roda a lógica de uninstall do módulo para limpar o banco, se o módulo tiver isso. Muitos módulos de terceiros não têm, então não conte com isso, e nunca rode com --remove-data sem um backup recente.

Para um módulo em app/code, apague a pasta com o Git para que a remoção faça parte do commit:

git rm -r app/code/Vendor/Module

Depois remova a linha do módulo no app/etc/config.php e a linha dele na tabela de registro de módulos:

DELETE FROM setup_module WHERE module = 'Vendor_Module';

A tabela patch_list guarda os nomes de classe dos patches que rodaram. Linhas sobrando ali não fazem mal, então limpá-las é opcional.

Passo 6: apague as tabelas

O Magento não apaga as tabelas de um módulo cujo código sumiu. O db_schema.xml e a whitelist vão embora junto, então nada avisa o setup:upgrade de que essas tabelas existem. Encontre-as pelo prefixo, confira que nenhum outro módulo as usa, faça um backup e então apague:

SELECT TABLE_NAME, TABLE_ROWS
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME LIKE 'vendor\_module\_%';

Se o módulo guardou dados que você pode precisar depois, como informações em nível de pedido, exporte antes de apagar. Deixar tabelas sem uso no lugar não faz mal, então não há pressa nesse passo.

Passo 7: reconstrua e teste

bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento setup:static-content:deploy -f
bin/magento indexer:reindex
bin/magento cache:flush

Teste salvar produto no admin para produtos que tinham atributos do módulo, o grid de produtos, páginas de categoria, a tela de pedidos antigos, páginas CMS que usavam os widgets dele e o checkout. Confira o var/log/exception.log em busca de erros de classe não encontrada.

Plano de release entre ambientes

Trate a remoção como dois releases, não um. O primeiro release desativa o módulo e leva o data patch que limpa atributos, widgets e conteúdo. Ele passa por local, staging e produção do jeito normal, e você acompanha os logs por alguns dias. O segundo release remove o código, a entrada do config.php e a linha do setup_module. Apagar as tabelas pode esperar uma terceira mudança, menor, quando todo mundo concordar que os dados não são mais necessários.

Guarde o backup feito antes do segundo release por mais tempo que o normal. Se alguém descobrir meses depois que um relatório dependia da tabela do módulo, você restaura aquela tabela do dump sem mexer no resto da loja.

Anote o que você removeu e por quê, no repositório ou na wiki do projeto. O próximo dev que encontrar um atributo órfão ou uma tabela antiga vai saber de onde veio.

Se você está removendo vários módulos depois de uma auditoria, faça um ou dois por vez, para que um problema aponte para um único módulo. A auditoria em si está descrita no guia de auditoria de extensões, e remover módulos sem uso antes de uma atualização de versão deixa o upgrade menor.

FAQ

Apagar a pasta do módulo basta para remover uma extensão do Magento?

Não. Atributos, widgets e conteúdo CMS ainda podem referenciar as classes dele, e a loja quebra quando eles carregam. Limpe os dados primeiro e remova o módulo do config.php e do setup_module.

O module:uninstall funciona para módulos em app/code?

Ele foi feito para módulos instalados com Composer. Para módulos em app/code, desative o módulo, limpe os dados, apague a pasta e remova as entradas dele do config.php e do setup_module.

O Magento apaga as tabelas de um módulo removido?

Não. As tabelas ficam no banco. Faça backup, confirme que nada mais as usa e apague você mesmo.

Como corrijo um erro de class does not exist depois de remover um módulo?

Procure o nome da classe que falta em eav_attribute, widget_instance e no conteúdo CMS, e atualize ou remova essas referências. Depois faça flush do cache.

Atendo como freelancer em Magento e Adobe Commerce, incluindo limpeza de extensões. Se precisa de ajuda com isso, fale comigo.

Artigos relacionados