Adding an attribute programmatically in Magento 2

Upon creating an extension for Magento, quite often you face with a necessity to create a custom attribute for product, category, customer etc. Of course, you can add a description somewhere in the extension’s documentation how to do it and do not bother you head. However, all Magento versions allow you to create an attribute on the fly during an extension’s installation. There were many topics how to do that in Magento 1.x, and let’s check what is the difference in the next major version of Magento.

In a nutshell, the process is almost the same. The major difference lays in the extension’s structure since Magento 2 has more ‘developer-fiendly’ architecture and provides much more ways to interact with the core avoiding different types of conflicts.

There are no code pools in Magento 2, so, no more disputes about the correct place where to put your code. The main extension’s files should be placed into the following path:

app/code/[VendorName]/[ExtensionName]

As you may remember, in  Magento 1.x we have app/code/[CodePool]/[VendorName]/[ExtensionName]. Basing on this, let’s create an extension in this directory:

app/code/Atwix/TestAttribute/

And to define our new module, we need to create a file called module.xml in the app/code/Atwix/TestAttribute/ directory with the following content:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Module/etc/module.xsd">
    <module name="Atwix_TestAttribute" setup_version="1.0.0">
        <sequence>
            <module name="Magento_Catalog"/>
        </sequence>
    </module>
</config>

As you can see, XML files in Magento 2 have schema definitions that allow to validate your XML in different places. The <sequence> node describes dependencies of your extension. It’s very similar to the <depends> node in Magento 1.x.
On the next step, we need to create the data setup script with parameters of the new attribute:

<?php
/* app/code/Atwix/TestAttribute/Setup/InstallData.php */

namespace Atwix\TestAttribute\Setup;

use Magento\Eav\Setup\EavSetup;
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\Setup\InstallDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;

/**
 * @codeCoverageIgnore
 */
class InstallData implements InstallDataInterface
{
    /**
     * EAV setup factory
     *
     * @var EavSetupFactory
     */
    private $eavSetupFactory;

    /**
     * Init
     *
     * @param EavSetupFactory $eavSetupFactory
     */
    public function __construct(EavSetupFactory $eavSetupFactory)
    {
        $this->eavSetupFactory = $eavSetupFactory;
    }

    /**
     * {@inheritdoc}
     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
     */
    public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
    {
        /** @var EavSetup $eavSetup */
        $eavSetup = $this->eavSetupFactory->create(['setup' => $setup]);

        /**
         * Add attributes to the eav/attribute
         */

        $eavSetup->addAttribute(
            \Magento\Catalog\Model\Product::ENTITY,
            'test_attribute',
            [
                'type' => 'int',
                'backend' => '',
                'frontend' => '',
                'label' => 'Test Attribute',
                'input' => '',
                'class' => '',
                'source' => '',
                'global' => \Magento\Catalog\Model\Resource\Eav\Attribute::SCOPE_GLOBAL,
                'visible' => true,
                'required' => false,
                'user_defined' => false,
                'default' => 0,
                'searchable' => false,
                'filterable' => false,
                'comparable' => false,
                'visible_on_front' => false,
                'used_in_product_listing' => true,
                'unique' => false,
                'apply_to' => ''
            ]
        );
    }
}

Well then, we have described all parameters of the new attribute, and now we should tell Magento that we have some files with the installation instructions that need to be executed. In the early versions of Magento 2 you had to define your install scripts additionally in the Dependancy Injection configuration. Fortunately, you don’t have to do that anymore.

Now, it’s time to enable our extension. If you use Magento 2  0.74.0-beta4 or later, you can use CLI command for this purpose. Go to the [magento_root]/bin directory and run the following command:

php magento module:enable Atwix_TestAttribute

Now, we need to clean the cache (the easiest way is to remove [magento_root]/var/cache and [magento_root]/var/page_cache directories) and check that everything goes right. Go to the [magento_root]/bin directory and run the following command:

php magento setup:db:status

You should see the following message:

The module code base doesn't match the DB schema and data.
 Atwix_TestAttribute     schema:        none  ->  1.0.0
 Atwix_TestAttribute       data:        none  ->  1.0.0
Run 'setup:upgrade' to update your DB schema and data.

Well then, the system says what to do next, quite handy. So, we need to synchronise the database with our module’s setup version. On this step our data setup script for adding an attribute will be executed. Run this command to execute the synchronisation process:

php magento setup:upgrade

 

That’s it, if you have done everything correctly – you should have the new attribute in the system. If it’s easier to you – you can get the described extension here https://github.com/Atwix/Mage2TestAttribute and try to install on your system. Feel free to ask questions and share your findings as well. Thank you!

You may also want to read: