Completed
Push — 1.9 ( d8eb28...5c3e2e )
by
unknown
61:52 queued 29s
created

ActivityContactMigrationQuery::canAddField()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 7
rs 9.2
cc 4
eloc 5
nc 4
nop 2
1
<?php
2
3
namespace OroCRM\Bundle\ActivityContactBundle\Migration;
4
5
use Psr\Log\LoggerInterface;
6
7
use Doctrine\DBAL\Types\Type;
8
use Doctrine\DBAL\Schema\Table;
9
use Doctrine\DBAL\Schema\Schema;
10
use Doctrine\DBAL\Schema\Comparator;
11
12
use Oro\Bundle\EntityConfigBundle\Entity\ConfigModel;
13
use Oro\Bundle\EntityExtendBundle\Migration\OroOptions;
14
use Oro\Bundle\EntityExtendBundle\Migration\EntityMetadataHelper;
15
use Oro\Bundle\EntityExtendBundle\Migration\ExtendOptionsManager;
16
use Oro\Bundle\MigrationBundle\Migration\ArrayLogger;
17
use Oro\Bundle\MigrationBundle\Migration\ParametrizedMigrationQuery;
18
19
use OroCRM\Bundle\ActivityContactBundle\Model\TargetExcludeList;
20
use OroCRM\Bundle\ActivityContactBundle\EntityConfig\ActivityScope;
21
use OroCRM\Bundle\ActivityContactBundle\Provider\ActivityContactProvider;
22
23
class ActivityContactMigrationQuery extends ParametrizedMigrationQuery
24
{
25
    /** @var Schema */
26
    protected $schema;
27
28
    /** @var EntityMetadataHelper */
29
    protected $metadataHelper;
30
31
    /** @var ActivityContactProvider */
32
    protected $activityContactProvider;
33
34
    /**
35
     * @param Schema                  $schema
36
     * @param EntityMetadataHelper    $metadataHelper
37
     * @param ActivityContactProvider $activityContactProvider
38
     */
39
    public function __construct(
40
        Schema $schema,
41
        EntityMetadataHelper $metadataHelper,
42
        ActivityContactProvider $activityContactProvider
43
    ) {
44
        $this->schema                  = $schema;
45
        $this->metadataHelper          = $metadataHelper;
46
        $this->activityContactProvider = $activityContactProvider;
47
    }
48
49
    /**
50
     * {@inheritdoc}
51
     */
52
    public function execute(LoggerInterface $logger)
53
    {
54
        $this->addActivityContactColumns($logger);
55
    }
56
57
    /**
58
     * {@inheritdoc}
59
     */
60
    public function getDescription()
61
    {
62
        $logger = new ArrayLogger();
63
        $this->addActivityContactColumns($logger, true);
64
65
        return $logger->getMessages();
66
    }
67
68
    /**
69
     * @param LoggerInterface $logger
70
     * @param bool            $dryRun
71
     */
72
    protected function addActivityContactColumns(LoggerInterface $logger, $dryRun = false)
73
    {
74
        $hasSchemaChanges          = false;
75
        $toSchema                  = clone $this->schema;
76
        $contactingActivityClasses = $this->activityContactProvider->getSupportedActivityClasses();
77
78
        $entities = $this->getConfigurableEntitiesData($logger);
79
        foreach ($entities as $entityClassName => $config) {
80
            // Skipp excluded entity
81
            if (TargetExcludeList::isExcluded($entityClassName)) {
82
                continue;
83
            }
84
85
            if ($this->canAddField($config, $contactingActivityClasses)) {
86
                $tableName = $this->getTableName($config, $entityClassName);
87
88
                // Process only existing tables
89
                if (!$toSchema->hasTable($tableName)) {
90
                    continue;
91
                }
92
93
                $table        = $toSchema->getTable($tableName);
94
                $tableColumns = $table->getColumns();
95
96
                /**
97
                 * Check if entity already has all needed columns.
98
                 * If at least one is not present we should check and add it.
99
                 */
100
                if ($this->hasEntityNeededColumns($tableColumns)) {
101
                    continue;
102
                }
103
104
                foreach (ActivityScope::$fieldsConfiguration as $fieldName => $fieldConfig) {
105
                    if (!$table->hasColumn($fieldName)) {
106
                        $hasSchemaChanges = $this->addColumn($table, $fieldName, $fieldConfig);
107
                    }
108
                }
109
            }
110
        }
111
112
        $this->runSchemaRelated($logger, $dryRun, $hasSchemaChanges, $toSchema);
113
    }
114
115
    /**
116
     * @param LoggerInterface $logger
117
     *
118
     * @return array
119
     *  key - class name
120
     *  value - entity config array data
121
     */
122
    protected function getConfigurableEntitiesData(LoggerInterface $logger)
123
    {
124
        $result = [];
125
126
        $sql    = 'SELECT class_name, data FROM oro_entity_config WHERE mode = ?';
127
        $params = [ConfigModel::MODE_DEFAULT];
128
        $types  = [Type::STRING];
129
130
        $this->logQuery($logger, $sql, $params, $types);
131
        $rows = $this->connection->fetchAll($sql, $params, $types);
132
        foreach ($rows as $row) {
133
            $result[$row['class_name']] = $this->connection->convertToPHPValue($row['data'], Type::TARRAY);
134
        }
135
136
        return $result;
137
    }
138
139
    /**
140
     * @param Table $table
141
     * @param string $fieldName
142
     * @param array $fieldConfig
143
     * @return bool
144
     */
145
    protected function addColumn($table, $fieldName, $fieldConfig)
146
    {
147
        $hasSchemaChanges = true;
148
        $table->addColumn(
149
            $fieldName,
150
            $fieldConfig['type'],
151
            [
152
                'notnull' => false,
153
                OroOptions::KEY => array_merge(
154
                    [
155
                        ExtendOptionsManager::MODE_OPTION => $fieldConfig['mode']
156
                    ],
157
                    $fieldConfig['options']
158
                )
159
            ]
160
        );
161
162
        return $hasSchemaChanges;
163
    }
164
165
    /**
166
     * Run schema related SQLs manually because this query run when diff is already calculated by schema tool
167
     *
168
     * @param LoggerInterface $logger
169
     * @param bool $dryRun
170
     * @param bool $hasSchemaChanges
171
     * @param Schema $toSchema
172
     * @throws \Doctrine\DBAL\DBALException
173
     */
174
    protected function runSchemaRelated(LoggerInterface $logger, $dryRun, $hasSchemaChanges, $toSchema)
175
    {
176
        if ($hasSchemaChanges) {
177
            $comparator = new Comparator();
178
            $platform = $this->connection->getDatabasePlatform();
179
            $schemaDiff = $comparator->compare($this->schema, $toSchema);
180
            foreach ($schemaDiff->toSql($platform) as $query) {
181
                $this->logQuery($logger, $query);
182
                if (!$dryRun) {
183
                    $this->connection->query($query);
184
                }
185
            }
186
        }
187
    }
188
189
    /**
190
     * @param array $config
191
     * @param array $contactingActivityClasses
192
     * @return bool
193
     */
194
    protected function canAddField($config, $contactingActivityClasses)
195
    {
196
        return isset($config['extend']['is_extend'], $config['activity']['activities'])
197
        && $config['extend']['is_extend'] == true
198
        && $config['activity']['activities']
199
        && array_intersect($contactingActivityClasses, $config['activity']['activities']);
200
    }
201
202
    /**
203
     * @param array $tableColumns
204
     * @return bool
205
     */
206
    protected function hasEntityNeededColumns($tableColumns)
207
    {
208
        return false === (bool)array_diff(
209
            array_keys(ActivityScope::$fieldsConfiguration),
210
            array_intersect(array_keys($tableColumns), array_keys(ActivityScope::$fieldsConfiguration))
211
        );
212
    }
213
214
    /**
215
     * @param $config
216
     * @param $entityClassName
217
     *
218
     * @return string
219
     */
220
    protected function getTableName($config, $entityClassName)
221
    {
222
        if (isset($config['extend']['schema']['doctrine'][$entityClassName]['table'])) {
223
            $tableName = $config['extend']['schema']['doctrine'][$entityClassName]['table'];
224
        } else {
225
            $tableName = $this->metadataHelper->getTableNameByEntityClass($entityClassName);
226
        }
227
        return $tableName;
228
    }
229
}
230