How to create Product attribute programmatically in Magento 2?

Up to Magento 2.2 Version, We can create product attributes programmatically using the InstallData.php file.

Using dynamically create product attribute save the time for creating attribute manually using the admin panel.

If you are creating an extension package and you need product attribute at that time below code snippet will be helpful for creating Product attribute.

We can create product attribute manually using Stores -> Attributes -> Product -> Add New Attribute.

To create product attribute, We need to create a simple module for that,

You need to create the first registration.php and module.xml file for defining our module. Here I have used Rbj as Packagename where ProductAttribute is a module name.

Path: app/code/Rbj/ProductAttribute/registration.php

<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Rbj_ProductAttribute',
    __DIR__
);

Create module.xml file, Path: app/code/Rbj/ProductAttribute/etc/module.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Rbj_ProductAttribute" setup_version="1.0.0">
    </module>
</config>

Create InstallData.php file for install our custom product attribute in Magento instance.

We are creating two custom product attribute, first select option, custom attribute and other one is the text field.
Path: app/code/Rbj/ProductAttribute/Setup/InstallData.php

<?php
namespace Rbj\ProductAttribute\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;
use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface;

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->removeAttribute(\Magento\Catalog\Model\Product::ENTITY, 'product_select_attribute');
        $eavSetup->removeAttribute(\Magento\Catalog\Model\Product::ENTITY, 'product_custom_attribute');

        $statusOptions = 'Rbj\ProductAttribute\Model\Config\Source\StatusOptions';
        $eavSetup->addAttribute(
            \Magento\Catalog\Model\Product::ENTITY,
            'product_select_attribute',
            [
                'group' => 'Custom Product Attribute',
                'type' => 'int',
                'backend' => '',
                'frontend' => '',
                'label' => 'Product Status',
                'input' => 'select',
                'class' => '',
                'source' => $statusOptions,
                'global' => ScopedAttributeInterface::SCOPE_GLOBAL,
                'visible' => true,
                'required' => false,
                'user_defined' => false,
                'default' => '',
                'searchable' => false,
                'filterable' => false,
                'comparable' => false,
                'is_used_in_grid' => true,
                'visible_on_front' => false,
                'used_in_product_listing' => true,
                'unique' => false
            ]
        );

        /**
         * Add attributes to the eav_attribute
         */
        $eavSetup->addAttribute(
            \Magento\Catalog\Model\Product::ENTITY,
            'product_custom_attribute',
            [
                'group'        => 'Custom Product Attribute',
                'type'         => 'varchar',
                'backend'      => '',
                'frontend'     => '',
                'label'        => 'Product attribute Value',
                'input'        => 'text',
                'frontend_class' => 'required-entry',
                'global'       => ScopedAttributeInterface::SCOPE_GLOBAL,
                'visible'      => true,
                'required'     => false,
                'user_defined' => false,
                'default'      => '',
                'searchable'   => false,
                'filterable'   => false,
                'comparable'   => false,
                'unique'       => false,
                'visible_on_front'        => false,
                'used_in_product_listing' => true
            ]
        );
    }
}

We have used Custom Product Attribute as a new Group on the product page. In the Custom Product Attribute tab section our new product attribute will display.

For Select option, we need to take type as int and for a text field, we need to take a type as varchar. All other fields are self-explanatory in the above code snippet.

For select option, We need to create Model file for define our custom option of select box,
$statusOptions = ‘Rbj\ProductAttribute\Model\Config\Source\StatusOptions’;
We have create StatusOptions.php file under Model and define our enable and disable option value.

Create StatusOptions.php file,
Path: app/code/Rbj/ProductAttribute/Model/Config/Source/StatusOptions.php

<?php
namespace Rbj\ProductAttribute\Model\Config\Source;

use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource;

class StatusOptions extends AbstractSource
{
    /**
     * Get all options
     *
     * @return array
     */
    public function getAllOptions()
    {
        if (null === $this->_options) {
            $this->_options=[
                                ['label' => __('Enable'), 'value' => 1],
                                ['label' => __('Disable'), 'value' => 0]
                            ];
        }
        return $this->_options;
    }
}

Using Command-line, Go to Magento instance where you have installed Magento, Open Command Line,

php bin/magento setup:upgrade
php bin/magento cache:flush
php bin/magento indexer:reindex
custom product attibute in Magento 2
custom product attribute in Magento 2

For Create Category Attribute, Category Attribute programmatically in Magento 2