How to change the configuration of the field’s entity using migration in OroCRM

Sometimes we can face with the different dependencies between the system bundles or bundles that are already created by other developers, and we will have to change the configuration for such bundles. Usually, it can be easily done because OroCRM has a quite good GUI interface. So, using GUI you will be able to change the configuration for any bundle. Nevertheless, pay attention that if you need the bundle for deployment to different servers, this way is not convenient. In case of deployment, you will need to add a description of the appropriate settings and it is also important to follow these settings in further development. The best way is to use the migrations scripts. In this article, we will show you how to change a config of a single entity in the bundle.

In the beginning, we should create our bundle itself – you can use own bundle, but we create a test one to show how to change the configuration of the field’s entity. Moreover, we will use this bundle for our new articles related to this topic. In our previous article we have already described how you can add a new bundle, and you are welcome to use this instruction for own bundle creation. After the bundle has been created – do not forget to clear the cache.

Next our step is to create the migration script. And for this, just create a Migration folder in the root of the bundle. Further, create a new folder named Schema inside of the Migration folder. And then one more child folder should be created, already in Schema folder, named the same way as your migration script’s version. For our new bundle, we use version 1_0.

The structure of our migration is the following:

atwix_test_bundle

And the AtwixTestBundle class (version 1_0) has only one method “up” which creates an object of the UpdateEntityData class. Here is the code of the AtwixTestBundle class:

<?php
// src/Atwix/Bundle/TestBundle/Migrations/Schema/v1_0/AtwixTestBundle.php

namespace Atwix\Bundle\TestBundle\Migrations\Schema\v1_0;

use Doctrine\DBAL\Schema\Schema;
use Atwix\Bundle\TestBundle\Migrations\Schema\AtwixTestMigrationsAbstract;
use Oro\Bundle\MigrationBundle\Migration\QueryBag;

class AtwixTestBundle extends AtwixTestMigrationsAbstract
{
    /**
     * {@inheritdoc}
     */
    public function up(Schema $schema, QueryBag $queries)
    {
        $queries->addQuery(new UpdateEntityData());
    }
}

Note that the AtwixTestBundle class (placed in the Schema folder) is extended from the AtwixTestMigrationsAbstract class, where the code is:

<?php
// src/Atwix/Bundle/TestBundle/Migrations/Schema/AtwixTestMigrationsAbstract.php

namespace Atwix\Bundle\TestBundle\Migrations\Schema;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\Table;
use Oro\Bundle\MigrationBundle\Migration\Migration;
use Oro\Bundle\MigrationBundle\Migration\QueryBag;

abstract class AtwixTestMigrationsAbstract implements Migration
{

    /**
     * {@inheritdoc}
     */
    public function getMigrationVersion()
    {
        return 'v1_0';
    }

    /**
     * {@inheritdoc}
     */
    public function up(Schema $schema, QueryBag $queries)
    {

    }
}

In this example, we’ve created a method for changing the config of the OroCRM OroCRM\Bundle\AccountBundle\Entity\Account entity. As you can see, we change the “Import/Export” config for this entity and set “excluded” to “yes”.

Create a class with the name UpdateEntityData in the same folder. The class extends ParametrizedMigrationQuery, and we also must create the methods getDescription() and execute() there. It is important to describe these methods because they are abstract in the ParametrizedMigrationQuery class.

Then, we should create the protected function “migrateConfigs”, and this function will change the “Import/Export”configuration. The code of this class:

<?php
// src/Atwix/Bundle/TestBundle/Migrations/Schema/v1_0/UpdateEntityData.php

namespace Atwix\Bundle\TestBundle\Migrations\Schema\v1_0;

use Psr\Log\LoggerInterface;

use Doctrine\DBAL\Types\Type;

use Oro\Bundle\EntityConfigBundle\Config\ConfigModelManager;
use Oro\Bundle\MigrationBundle\Migration\ArrayLogger;
use Oro\Bundle\MigrationBundle\Migration\ParametrizedMigrationQuery;

class UpdateEntityData extends ParametrizedMigrationQuery
{
    /**
     * {@inheritdoc}
     */
    public function getDescription()
    {
        $logger = new ArrayLogger();
        $this->migrateConfigs($logger, true);

        return $logger->getMessages();
    }

    /**
     * {@inheritdoc}
     */
    public function execute(LoggerInterface $logger)
    {
        $this->migrateConfigs($logger);
    }

    /**
     * @param LoggerInterface $logger
     * @param bool            $dryRun
     */
    protected function migrateConfigs(LoggerInterface $logger, $dryRun = false)
    {
        $query  = 'SELECT cf.id, cf.data'
            . ' FROM oro_entity_config c'
            . ' INNER JOIN oro_entity_config_field cf ON cf.entity_id = c.id'
            . ' WHERE c.class_name = :class_name'
            . ' AND cf.field_name = :field_name';

        $params = ['class_name' => 'OroCRM\Bundle\AccountBundle\Entity\Account', 'field_name' => 'name'];
        $types  = ['field' => Type::STRING];

        $this->logQuery($logger, $query, $params, $types);

        $updateQueries = [];

        // prepare update queries
        $rows = $this->connection->fetchAll($query, $params, $types);

        foreach ($rows as $row) {
            $id = $row['id'];
            $data = $this->connection->convertToPHPValue($row['data'], 'array');

            if (isset($data['importexport']['excluded'])) {
                $data['importexport']['excluded'] = 1;

                $value = $this->connection->convertToDatabaseValue($data, Type::TARRAY);

                $updateQueries[] = [
                    'UPDATE oro_entity_config_field SET data = :data WHERE id = :id',
                    ['id' => $id, 'data' => $value],
                    ['id' => Type::INTEGER, 'data' => Type::STRING]
                ];
            }
        }

        // execute update queries
        foreach ($updateQueries as $val) {
            $this->logQuery($logger, $val[0], $val[1], $val[2]);
            if (!$dryRun) {
                $this->connection->executeUpdate($val[0], $val[1], $val[2]);
            }
        }
    }
}

If you do not know which setting should be used, you can look into the config array of your field or use var_dump or print_r to find the array’s content. And you need to add die(print_r($data));, and run:

 php app/console oro:migration:load --dry-run --show-queries

after the line 59 $data = $this->connection->convertToPHPValue($row[‘data’], ‘array’);
– the script will not execute, but you will see a dump of the array in the console. The result of the array will be the following:

Array
(
    [email] => Array
        (
            [available_in_template] => 1
        )

    [entity] => Array
        (
            [label] => orocrm.account.name.label
            [description] => orocrm.account.name.description
        )

    [datagrid] => Array
        (
            [is_visible] => 1
        )

    [form] => Array
        (
            [is_enabled] => 1
        )

    [view] => Array
        (
            [is_displayable] => 1
        )

    [extend] => Array
        (
            [owner] => System
            [state] => Active
            [is_extend] => 
            [length] => 255
            [is_deleted] => 
            [origin] => System
            [is_extend_field_config] => 
            [is_default] => 
            [show_in_segments] => 
        )

    [dataaudit] => Array
        (
            [auditable] => 1
        )

    [merge] => Array
        (
            [display] => 1
        )

    [importexport] => Array
        (
            [identity] => 1
            [order] => 20
        )

    [organization] => Array
        (
            [applicable] => Array
                (
                    [all] => 1
                    [selective] => Array
                        (
                        )

                )

        )

)

Also, check the part of this code:

$query  = 'SELECT cf.id, cf.data'
            . ' FROM oro_entity_config c'
            . ' INNER JOIN oro_entity_config_field cf ON cf.entity_id = c.id'
            . ' WHERE c.class_name = :class_name'
            . ' AND cf.field_name = :field_name';

        $params = ['class_name' => 'OroCRM\Bundle\AccountBundle\Entity\Account', 'field_name' => 'name'];
        $types  = ['field' => Type::STRING];

note, that in the $params array – there you should specify the class name and the field name that you want to change. So, if you create the script correctly, then you can run the mentioned migration script.

To start with, check the script – for this, use a dry run:

php app/console oro:migration:load --dry-run --show-queries

Now, if we do not see any errors after the migration, we can run our script. Here it can be used for all bundles:

php app/console oro:migration:load --force

or only for our bundle, that was used as an example:

php app/console oro:migration:load --force --bundles="AtwixTestBundle"

We think now you are good at changing the config for a single field for different entities. Thank you for reading us.