/   /   /  OroCommerce add custom product attribute programmatically

Note:

For more extensions and themes visit our store

OroCommerce add custom product attribute programmatically


OroPlatform come with many entities type some of it  =  configurable entity and extendable entity 

Configurable Entity:

The first give you the ability to add custom oro configuration to your entity for example to manage it from administration By UI , So Your entity will support some new annotation : @Config and @ConfigField.

Example of @Config for Product Entity:

  1.  
  2.  * @Config(
  3.  *      routeName="oro_product_index",
  4.  *      routeView="oro_product_view",
  5.  *      routeUpdate="oro_product_update",
  6.  *      defaultValues={
  7.  *          "entity"={
  8.  *              "icon"="fa-briefcase"
  9.  *          },
  10.  *          "ownership"={
  11.  *              "owner_type"="BUSINESS_UNIT",
  12.  *              "owner_field_name"="owner",
  13.  *              "owner_column_name"="business_unit_owner_id",
  14.  *              "organization_field_name"="organization",
  15.  *              "organization_column_name"="organization_id"
  16.  *          },
  17.  *          "dataaudit"={
  18.  *              "auditable"=true
  19.  *          },
  20.  *          "security"={
  21.  *              "type"="ACL",
  22.  *              "group_name"="",
  23.  *              "category"="catalog"
  24.  *          },
  25.  *          "form"={
  26.  *              "form_type"="Oro\Bundle\ProductBundle\Form\Type\ProductSelectType",
  27.  *              "grid_name"="products-select-grid"
  28.  *          },
  29.  *          "attribute"={
  30.  *              "has_attributes"=true
  31.  *          }
  32.  *      }
  33.  * )
  34.  

Many configuration here well get used by oroplatform to define some level of security or for audit or from example to add an icon  which used by UI as icon for this entity = contact with the name of the entity here "icon"="fa-briefcase" , other important thing is ths product entity is configuration:
  1.  
  2.  *          "attribute"={
  3.  *              "has_attributes"=true
  4.  *          }
  5.  

So our entity will not accept just a custom field but will accept a custom attribute with group and family .
Example of @ConfigField for Product Entity:
  1.  
  2.     /**
  3.      * @var string
  4.      *
  5.      * @ORM\Column(name="type", type="string", length=32, nullable=false)
  6.      * @ConfigField(
  7.      *      defaultValues={
  8.      *          "dataaudit"={
  9.      *              "auditable"=true
  10.      *          },
  11.      *          "importexport"={
  12.      *              "order"=20
  13.      *          }
  14.      *      }
  15.      *  )
  16.      */
  17.     protected $type = self::TYPE_SIMPLE;
  18.  

So is auditable With this functionality, users can see the full history of changes made to any record of an auditable entity, as well as the out-of-the-box report of all such actions. 
And  is supported by importexport .

Extendable Entity:

So Is an entity which give you the ability to add custom field to it , by simple type or complex relation like ManyToMany or …. how you can make your entity Extendable? -is by extend a parent model like entity product :
class Product extends ExtendProduct
for example the content of ExtendProduct Model:

  1.  
  2. namespace Oro\Bundle\ProductBundle\Model;
  3. use Oro\Bundle\EntityBundle\Entity\EntityFieldFallbackValue;
  4. use Oro\Bundle\EntityExtendBundle\Entity\AbstractEnumValue;
  5. use Oro\Bundle\LocaleBundle\Entity\Localization;
  6. use Oro\Bundle\LocaleBundle\Entity\LocalizedFallbackValue;
  7. use Oro\Bundle\ProductBundle\Entity\Product;
  8. class ExtendProduct
  9. {
  10.     /**
  11.      * Constructor
  12.      *
  13.      * The real implementation of this method is auto generated.
  14.      *
  15.      * IMPORTANT: If the derived class has own constructor it must call parent constructor.
  16.      */
  17.     public function __construct()
  18.     {
  19.     }
  20. }
  21.  
 

So the entity product is a configurable and extendable one , you can add a custom field as product attributes .
Add Product Attribute by Migration:

Add custom product attribute:

Inside you bundle add Migrations add folder Schema for example inside the last folder add v1_0 folder and inside add AddCustomsField.php the full path Migrations/Schema/v1_0/ AddCustomsField.php you can fill with :

  1.  
  2. <?php
  3. namespace Ibnab\Bundle\CustomBundle\Migrations\Schema\v1_0;
  4. use Doctrine\DBAL\Schema\Schema;
  5. use Oro\Bundle\MigrationBundle\Migration\Migration;
  6. use Oro\Bundle\MigrationBundle\Migration\QueryBag;
  7. use Oro\Bundle\ProductBundle\Entity\Product;
  8. use Oro\Bundle\EntityExtendBundle\EntityConfig\ExtendScope;
  9. class AddCustomsField implements Migration
  10. {
  11.     const PRODUCT_TABLE_NAME = 'oro_product';
  12.     /**
  13.      * {@inheritdoc}
  14.      */
  15.     public function up(Schema $schema, QueryBag $queries)
  16.     {
  17.         $table = $schema->getTable(self::PRODUCT_TABLE_NAME);
  18.         $table->addColumn('customNumber', 'string',['length' => 20,'notnull' => false,                         
  19.                          'oro_options' => [
  20.                             'extend'    => [
  21.                                 'is_extend' => true,
  22.                                 'owner'     => ExtendScope::OWNER_SYSTEM
  23.                             ],
  24.                             'attribute' => [
  25.                                 'is_attribute' => true,
  26.                                 'enabled'      => true,
  27.                                 'visible'      => true,
  28.                                 'label'         => 'Customs Number',
  29.                                 'description'   => 'Customs Number'
  30.                             ],
  31.                             'entity' => [
  32.                                 'label'         => 'Customs Number',
  33.                                 'description'   => 'Customs Number'
  34.                             ]
  35.                             ]]);
  36.     }
  37.     public function getOrder()
  38.     {
  39.         return 90;
  40.     }
  41. }
  42.  

Here we have added a field to product entity , and with  'oro_options' , we defined this field as attribute  - enable and visible - and give them a label and description , because the product is extendable entity your field well get added by extend technique . Tape on terminal to load migrations:

  1.  
  2. php bin/console oro:migration:load --env prod
  3.  

If you go now to DB and open the oro_product you will find your field,  As is extendable entity Oro well add in the cache go to  var/cache/prod/oro_entities/Extend/Entity/EX_OroProductBundle_Product.php visit it to explore the entity result .

So now it’s the time to make or update the configuration of attributes inside your bundle /Migrations/Data/ORM create  UpdateAttributesConfig.php and file with:

  1.  
  2. <?php
  3. namespace Ibnab\Bundle\CustomBundle\Migrations\Data\ORM;
  4. use Oro\Bundle\ProductBundle\Migrations\Data\ORM\MakeProductAttributesTrait;
  5. use Doctrine\Common\DataFixtures\AbstractFixture;
  6. use Doctrine\Common\Persistence\ObjectManager;
  7. use Symfony\Component\DependencyInjection\ContainerAwareInterface;
  8. class UpdateAttributesConfig extends AbstractFixture implements ContainerAwareInterface
  9. {
  10.     use MakeProductAttributesTrait;
  11.     /**
  12.      * @var array
  13.      */
  14.     private $fields = [
  15.         'customNumber' => [
  16.             'searchable' => true,
  17.             'filterable' => true,
  18.             'filter_by' => 'exact_value'
  19.         ]
  20.     ];
  21.     /**
  22.      * {@inheritdoc}
  23.      */
  24.     public function load(ObjectManager $manager)
  25.     {
  26.        // make new field as attribute
  27.         $this->makeProductAttributes($this->fields);
  28.        // update existing attribute
  29.         $this->updateProductAttributes($this->fields);
  30.     }
  31. }
  32.  

We will make our attribute searchable and filterable , So the function  makeProductAttributes and updateProductAttributes is inside class  Oro\Bundle\ProductBundle\Migrations\Data\ORM\MakeProductAttributesTrait and the content of function is :
  1.  
  2.     private function makeProductAttributes(array $fields, $owner = ExtendScope::ORIGIN_SYSTEM)
  3.     {
  4.         $configManager = $this->getConfigManager();
  5.         $configHelper = $this->container->get('oro_entity_config.config.config_helper');
  6.         $entityManager = $configManager->getEntityManager();
  7.         foreach ($fields as $field => $attributeOptions) {
  8.             $fieldConfigModel = $configManager->getConfigFieldModel(Product::class, $field);
  9.             $options = [
  10.                 'attribute' => array_merge([
  11.                     'is_attribute' => true,
  12.                 ], $attributeOptions),
  13.                 'extend' => [
  14.                     'owner' => $owner
  15.                 ]
  16.             ];
  17.             $configHelper->updateFieldConfigs($fieldConfigModel, $options);
  18.             $entityManager->persist($fieldConfigModel);
  19.         }
  20.         $entityManager->flush();
  21.     }
  22.  

We have just update the field  to attribute with new configuration your can update the existing attribute too with $this->updateProductAttributes($this->fields); .
and because the Product Entity is configurable one and you have add a configurable field go to table oro_entity_config_field you will find , or go to  oro_entity_config table to explore full serialized configuration inside data field .

How to add the attribute to family and attribute group:

inside you bundle in /Migrations/Data/ORM add LoadProductNewAttributesData.php and fill:

  1.  
  2. <?php
  3. namespace Ibnab\Bundle\CustomBundle\Migrations\Data\ORM;
  4. use Doctrine\Common\DataFixtures\AbstractFixture;
  5. use Doctrine\Common\DataFixtures\DependentFixtureInterface;
  6. use Doctrine\Common\Persistence\ObjectManager;
  7. use Oro\Bundle\EntityConfigBundle\Attribute\Entity\AttributeFamily;
  8. use Oro\Bundle\EntityConfigBundle\Attribute\Entity\AttributeGroup;
  9. use Oro\Bundle\EntityConfigBundle\Attribute\Entity\AttributeGroupRelation;
  10. use Oro\Bundle\EntityConfigBundle\Config\ConfigManager;
  11. use Oro\Bundle\ProductBundle\Entity\Product;
  12. use Oro\Bundle\UserBundle\DataFixtures\UserUtilityTrait;
  13. use Symfony\Component\DependencyInjection\ContainerAwareInterface;
  14. use Symfony\Component\DependencyInjection\ContainerAwareTrait;
  15. use Oro\Bundle\ProductBundle\Migrations\Data\ORM\LoadProductDefaultAttributeFamilyData;
  16. class LoadProductNewAttributesData extends AbstractFixture implements
  17.     DependentFixtureInterface,
  18.     ContainerAwareInterface
  19. {
  20.     use UserUtilityTrait;
  21.     use ContainerAwareTrait;
  22.      
  23.     const GROUP_CODE = 'custom';
  24.      private $fields = [
  25.         'customNumber' => [
  26.             'visible' => false
  27.         ]
  28.     ];
  29.     public function getDependencies()
  30.     {
  31.         return [
  32.             LoadProductDefaultAttributeFamilyData::class
  33.         ];
  34.     }
  35.     /**
  36.      * {@inheritdoc}
  37.      */
  38.     public function load(ObjectManager $manager)
  39.     {
  40.         $configManager = $this->container->get('oro_entity_config.config_manager');
  41.         $attributeFamilyRepository = $manager->getRepository(AttributeFamily::class);
  42.         $defaultFamily =
  43.             $attributeFamilyRepository->findOneBy([
  44.                 'code' => LoadProductDefaultAttributeFamilyData::DEFAULT_FAMILY_CODE
  45.             ]);
  46.         $attributeGroup = new AttributeGroup();
  47.         $attributeGroup->setAttributeFamily($defaultFamily);
  48.         $attributeGroup->setDefaultLabel('Custom');
  49.         $attributeGroup->setCode(self::GROUP_CODE);
  50.         $attributeGroup->setIsVisible(false);
  51.         foreach ($this->fields as $attribute => $data) {
  52.             $fieldConfigModel = $configManager->getConfigFieldModel(Product::class, $attribute);
  53.             $attributeGroupRelation = new AttributeGroupRelation();
  54.             $attributeGroupRelation->setEntityConfigFieldId($fieldConfigModel->getId());
  55.             $attributeGroup->addAttributeRelation($attributeGroupRelation);
  56.         }
  57.         $manager->persist($attributeGroup);
  58.         $manager->flush();
  59.     }
  60. }
  61.  

So here we create a new attribute group with name custom and we add our attribute to this group , and we’re using the default family for this attribute group.

Comments

Related Posts

make your store more efficient

Solving problems. With open source technology. Professional results. That’s what makes Ibnab your best choice

IBNAB is a company made of a group of professionals whose work is providing secure open source solutions. Our company strives for reaching magnificent results with each experience and provides professional open source solutions that cover every part of the business process.