Important

You are browsing the documentation for version 3.1 of OroCommerce, OroCRM and OroPlatform, which is no longer maintained. Read version 5.1 (the latest LTS version) of the Oro documentation to get up-to-date information.

See our Release Process documentation for more information on the currently supported and upcoming releases.

Extend Entities

Common Doctrine entities have a fixed structure. This means that you cannot add additional attributes to existing entities. Of course, one can extend an entity class and add additional properties in the subclass. However, this approach does not work anymore when an entity should be extended by different modules.

To solve this, you can use the EntityExtendBundle which offers the following features:

  • Dynamically add fields to entities through configuration.

  • Users with appropriate permissions can add or remove dynamic fields from entities in the user interface without the assistance of a developer.

  • Show dynamic fields in views, forms, and grids.

  • Support for dynamic relations between entities.

Caution

It is not recommended to rely on the existence of dynamic fields in your business logic since they can be removed by administrative users.

Create Extended Entities

  1. Create the extend entity class:

     1// src/Acme/DemoBundle/Model/ExtendHotel.php
     2namespace Acme\DemoBundle\Model;
     3
     4class ExtendHotel
     5{
     6    /**
     7     * Constructor
     8     *
     9     * The real implementation of this method is auto generated.
    10     *
    11     * IMPORTANT: If the derived class has own constructor it must call parent constructor.
    12     */
    13    public function __construct()
    14    {
    15    }
    16}
    

    The class name of an extended entity consists of two parts: Its name always must start with Extend. The suffix (here Hotel) must be the name of your entity class.

    The class itself is an empty skeleton. Its actual content will be generated dynamically in the application cache.

  2. Let the entity class extend the extend entity class:

     1// src/Acme/DemoBundle/Entity/Hotel.php
     2namespace Acme\DemoBundle\Entity;
     3
     4use Acme\DemoBundle\Model\ExtendHotel;
     5use Doctrine\ORM\Mapping as ORM;
     6
     7/**
     8 * @ORM\Entity
     9 * @ORM\Table(name="acme_hotel")
    10 */
    11class Hotel extends ExtendHotel
    12{
    13    /**
    14     * @ORM\Id
    15     * @ORM\Column(type="integer")
    16     * @ORM\GeneratedValue(strategy="AUTO")
    17     */
    18    private $id;
    19
    20    /**
    21     * @ORM\Column(type="string", length=255)
    22     */
    23    private $name;
    24
    25    public function getId()
    26    {
    27        return $this->id;
    28    }
    29
    30    public function getName()
    31    {
    32        return $this->name;
    33    }
    34
    35    public function setName($name)
    36    {
    37        $this->name = $name;
    38    }
    39}
    
  3. Add new properties using Oro migrations:

     1// src/Acme/DemoBundle/Migrations/Schema/v2_0;
     2namespace Acme\DemoBundle\Migrations\Schema\v2_0;
     3
     4use Doctrine\DBAL\Schema\Schema;
     5use Oro\Bundle\MigrationBundle\Migration\Migration;
     6use Oro\Bundle\MigrationBundle\Migration\QueryBag;
     7use Oro\Bundle\EntityExtendBundle\EntityConfig\ExtendScope;
     8
     9class HotelRankingColumn implements Migration
    10{
    11    /**
    12     * @inheritdoc
    13     */
    14    public function up(Schema $schema, QueryBag $queries)
    15    {
    16        $table = $schema->getTable('acme_hotel');
    17        $table->addColumn(
    18            'hotel_rating',
    19            'string',
    20            ['oro_options' => [
    21                'extend' => [
    22                    'is_extend' => true,
    23                    'owner' => ExtendScope::OWNER_CUSTOM
    24                ],
    25                'entity' => ['label' => 'Hotel rating'],
    26                'datagrid' => ['is_visible' => false]
    27            ]]
    28        );
    29    }
    30}
    

    The example above adds a new column hotel_ranking. The third parameter configures the column as an extended field. The ExtendScope::OWNER_CUSTOM owner in the oro_options key indicates that the column was added dynamically. It will be visible and configurable in the UI.

    Note that this property is neither present in the Hotel entity class nor in the ExtendHotel class in your bundle, but it will only be part of the ExtendHotel class that will be generated in your application cache.

  4. Finally, load the changed configuration using the oro:migration:load command:

    $ php bin/console oro:migration:load --force
    

    This command updates the database schema and generates the real implementation of the ExtendHotel class in the application cache as well.

Note

You can add, modify and remove custom fields in the UI under System/Entities/Entity Management.

Add Entity Properties

You may require to customize the default Oro entities to fit your application’s needs.

Let us customize the Contact entity to store the date when a contact becomes a member of your company’s partner network. For the sake of example, we will use the Contact entity from the custom AppBundle.

To achieve this, add a new property partnerSince to store the date and time of when a contact joined your network. To add the property, create a migration:

 1// src/AppBundle/Migrations/Schema/v1_0/AddPartnerSinceToContact.php
 2namespace AppBundle\Migrations\Schema\v1_0;
 3
 4use Doctrine\DBAL\Schema\Schema;
 5use Oro\Bundle\EntityExtendBundle\EntityConfig\ExtendScope;
 6use Oro\Bundle\MigrationBundle\Migration\Migration;
 7use Oro\Bundle\MigrationBundle\Migration\QueryBag;
 8
 9class AddPartnerSinceToContact implements Migration
10{
11    public function up(Schema $schema, QueryBag $queries)
12    {
13        $table = $schema->getTable('contact');
14        $table->addColumn('partnerSince', 'datetime', [
15            'oro_options' => [
16                'extend' => ['owner' => ExtendScope::OWNER_CUSTOM],
17            ],
18        ]);
19    }
20}

Note

Please note that the entity that you add a new property to must have the @Config annotation and should extend an empty Extend class:

 1// src/AppBundle/Entity/Contact.php
 2namespace AppBundle\Entity;
 3
 4use Doctrine\ORM\Mapping as ORM;
 5
 6use Oro\Bundle\EntityConfigBundle\Metadata\Annotation\Config;
 7
 8use AppBundle\Entity\Model\ExtendContact;
 9
10/**
11 * @ORM\Entity()
12 * @ORM\Table(name="contact")
13 * @Config()
14 */
15class Contact extends ExtendContact
16{
17}
 1// src/AppBundle/Model/ExtendContact.php
 2namespace AppBundle\Model;
 3
 4class ExtendContact
 5{
 6    /**
 7     * A skeleton method for the getter. You can add it to use autocomplete hints from the IDE.
 8     * The real implementation of this method is auto generated.
 9     *
10     * @return \DateTime
11     */
12    public function getPartnerSince()
13    {
14    }
15
16    /**
17     * A skeleton method for the setter. You can add it to use autocomplete hints from the IDE.
18     * The real implementation of this method is auto generated.
19     *
20     * @param \DateTime $partnerSince
21     */
22    public function setPartnerSince(\DateTime $partnerSince)
23    {
24    }
25}

The important part in this migration (which is different from common Doctrine migrations) is the oro_options key. It is passed through the options argument of the addColumn() method:

1...
2         $table->addColumn('partnerSince', 'datetime', [
3             'oro_options' => [
4                 'extend' => ['owner' => ExtendScope::OWNER_CUSTOM],
5             ],
6         ]);
7...

All options nested under this key are handled outside of the usual Doctrine migration workflow.

When the EntityExtendBundle of OroPlatform finds the extend key, it generates an intermediate class with getters and setters for the defined properties, thus making them accessible from every part of the code. The intermediate class is generated automatically based on the configured data when the application cache is warmed up.

The owner attribute can have the following values:

  • ExtendScope::OWNER_CUSTOM — The property is user-defined, and the core system should handle how the property appears in grids, forms, etc. (if not configured otherwise).

  • ExtendScope::OWNER_SYSTEM— Nothing is rendered automatically, and the developer must explicitly specify how to show the property in different parts of the system (grids, forms, views, etc.).

Add Entity Relations

Adding relations between entities is a common task. For example, imagine that the owner of an Email entity can either be a user or a contact. Using OroPlatform, you have two opportunities to manage relations between the email and its owner:

Use Doctrine’s built-in functions to add two relations to the Email entity. One to model a many-to-one relationship to a user and another one to model the relationship to a contact. No matter what actual entity the Email belongs to, one of the properties contact and user will always be null. Furthermore, you always have to modify your code to add new types of ownership. Third-party modules can’t add new types but have to ask you, the developer, to add them instead.

The second approach is to use the EntityExtendBundle to configure so-called associations. Once you have done it in your application, you can also repeat that for configurable entities from third-party modules. The bundle will create matching Doctrine relations and getter/setter methods for you automatically. The downside of this approach is that the owning side of a relationship always has to be an extended entity, and that associations do not work for bidirectional relations.

Doctrine Relations

If you know in advance which entities will be associated with your Email entity, you can use common Doctrine relations. For example, an Email can either belong to a Contact or to a User. All you have to do is to add both a $user and a $contact property to your Email class and dynamically choose the property to use in the setOwner() and getOwner() methods:

 1// src/Acme/DemoBundle/Entity/Email.php
 2namespace Acme\DemoBundle\Entity;
 3
 4use Doctrine\ORM\Mapping as ORM;
 5
 6/**
 7 * @ORM\Entity
 8 */
 9class Email
10{
11    /**
12     * @ORM\OneToOne(targetEntity="User", inversedBy="email")
13     */
14    private $user;
15
16    /**
17     * @ORM\OneToOne(targetEntity="Contact", inversedBy="email")
18     */
19    private $contact;
20
21    /**
22     * @return User|Contact|null $owner
23     */
24   public function getOwner()
25   {
26       if (null !== $this->user) {
27            return $this->user;
28       }
29
30       if (null !== $this->contact) {
31            return $this->contact;
32       }
33
34       return null;
35   }
36
37    /**
38     * @param User|Contact|null $owner
39     */
40    public function setOwner($owner)
41    {
42        if (null === $owner) {
43            $this->user = null;
44            $this->contact = null;
45        } elseif ($owner instanceof User) {
46            $this->user = $owner;
47            $this->contact = null;
48        } elseif ($owner instanceof Contact) {
49            $this->user = null;
50            $this->contact = $owner;
51        } else {
52            throw new \InvalidArgumentException('Owner needs to be a user or a contact');
53        }
54    }
55}

The advantage of this solution is that you are in full control of your entity management. For example, you can add additional methods that ease your development or create bidirectional relationships. On the downside, your code is more verbose: You have to add conditions in your getter and setter methods for all possible referenced entities. Furthermore, third-party modules cannot add new types, and you cannot create relations to custom entities that were created by an administrator through the entity management interface.

If you are in the need of those features, you have to use associations as provided for extended entities.

Many-to-One Associations

To explain how to create many-to-one associations, the following section explains some parts of the OroNoteBundle to show how an entity can be created to which you can then attach a collection of Note objects. First, you need to create the owning side of the associations. As explained above, the owning side has to be an extended entity. Please note that the real implementations of the methods shown below will be generated in the cache:

 1namespace Oro\Bundle\NoteBundle\Model;
 2
 3class ExtendNote
 4{
 5    public function __construct()
 6    {
 7    }
 8
 9    public function supportTarget($targetClass)
10    {
11        return false;
12    }
13
14    public function getTarget()
15    {
16        return null;
17    }
18
19    public function setTarget($target)
20    {
21        return $this;
22    }
23}

The actual Note entity then needs to extend the ExtendNote:

 1namespace Oro\Bundle\NoteBundle\Entity;
 2
 3/**
 4 * @ORM\Entity
 5 * @ORM\Table(name="oro_note")
 6 * @Config
 7 */
 8class Note extends ExtendNote
 9{
10}

The bundle also defines some entity configuration properties which make it possible to control to which entities notes can be added:

 1entity_config:
 2    note:
 3        entity:
 4            items:
 5                # indicates whether the entity can have notes or not
 6                enabled: # boolean
 7                    options:
 8                        require_schema_update: true
 9                        priority:           250
10                        default_value:      false
11                    form:
12                        type:               oro_entity_extend_association_choice
13                        options:
14                            block:          associations
15                            required:       true
16                            label:          oro.note.enabled
17                            association_class: 'OroNoteBundle:Note'
18
19                # this attribute can be used to prohibit changing the note association state (no matter whether
20                # it is enabled or not) for the entity
21                # if TRUE than the current state cannot be changed
22                immutable: # boolean
23                    options:
24                        auditable:          false

Finally, you have to create extensions for the entity config dumper, the entity generator and the migrations to make the association available through all stages of the entity generation process:

  1. Hook into the entity config dumper:

     1namespace Oro\Bundle\NoteBundle\Tools;
     2
     3use Oro\Bundle\EntityExtendBundle\Tools\DumperExtensions\AssociationEntityConfigDumperExtension;
     4use Oro\Bundle\NoteBundle\Entity\Note;
     5
     6class NoteEntityConfigDumperExtension extends AssociationEntityConfigDumperExtension
     7{
     8    /**
     9     * {@inheritdoc}
    10     */
    11    protected function getAssociationEntityClass()
    12    {
    13        return Note::ENTITY_NAME;
    14    }
    15
    16    /**
    17     * {@inheritdoc}
    18     */
    19    protected function getAssociationScope()
    20    {
    21        return 'note';
    22    }
    23}
    
  2. Extend the entity generator:

     1namespace Oro\Bundle\NoteBundle\Tools;
     2
     3use Oro\Bundle\EntityExtendBundle\Tools\GeneratorExtensions\AbstractAssociationEntityGeneratorExtension;
     4use Oro\Bundle\NoteBundle\Entity\Note;
     5
     6class NoteEntityGeneratorExtension extends AbstractAssociationEntityGeneratorExtension
     7{
     8    /**
     9     * {@inheritdoc}
    10     */
    11    public function supports(array $schema)
    12    {
    13        return $schema['class'] === Note::ENTITY_NAME && parent::supports($schema);
    14    }
    15}
    
  3. Extend the migration behavior to add the association to target entities:

     1namespace Oro\Bundle\NoteBundle\Migration\Extension;
     2
     3use Doctrine\DBAL\Schema\Schema;
     4use Oro\Bundle\EntityExtendBundle\Migration\Extension\ExtendExtension;
     5use Oro\Bundle\EntityExtendBundle\Migration\Extension\ExtendExtensionAwareInterface;
     6use Oro\Bundle\EntityExtendBundle\Migration\OroOptions;
     7use Oro\Bundle\EntityExtendBundle\Tools\ExtendHelper;
     8
     9class NoteExtension implements ExtendExtensionAwareInterface
    10{
    11    const NOTE_TABLE_NAME = 'oro_note';
    12
    13    /** @var ExtendExtension */
    14    protected $extendExtension;
    15
    16    /**
    17     * {@inheritdoc}
    18     */
    19    public function setExtendExtension(ExtendExtension $extendExtension)
    20    {
    21        $this->extendExtension = $extendExtension;
    22    }
    23
    24    /**
    25     * Adds the association between the target table and the note table
    26     *
    27     * @param Schema $schema
    28     * @param string $targetTableName  Target entity table name
    29     * @param string $targetColumnName A column name is used to show related entity
    30     */
    31    public function addNoteAssociation(
    32         Schema $schema,
    33         $targetTableName,
    34         $targetColumnName = null
    35    ) {
    36        $noteTable   = $schema->getTable(self::NOTE_TABLE_NAME);
    37        $targetTable = $schema->getTable($targetTableName);
    38
    39        if (empty($targetColumnName)) {
    40            $primaryKeyColumns = $targetTable->getPrimaryKeyColumns();
    41            $targetColumnName  = array_shift($primaryKeyColumns);
    42        }
    43
    44        $options = new OroOptions();
    45        $options->set('note', 'enabled', true);
    46        $targetTable->addOption(OroOptions::KEY, $options);
    47
    48        $associationName = ExtendHelper::buildAssociationName(
    49            $this->extendExtension->getEntityClassByTableName($targetTableName)
    50        );
    51
    52        $this->extendExtension->addManyToOneRelation(
    53            $schema,
    54            $noteTable,
    55            $associationName,
    56            $targetTable,
    57            $targetColumnName
    58        );
    59    }
    60}
    

Many-to-Many Associations

When it comes to many-to-many associations, it’s up to you as the developer to choose the owning side of the relation. The owning side of this association must be an extended entity, and you need to choose a group name (the group name is the name of the association). Therefore, the extended entity needs to provide five methods (Group has to be replaced with the actual name of the association):

  • supportGroupTarget

  • getGroupTargets

  • hasGroupTarget

  • addGroupTarget

  • removeGroupTarget

To make this more clear, the ActivityBundle will be taken as an example. It provides the ability to assign activities (like calls, emails, tasks) to other entities. The association name is Activity. Therefore, the ExtendActivity class looks like this:

 1namespace Oro\Bundle\ActivityBundle\Model;
 2
 3trait ExtendActivity
 4{
 5    /**
 6     * Checks if an entity of the given type can be associated with this activity entity
 7     *
 8     * The real implementation of this method is auto generated.
 9     *
10     * @param string $targetClass The class name of the target entity
11     * @return bool
12     */
13    public function supportActivityTarget($targetClass)
14    {
15        return false;
16    }
17
18    /**
19     * Gets entities of the given type associated with this activity entity
20     *
21     * The real implementation of this method is auto generated.
22     *
23     * @param string $targetClass The class name of the target entity
24     * @return object[]
25     */
26    public function getActivityTargets($targetClass)
27    {
28        return null;
29    }
30
31    /**
32     * Checks is the given entity is associated with this activity entity
33     *
34     * The real implementation of this method is auto generated.
35     *
36     * @param object $target Any configurable entity that can be associated with this activity
37     *
38     * @return bool
39     */
40    public function hasActivityTarget($target)
41    {
42        return false;
43    }
44
45    /**
46     * Associates the given entity with this activity entity
47     *
48     * The real implementation of this method is auto generated.
49     *
50     * @param object $target Any configurable entity that can be associated with this activity
51     * @return object This object
52     */
53    public function addActivityTarget($target)
54    {
55        return $this;
56    }
57
58    /**
59     * Removes the association of the given entity with this activity entity
60     *
61     * The real implementation of this method is auto generated.
62     *
63     * @param object $target Any configurable entity that can be associated with this activity
64     * @return object This object
65     */
66    public function removeActivityTarget($target)
67    {
68        return $this;
69    }
70}

To create a new entity that can be assigned in an Activity association, let the entity class use the ExtendActivity trait:

 1// src/Acme/DemoBundle/Model/ExtendEmail.php
 2namespace Acme\DemoBundle\Model;
 3
 4use Oro\Bundle\ActivityBundle\Model\ActivityInterface;
 5use Oro\Bundle\ActivityBundle\Model\ExtendActivity;
 6
 7class ExtendEmail implements ActivityInterface
 8{
 9    use ExtendActivity;
10
11    /**
12     * Constructor
13     *
14     * The real implementation of this method is auto generated.
15     *
16     * IMPORTANT: If the derived class has own constructor it must call parent constructor.
17     */
18    public function __construct()
19    {
20    }
21}
 1// src/Acme/DemoBundle/Entity/Email.php
 2namespace Acme\DemoBundle\Entity;
 3
 4use Doctrine\ORM\Mapping as ORM;
 5use Oro\Bundle\EntityConfigBundle\Metadata\Annotation\Config;
 6use Acme\DemoBundle\Model\ExtendEmail;
 7
 8/**
 9 * @ORM\Entity
10 * @ORM\Table(name="acme_email")
11 * @Config
12 */
13class Email extends ExtendEmail
14{
15}

You then have to use the entity configuration

 1# src/Acme/DemoBundle/Resources/config/oro/entity_config.yml
 2entity_config:
 3    activity:
 4        entity:
 5            items:
 6                # the list of activities that can be assigned to the entity
 7                activities: # array of class names
 8                    options:
 9                        require_schema_update: true
10                        priority:           250
11                    form:
12                        type:               oro_entity_extend_multiple_association_choice
13                        options:
14                            block:          associations
15                            required:       false
16                            label:          oro.activity.activities
17                            association_class: activity
18
19                # this attribute can be used to prohibit changing activity state (no matter whether
20                # it is enabled or not) for the entity
21                # if TRUE than no one activity state can be changed
22                # also it can be an array with the list of class names of activities which state cannot be changed
23                immutable: # boolean or array
24                    options:
25                        auditable:          false

Finally, you have to create extensions for the entity config dumper, the entity generator and the migrations to make the association available through all stages of the entity generation process:

  1. Hook into the entity config dumper:

     1namespace Oro\Bundle\ActivityBundle\Tools;
     2
     3use Oro\Bundle\ActivityBundle\EntityConfig\ActivityScope;
     4use Oro\Bundle\EntityExtendBundle\Tools\DumperExtensions\MultipleAssociationEntityConfigDumperExtension;
     5
     6class ActivityEntityConfigDumperExtension extends MultipleAssociationEntityConfigDumperExtension
     7{
     8    /**
     9     * {@inheritdoc}
    10     */
    11    protected function getAssociationScope()
    12    {
    13        return 'activity';
    14    }
    15
    16    /**
    17     * {@inheritdoc}
    18     */
    19    protected function getAssociationAttributeName()
    20    {
    21        return 'activities';
    22    }
    23
    24    /**
    25     * {@inheritdoc}
    26     */
    27    protected function getAssociationKind()
    28    {
    29        return ActivityScope::ASSOCIATION_KIND;
    30    }
    31}
    
  2. Extend the entity generator:

     1namespace Oro\Bundle\ActivityBundle\Tools;
     2
     3use CG\Generator\PhpClass;
     4use Oro\Bundle\ActivityBundle\EntityConfig\ActivityScope;
     5use Oro\Bundle\EntityConfigBundle\Provider\ConfigProvider;
     6use Oro\Bundle\EntityExtendBundle\Extend\RelationType;
     7use Oro\Bundle\EntityExtendBundle\Tools\GeneratorExtensions\AbstractAssociationEntityGeneratorExtension;
     8
     9class ActivityEntityGeneratorExtension extends AbstractAssociationEntityGeneratorExtension
    10{
    11    /** @var ConfigProvider */
    12    protected $groupingConfigProvider;
    13
    14    /**
    15     * @param ConfigProvider $groupingConfigProvider
    16     */
    17    public function __construct(ConfigProvider $groupingConfigProvider)
    18    {
    19        $this->groupingConfigProvider = $groupingConfigProvider;
    20    }
    21
    22    /**
    23     * {@inheritdoc}
    24     */
    25    public function supports(array $schema)
    26    {
    27        if (!$this->groupingConfigProvider->hasConfig($schema['class'])) {
    28            return false;
    29        }
    30
    31        $groups = $this->groupingConfigProvider->getConfig($schema['class'])->get('groups');
    32
    33        return
    34            !empty($groups)
    35            && in_array(ActivityScope::GROUP_ACTIVITY, $groups);
    36    }
    37
    38    /**
    39     * {@inheritdoc}
    40     */
    41    public function generate(array $schema, PhpClass $class)
    42    {
    43        $class->addInterfaceName('Oro\Bundle\ActivityBundle\Model\ActivityInterface');
    44
    45        parent::generate($schema, $class);
    46    }
    47
    48    /**
    49     * {@inheritdoc}
    50     */
    51    protected function getAssociationKind()
    52    {
    53        return ActivityScope::ASSOCIATION_KIND;
    54    }
    55
    56    /**
    57     * {@inheritdoc}
    58     */
    59    protected function getAssociationType()
    60    {
    61        return RelationType::MANY_TO_MANY;
    62    }
    63}
    
  3. Extend the migration behavior to add the association to target entities:

     1namespace Oro\Bundle\ActivityBundle\Migration\Extension;
     2
     3use Doctrine\DBAL\Schema\Schema;
     4
     5use Oro\Bundle\ActivityBundle\EntityConfig\ActivityScope;
     6use Oro\Bundle\EntityExtendBundle\Migration\Extension\ExtendExtension;
     7use Oro\Bundle\EntityExtendBundle\Migration\Extension\ExtendExtensionAwareInterface;
     8use Oro\Bundle\EntityExtendBundle\Migration\OroOptions;
     9use Oro\Bundle\EntityExtendBundle\Tools\ExtendHelper;
    10
    11class ActivityExtension implements ExtendExtensionAwareInterface
    12{
    13    /** @var ExtendExtension */
    14    protected $extendExtension;
    15
    16    /**
    17     * {@inheritdoc}
    18     */
    19    public function setExtendExtension(ExtendExtension $extendExtension)
    20    {
    21        $this->extendExtension = $extendExtension;
    22    }
    23
    24    /**
    25     * Adds the association between the given table and the table contains activity records
    26     *
    27     * The activity entity must be included in 'activity' group ('groups' attribute of 'grouping' scope)
    28     *
    29     * @param Schema $schema
    30     * @param string $activityTableName Activity entity table name. It is owning side of the association
    31     * @param string $targetTableName   Target entity table name
    32     * @param bool   $immutable         Set TRUE to prohibit disabling the activity association from UI
    33     */
    34    public function addActivityAssociation(
    35        Schema $schema,
    36        $activityTableName,
    37        $targetTableName,
    38        $immutable = false
    39    ) {
    40        $targetTable = $schema->getTable($targetTableName);
    41
    42        // Column names are used to show a title of target entity
    43        $targetTitleColumnNames = $targetTable->getPrimaryKeyColumns();
    44        // Column names are used to show detailed info about target entity
    45        $targetDetailedColumnNames = $targetTable->getPrimaryKeyColumns();
    46        // Column names are used to show target entity in a grid
    47        $targetGridColumnNames = $targetTable->getPrimaryKeyColumns();
    48
    49        $activityClassName = $this->extendExtension->getEntityClassByTableName($activityTableName);
    50
    51        $options = new OroOptions();
    52        $options->append(
    53            'activity',
    54            'activities',
    55            $activityClassName
    56        );
    57        if ($immutable) {
    58            $options->append(
    59                'activity',
    60                'immutable',
    61                $activityClassName
    62            );
    63        }
    64
    65        $targetTable->addOption(OroOptions::KEY, $options);
    66
    67        $associationName = ExtendHelper::buildAssociationName(
    68            $this->extendExtension->getEntityClassByTableName($targetTableName),
    69            ActivityScope::ASSOCIATION_KIND
    70        );
    71
    72        $this->extendExtension->addManyToManyRelation(
    73            $schema,
    74            $activityTableName,
    75            $associationName,
    76            $targetTable,
    77            $targetTitleColumnNames,
    78            $targetDetailedColumnNames,
    79            $targetGridColumnNames,
    80            [
    81                'extend' => [
    82                    'without_default' => true
    83                ]
    84            ]
    85        );
    86    }
    87}