One of the shortcomings of Magento’s first version was the inability to clean up module data from the database upon its removal. This is a common situation when you uninstall an extension but all the related data remains in the database. You can only get rid of it manually. It is inconvenient especially if the module has created a bunch of new tables, custom attributes, system configurations etc. In such cases, an automatic data removal tool would be highly beneficial.
In Magento 2 there is a great feature, which allows to create an uninstall script for your module. Let’s find out how it works.
Overview
Each module that was installed via composer can be easily uninstalled using the following command:
bin/magento module:uninstall [--backup-code] [--backup-media] [--backup-db] [-r|--remove-data] [-c|--clear-static-content] {ModuleName}
In general, the module uninstall command performs four main tasks:
- Removes module’s code from the codebase using a composer remove;
- Removes the specified module from the module list;
- Removes the specified module from the
setup_module
database table; - Invokes the uninstall method in its Uninstall class.
If you check the logic in Magento\Setup\Model\UninstallCollector::collectUninstall
method, which is used to initialize the uninstall classes, you will notice that the following namespace and class naming convention of uninstall classes is required for each module.
{VendorName}\{ModuleName}\Setup\Uninstall
Another important thing is that each uninstall class must implement Magento\Framework\Setup\UninstallInterface
interface.
/** * Collect Uninstall classes from modules * * @param array $filterModules * @return UninstallInterface[] */ public function collectUninstall($filterModules = []) { $uninstallList = []; /** @var \Magento\Setup\Module\DataSetup $setup */ $setup = $this->dataSetupFactory->create(); $result = $setup->getConnection()->select()->from($setup->getTable('setup_module'), ['module']); if (isset($filterModules) && sizeof($filterModules) > 0) { $result->where('module in( ? )', implode(',', $filterModules)); } // go through modules foreach ($setup->getConnection()->fetchAll($result) as $row) { $uninstallClassName = str_replace('_', '\\', $row['module']) . '\Setup\Uninstall'; if (class_exists($uninstallClassName)) { $uninstallClass = $this->objectManager->create($uninstallClassName); if (is_subclass_of($uninstallClass, 'Magento\Framework\Setup\UninstallInterface')) { $uninstallList[$row['module']] = $uninstallClass; } } } return $uninstallList; }
If the uninstall class can be instantiated, it will be executed by Magento\Setup\Model\ModuleUninstaller::uninstallData
method. Otherwise, if the setup uninstall class can not be found or does not match the requirements, the module will be removed without a database clean up.
Try it out
Now, we are able to create our simple uninstall script. Let’s say we have a small “Atwix_SampleSetup” module, which adds a new custom table named atwix_sample
during the installation. It means that the atwix_sample
table should be also removed when the module is uninstalled. This issue can be solved by the following Uninstall script:
<?php /** * @author Atwix Team * @copyright Copyright (c) 2016 Atwix (https://www.atwix.com/) * @package Atwix_SampleSetup */ namespace Atwix\SampleSetup\Setup; use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\Db\Select; use Magento\Framework\Setup\ModuleContextInterface; use Magento\Framework\Setup\SchemaSetupInterface; use Magento\Framework\Setup\UninstallInterface as UninstallInterface; /** * Class Uninstall */ class Uninstall implements UninstallInterface { /** * Atwix Sample Table Name */ const SAMPLE_TABLE_NAME = 'atwix_sample'; /** * Invoked when remove-data flag is set during module uninstall * * @param SchemaSetupInterface $setup * @param ModuleContextInterface $context * * @return void */ public function uninstall(SchemaSetupInterface $setup, ModuleContextInterface $context) { $installer = $setup; $installer->startSetup(); /** @var AdapterInterface $connection */ $connection = $installer->getConnection(); $connection->dropTable(self::SAMPLE_TABLE_NAME); $installer->endSetup(); } }
As you see the implementation is pretty simple and very similar to well-known install and upgrade scripts.
How to test?
Start by installing the module via composer in order to make the uninstallation possible. Our test module is stored in Atwix_SampleSetup GIT repository. We will install it using VSC from the master branch.
Add the module’s dependency into require
section.
"atwix/sample-setup": "dev-master"
Define the module’s repository within the repositories
section.
"repositories": [ { "type": "git", "url": "https://github.com/dmytro-ch/m2-sample-setup.git" } ]
Perform the package installation.
composer update
Enable the module.
bin/magento module:enable Atwix_SampleSetup
Upgrade the database.
bin/magento setup:upgrade
The module has been successfully installed. Now we can run the uninstall script.
bin/magento module:uninstall Atwix_SampleSetup
As a result of execution a success message will be shown.
You are about to remove code and/or database tables. Are you sure?[y/N]y Enabling maintenance mode You are about to remove a module(s) that might have database data. Do you want to remove the data from database?[y/N]y You are removing data without a database backup. Removing data of Atwix_SampleSetup Removing Atwix_SampleSetup from module registry in database Removing Atwix_SampleSetup from module list in deployment configuration Removing code from Magento codebase: Cache cleared successfully. Generated classes cleared successfully. Please run the 'setup:di:compile' command to generate classes. Info: Some modules might require static view files to be cleared. To do this, run 'module:uninstall' with the --clear-static-content option to clear them. Disabling maintenance mode
The module has been successfully uninstalled. And that’s it!
Read more: