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:
* @Config( * routeName="oro_product_index", * routeView="oro_product_view", * routeUpdate="oro_product_update", * defaultValues={ * "entity"={ * "icon"="fa-briefcase" * }, * "ownership"={ * "owner_type"="BUSINESS_UNIT", * "owner_field_name"="owner", * "owner_column_name"="business_unit_owner_id", * "organization_field_name"="organization", * "organization_column_name"="organization_id" * }, * "dataaudit"={ * "auditable"=true * }, * "security"={ * "type"="ACL", * "group_name"="", * "category"="catalog" * }, * "form"={ * "form_type"="Oro\Bundle\ProductBundle\Form\Type\ProductSelectType", * "grid_name"="products-select-grid" * }, * "attribute"={ * "has_attributes"=true * } * } * )
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:
* "attribute"={ * "has_attributes"=true * }
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:
/** * @var string * * @ORM\Column(name="type", type="string", length=32, nullable=false) * @ConfigField( * defaultValues={ * "dataaudit"={ * "auditable"=true * }, * "importexport"={ * "order"=20 * } * } * ) */ protected $type = self::TYPE_SIMPLE;
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:
namespace Oro\Bundle\ProductBundle\Model; use Oro\Bundle\EntityBundle\Entity\EntityFieldFallbackValue; use Oro\Bundle\EntityExtendBundle\Entity\AbstractEnumValue; use Oro\Bundle\LocaleBundle\Entity\Localization; use Oro\Bundle\LocaleBundle\Entity\LocalizedFallbackValue; use Oro\Bundle\ProductBundle\Entity\Product; class ExtendProduct { /** * Constructor * * The real implementation of this method is auto generated. * * IMPORTANT: If the derived class has own constructor it must call parent constructor. */ public function __construct() { } }
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 :
<?php namespace Ibnab\Bundle\CustomBundle\Migrations\Schema\v1_0; use Doctrine\DBAL\Schema\Schema; use Oro\Bundle\MigrationBundle\Migration\Migration; use Oro\Bundle\MigrationBundle\Migration\QueryBag; use Oro\Bundle\ProductBundle\Entity\Product; use Oro\Bundle\EntityExtendBundle\EntityConfig\ExtendScope; class AddCustomsField implements Migration { const PRODUCT_TABLE_NAME = 'oro_product'; /** * {@inheritdoc} */ public function up(Schema $schema, QueryBag $queries) { $table = $schema->getTable(self::PRODUCT_TABLE_NAME); $table->addColumn('customNumber', 'string',['length' => 20,'notnull' => false, 'oro_options' => [ 'extend' => [ 'is_extend' => true, 'owner' => ExtendScope::OWNER_SYSTEM ], 'attribute' => [ 'is_attribute' => true, 'enabled' => true, 'visible' => true, 'label' => 'Customs Number', 'description' => 'Customs Number' ], 'entity' => [ 'label' => 'Customs Number', 'description' => 'Customs Number' ] ]]); } public function getOrder() { return 90; } }
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:
php bin/console oro:migration:load --env prod
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:
<?php namespace Ibnab\Bundle\CustomBundle\Migrations\Data\ORM; use Oro\Bundle\ProductBundle\Migrations\Data\ORM\MakeProductAttributesTrait; use Doctrine\Common\DataFixtures\AbstractFixture; use Doctrine\Common\Persistence\ObjectManager; use Symfony\Component\DependencyInjection\ContainerAwareInterface; class UpdateAttributesConfig extends AbstractFixture implements ContainerAwareInterface { use MakeProductAttributesTrait; /** * @var array */ private $fields = [ 'customNumber' => [ 'searchable' => true, 'filterable' => true, 'filter_by' => 'exact_value' ] ]; /** * {@inheritdoc} */ public function load(ObjectManager $manager) { // make new field as attribute $this->makeProductAttributes($this->fields); // update existing attribute $this->updateProductAttributes($this->fields); } }
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 :
{ $configManager = $this->getConfigManager(); $configHelper = $this->container->get('oro_entity_config.config.config_helper'); $entityManager = $configManager->getEntityManager(); foreach ($fields as $field => $attributeOptions) { $fieldConfigModel = $configManager->getConfigFieldModel(Product::class, $field); $options = [ 'is_attribute' => true, ], $attributeOptions), 'extend' => [ 'owner' => $owner ] ]; $configHelper->updateFieldConfigs($fieldConfigModel, $options); $entityManager->persist($fieldConfigModel); } }
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:
<?php namespace Ibnab\Bundle\CustomBundle\Migrations\Data\ORM; use Doctrine\Common\DataFixtures\AbstractFixture; use Doctrine\Common\DataFixtures\DependentFixtureInterface; use Doctrine\Common\Persistence\ObjectManager; use Oro\Bundle\EntityConfigBundle\Attribute\Entity\AttributeFamily; use Oro\Bundle\EntityConfigBundle\Attribute\Entity\AttributeGroup; use Oro\Bundle\EntityConfigBundle\Attribute\Entity\AttributeGroupRelation; use Oro\Bundle\EntityConfigBundle\Config\ConfigManager; use Oro\Bundle\ProductBundle\Entity\Product; use Oro\Bundle\UserBundle\DataFixtures\UserUtilityTrait; use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerAwareTrait; use Oro\Bundle\ProductBundle\Migrations\Data\ORM\LoadProductDefaultAttributeFamilyData; class LoadProductNewAttributesData extends AbstractFixture implements DependentFixtureInterface, ContainerAwareInterface { use UserUtilityTrait; use ContainerAwareTrait; const GROUP_CODE = 'custom'; private $fields = [ 'customNumber' => [ 'visible' => false ] ]; public function getDependencies() { return [ LoadProductDefaultAttributeFamilyData::class ]; } /** * {@inheritdoc} */ public function load(ObjectManager $manager) { $configManager = $this->container->get('oro_entity_config.config_manager'); $attributeFamilyRepository = $manager->getRepository(AttributeFamily::class); $defaultFamily = $attributeFamilyRepository->findOneBy([ 'code' => LoadProductDefaultAttributeFamilyData::DEFAULT_FAMILY_CODE ]); $attributeGroup = new AttributeGroup(); $attributeGroup->setAttributeFamily($defaultFamily); $attributeGroup->setDefaultLabel('Custom'); $attributeGroup->setCode(self::GROUP_CODE); $attributeGroup->setIsVisible(false); foreach ($this->fields as $attribute => $data) { $fieldConfigModel = $configManager->getConfigFieldModel(Product::class, $attribute); $attributeGroupRelation = new AttributeGroupRelation(); $attributeGroupRelation->setEntityConfigFieldId($fieldConfigModel->getId()); $attributeGroup->addAttributeRelation($attributeGroupRelation); } $manager->persist($attributeGroup); } }
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