Setup scripts in Magento 2

setup_scripts-featured

Today we’ll cover how to manually enable or disable module in Magento 2 and which module setup scripts are executed in the process.

To enable custom module manually in Magento 2, two commands needs to be executed from the shell:

php bin/magento module:enable Inchoo_Custom
php bin/magento setup:upgrade

First command adds your module to the modules list in app/etc/config.php (and crashes M2 until you run second command 😛 ).

Second one really installs your module. Well, it will basically trigger setup scripts of all Magento modules that needs setup, but if you just enabled one new module nothing else will execute except that one.

If module has setup scripts, they are executed and current module version is saved in setup_module table, which is equivalent of core_resource in Magento 1. Magento regulates what needs setup by comparing module versions with what it sees in that table.

If module is installing for the first time, Install + Upgrade scripts are triggered. If module was installed before, but module version is increased, only Upgrade scripts are triggered.

To disable module, you will run:

php bin/magento module:disable Inchoo_Custom

This will flag module as disabled in config.php.

Another interesting command is:

php bin/magento module:uninstall Inchoo_Custom

This one is something new, module uninstall script is executed and whole module gets deleted afterwards. Only modules installed through Composer can be uninstalled.

To simplify, in Magento 2 you can have 6 different Setup classes in your module:

  • Setup/InstallSchema
  • Setup/UpgradeSchema
  • Setup/InstallData
  • Setup/UpgradeData
  • Setup/Recurring
  • Setup/Uninstall

There are no separate version setup files anymore, only one class per action. No more:

install-1.0.0.php
upgrade-1.0.0-1.0.1.php

upgrade-1.0.1-1.0.3.php

For the reference, all mentioned installer logic is located at /setup/src/Magento/Setup/.

Install or Upgrade schema

Schema setup scripts change database schema, they create or change needed database tables. If module is installing, Setup\InstallSchema::install() is executed.

namespace Inchoo\Custom\Setup;
 
use Magento\Framework\Setup\InstallSchemaInterface;
use Magento\Framework\Setup\SchemaSetupInterface;
use Magento\Framework\Setup\ModuleContextInterface;
 
class InstallSchema implements InstallSchemaInterface
{
    public function install(SchemaSetupInterface $setup, ModuleContextInterface $context)
    {
        $setup->startSetup();
 
        $table = $setup->getConnection()->newTable(
            $setup->getTable('inchoo_custom')
        )->addColumn(
            'custom_id',
            \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
            null,
            ['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true],
            'Custom Id'
        )->addColumn(
            'name',
            \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
            255,
            [],
            'Custom Name'
        )->setComment(
            'Custom Table'
        );
        $setup->getConnection()->createTable($table);
 
        $setup->endSetup();
}

If module is installing or upgrading, Setup\UpgradeSchema::upgrade() is executed. Since there are no separate upgrade scripts for specific upgrade versions, your UpgradeSchema class needs to handle all possible version scenarios. As mentioned, this is quite different then in M1.

namespace Inchoo\Custom\Setup;
 
use Magento\Framework\Setup\UpgradeSchemaInterface;
use Magento\Framework\Setup\SchemaSetupInterface;
use Magento\Framework\Setup\ModuleContextInterface;
 
class UpgradeSchema implements UpgradeSchemaInterface
{
    public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $context)
    {
        $setup->startSetup();
 
        //handle all possible upgrade versions
 
        if(!$context->getVersion()) {
            //no previous version found, installation, InstallSchema was just executed
            //be careful, since everything below is true for installation !
        }
 
        if (version_compare($context->getVersion(), '1.0.1') < 0) {
            //code to upgrade to 1.0.1
        }
 
        if (version_compare($context->getVersion(), '1.0.3') < 0) {
            //code to upgrade to 1.0.3
        }
 
        $setup->endSetup();
    }
}

For additional examples just check /Setup/ folder of almost any Magento module.

Install or Upgrade data

Data setup scripts contain entries module needs to insert into database. Attributes that come with Magento by default, 404 and other Cms pages, various default groups and roles, are all examples of data setup.

Data setup is executed after Schema setup, they function in a similar fashion.

Recurring (post install)

Recurring script is executed after any module setup. The idea is that Module1 can do something after Module2 and Module3 are installed, if needed.

The only example in current Magento 2 is Magento\Indexer\Setup\Recurring class where Magento_Indexer module checks for new defined indexers and adds them to indexer_state table.

Uninstall

Magento 2 modules can have Uninstall script !! The purpose of Uninstall script is to remove all module tables, columns, data from database, like it was never installed in the first place.

Since disabling and uninstalling are 2 different actions, I hope all M2 extensions will have uninstall script, it should be best practice to have them. This would simplify database cleanup if it’s decided that some extensions are not needed anymore.

Currently there are no Uninstall scripts in core modules to check, but the pattern is the same:

namespace Inchoo\SetupTest\Setup;
 
use Magento\Framework\Setup\UninstallInterface;
use Magento\Framework\Setup\SchemaSetupInterface;
use Magento\Framework\Setup\ModuleContextInterface;
 
class Uninstall implements UninstallInterface
{
    public function uninstall(SchemaSetupInterface $setup, ModuleContextInterface $context)
    {
        $setup->startSetup();
 
        //uninstall code; Little Bobby Tables we call him ..
 
        $setup->endSetup();
    }
}

The only thing I don’t understand is why o why this works only for modules installed with Composer.

Additional

One more thing we’re usually adding through setups are EAV attributes. For them you use functionality of \Magento\Eav\Setup\EavSetup injected in your InstallData.

namespace Inchoo\Custom\Setup;
 
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\Setup\InstallDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
 
class InstallData implements InstallDataInterface
{
    /**
     * EAV setup factory
     *
     * @var EavSetupFactory
     */
    private $eavSetupFactory;
 
    /**
     * Init
     *
     * @param EavSetupFactory $eavSetupFactory
     */
    public function __construct(EavSetupFactory $eavSetupFactory)
    {
        $this->eavSetupFactory = $eavSetupFactory;
    }
 
    /**
     * {@inheritdoc}
     */
    public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
    {
        $setup->startSetup();
 
        /** @var \Magento\Eav\Setup\EavSetup $eavSetup */
        $eavSetup = $this->eavSetupFactory->create(['setup' => $setup]);
 
        $eavSetup->addAttribute(
            \Magento\Catalog\Model\Category::ENTITY,
            'inchoo_custom',
            [
                'type' => 'varchar',
                'input' => 'text',
                'label' => 'Inchoo Custom',
                'required' => false,
                'user_defined' => true,
                'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE,
                //...
                'group' => 'Inchoo Custom',
            ]
        );
 
        $setup->endSetup();
    }
}

Best to check Catalog and Customer InstallData for the reference.

So long, and thanks for all the fish.


About Ivan Weiler

Technical Educator and Consultant

Ivan is a Technical Educator and Consultant. He gained lots of experience managing some of the most complex Magento projects we had at Inchoo.

Read more posts by Ivan / Visit Ivan's profile

10 comments

  1. I am getting this error after running installation ….could u pl help ….Thanks in advance

    createApplication(‘Magento\Framework\App\Http’); * $bootstrap->run($app); * ——————————————– * * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ try { require __DIR__ . ‘/app/bootstrap.php’; } catch (\Exception $e) { echo <getMessage()}
    HTML; exit(1); } $bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER); /** @var \Magento\Framework\App\Http $app */ $app = $bootstrap->createApplication(‘Magento\Framework\App\Http’); $bootstrap->run($app)

  2. Following my experience, it seems that the EAV part does not work on the installation of Magento (bin/magento setup:install) due to the fact that the entity “category” is not created yet. Did someone faced the problem as well ?

  3. Is there a way to combine the creation of custom customer attributes and custom product attributes into the same installData.php?

  4. Can you help me to create a custom form in frontend like: Name, Age, Gender, Address and when the users click the submit button, all data will be save in your custom table

  5. That sucks!!! not your post which is awesome, the “one class” approach for upgrades force devs to add @SuppressWarnings(PHPMD.ExcessiveMethodLength) at the upgrade method and deal with a giant chain of “if” statements, plus you can easily introduce a bug in a working module since all version upgrades are inside the same method. I really miss the magento1 approach.

    Again Nice Post @ivan!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <blockquote cite=""> <code> <del datetime=""> <em> <s> <strike> <strong>. You may use following syntax for source code: <pre><code>$current = "Inchoo";</code></pre>.