Passed
Push — master ( d6ce84...9c42e9 )
by Gaetano
10:07
created

RepositoryExecutor::allowEmptyReferences()   A

Complexity

Conditions 5
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 4
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 7
ccs 4
cts 4
cp 1
crap 5
rs 9.6111
1
<?php
2
3
namespace Kaliop\eZMigrationBundle\Core\Executor;
4
5
use eZ\Publish\API\Repository\Repository;
6
use eZ\Publish\Core\MVC\ConfigResolverInterface;
7
use Kaliop\eZMigrationBundle\API\Collection\AbstractCollection;
8
use Kaliop\eZMigrationBundle\API\ReferenceResolverBagInterface;
9
use Kaliop\eZMigrationBundle\API\Value\MigrationStep;
10
use Kaliop\eZMigrationBundle\Core\RepositoryUserSetterTrait;
11
12
/**
13
 * The core manager class that all migration action managers inherit from.
14
 */
15
abstract class RepositoryExecutor extends AbstractExecutor
16
{
17
    use RepositoryUserSetterTrait;
0 ignored issues
show
introduced by
The trait Kaliop\eZMigrationBundle...positoryUserSetterTrait requires some properties which are not provided by Kaliop\eZMigrationBundle...utor\RepositoryExecutor: $id, $login
Loading history...
18
    use IgnorableStepExecutorTrait;
0 ignored issues
show
Bug introduced by
The trait Kaliop\eZMigrationBundle...orableStepExecutorTrait requires the property $dsl which is not provided by Kaliop\eZMigrationBundle...utor\RepositoryExecutor.
Loading history...
19
    use NonScalarReferenceSetterTrait;
0 ignored issues
show
Bug introduced by
The trait Kaliop\eZMigrationBundle...larReferenceSetterTrait requires the property $dsl which is not provided by Kaliop\eZMigrationBundle...utor\RepositoryExecutor.
Loading history...
20
21
    protected $scalarReferences = array('count');
22
23
    /**
24
     * Constant defining the default language code (used if not specified by the migration or on the command line)
25
     */
26
    const DEFAULT_LANGUAGE_CODE = 'eng-GB';
27
28
    /**
29
     * The default Admin user Id, used when no Admin user is specified
30
     */
31
    const ADMIN_USER_ID = 14;
32
33
    /** Used if not specified by the migration */
34
    const USER_CONTENT_TYPE = 'user';
35
    /** Used if not specified by the migration */
36
    const USERGROUP_CONTENT_TYPE = 'user_group';
37
38
    /**
39
     * @var array $dsl The parsed DSL instruction array
40
     */
41
    //protected $dsl;
42
43
    /** @var array $context The context (configuration) for the execution of the current step */
44
    //protected $context;
45
46
    /**
47
     * The eZ Publish 5 API repository.
48
     *
49
     * @var \eZ\Publish\API\Repository\Repository
50
     */
51
    protected $repository;
52
53
    protected $configResolver;
54
55
    /** @var ReferenceResolverBagInterface $referenceResolver */
56
    protected $referenceResolver;
57
58
    // to redefine in subclasses if they don't support all methods, or if they support more...
59
    protected $supportedActions = array(
60
        'create', 'update', 'delete'
61 96
    );
62
63 96
    public function setRepository(Repository $repository)
64 96
    {
65
        $this->repository = $repository;
66 96
    }
67
68 96
    public function setConfigResolver(ConfigResolverInterface $configResolver)
69 96
    {
70
        $this->configResolver = $configResolver;
71 96
    }
72
73 96
    public function setReferenceResolver(ReferenceResolverBagInterface $referenceResolver)
74 96
    {
75
        $this->referenceResolver = $referenceResolver;
76 24
    }
77
78
    public function execute(MigrationStep $step)
79 24
    {
80
        // base checks
81 24
        parent::execute($step);
82
83
        if (!isset($step->dsl['mode'])) {
84
            throw new \Exception("Invalid step definition: missing 'mode'");
85
        }
86 24
87
        // q: should we convert snake_case to camelCase ?
88 24
        $action = $step->dsl['mode'];
89
90
        if (!in_array($action, $this->supportedActions)) {
91
            throw new \Exception("Invalid step definition: value '$action' is not allowed for 'mode'");
92 24
        }
93
94 24
        if (method_exists($this, $action)) {
95
96 24
            $this->skipStepIfNeeded($step);
97
98
            $previousUserId = $this->loginUser($this->getAdminUserIdentifierFromContext($step->context));
99 24
100 6
            try {
101 6
                $output = $this->$action($step);
102 6
            } catch (\Exception $e) {
103
                $this->loginUser($previousUserId);
104
                throw $e;
105
            }
106 22
107
            // reset the environment as much as possible as we had found it before the migration
108 22
            $this->loginUser($previousUserId);
109
110
            return $output;
111
        } else {
112
            throw new \Exception("Invalid step definition: value '$action' is not a method of " . get_class($this));
113
        }
114
    }
115
116
    /**
117
     * Method that each executor (subclass) has to implement.
118
     *
119
     * It is used to get values for references based on the DSL instructions executed in the current step, for later steps to reuse.
120
     *
121
     * @throws \InvalidArgumentException when trying to set a reference to an unsupported attribute.
122
     * @param mixed $object a single element to extract reference values from
123
     * @param array $referencesDefinitions the definitions of the references to extract
124
     * @param MigrationStep $step
125
     * @return array key: the reference name (taken from $referencesDefinitions[n]['identifier'], value: the ref. value
126
     */
127
    abstract protected function getReferencesValues($object, array $referencesDefinitions, $step);
128
129
    /**
130
     * @param MigrationStep $step
131 17
     * @return string
132
     */
133 17
    protected function getLanguageCode($step)
134
    {
135
        return isset($step->dsl['lang']) ? $step->dsl['lang'] : $this->getLanguageCodeFromContext($step->context);
136
    }
137
138
    /**
139
     * @param array|null $context
140 22
     * @return string
141
     */
142 22
    protected function getLanguageCodeFromContext($context)
143 1
    {
144
        if (is_array($context) && isset($context['defaultLanguageCode'])) {
145
            return $context['defaultLanguageCode'];
146 21
        }
147 21
148 21
        if ($this->configResolver) {
149
            $locales = $this->configResolver->getParameter('languages');
150
            return reset($locales);
151
        }
152
153
        return self::DEFAULT_LANGUAGE_CODE;
154
    }
155
156
    /**
157
     * @param MigrationStep $step
158 2
     * @return string
159
     */
160 2
    protected function getUserContentType($step)
161
    {
162
        return isset($step->dsl['user_content_type']) ? $this->referenceResolver->resolveReference($step->dsl['user_content_type']) : $this->getUserContentTypeFromContext($step->context);
163
    }
164
165
    /**
166
     * @param MigrationStep $step
167 2
     * @return string
168
     */
169 2
    protected function getUserGroupContentType($step)
170
    {
171
        return isset($step->dsl['usergroup_content_type']) ? $this->referenceResolver->resolveReference($step->dsl['usergroup_content_type']) : $this->getUserGroupContentTypeFromContext($step->context);
172
    }
173
174
    /**
175
     * @param array $context
176 52
     * @return string
177
     */
178 52
    protected function getUserContentTypeFromContext($context)
179
    {
180
        return isset($context['userContentType']) ? $context['userContentType'] : self::USER_CONTENT_TYPE;
181
    }
182 52
183
    /**
184
     * @param array $context
185
     * @return string
186
     */
187
    protected function getUserGroupContentTypeFromContext($context)
188
    {
189
        return isset($context['userGroupContentType']) ? $context['userGroupContentType'] : self::USERGROUP_CONTENT_TYPE;
190
    }
191
192
    /**
193
     * @param array $context we have to return FALSE if that is set as adminUserLogin, whereas if NULL is set, we return the default admin
194 23
     * @return int|string|false
195
     */
196 23
    protected function getAdminUserIdentifierFromContext($context)
197 20
    {
198
        if (isset($context['adminUserLogin'])) {
199
            return $context['adminUserLogin'];
200 18
        }
201
202 18
        return self::ADMIN_USER_ID;
203
    }
204 17
205
    /**
206 17
     * Sets references to certain attributes of the items returned by steps.
207 12
     *
208
     * @param \Object|AbstractCollection $item
0 ignored issues
show
Bug introduced by
The type Object was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
209 16
     * @param MigrationStep $step
210
     * @throws \InvalidArgumentException When trying to set a reference to an unsupported attribute
211
     * @return boolean
212 17
     * @todo should we allow to be passed in plain arrays as well as Collections?
213 17
     */
214 17
    protected function setReferences($item, $step)
215 17
    {
216 16
        if (!array_key_exists('references', $step->dsl)) {
217
            return false;
218 1
        }
219 1
220 1
        $referencesDefs = $this->setScalarReferences($item, $step->dsl['references']);
221
222 17
        // this check is now done immediately after matching
223
        //$this->insureResultsCountCompatibility($item, $referencesDefs, $step);
224
225
        $multivalued = ($this->getResultsType($step) == self::$RESULT_TYPE_MULTIPLE);
226
227
        if ($item instanceof AbstractCollection  || is_array($item)) {
228 17
            $items = $item;
229 17
        } else {
230 17
            $items = array($item);
231 1
        }
232
233 17
        $referencesValues = array();
234
        foreach ($items as $item) {
0 ignored issues
show
introduced by
$item is overwriting one of the parameters of this function.
Loading history...
235
            $itemReferencesValues = $this->getReferencesValues($item, $referencesDefs, $step);
236 17
            if (!$multivalued) {
237
                $referencesValues = $itemReferencesValues;
238
            } else {
239
                foreach ($itemReferencesValues as $refName => $refValue) {
240
                    if (!isset($referencesValues[$refName])) {
241
                        $referencesValues[$refName] = array($refValue);
242
                    } else {
243
                        $referencesValues[$refName][] = $refValue;
244 18
                    }
245
                }
246
            }
247 18
        }
248 18
249
        foreach ($referencesDefs as $reference) {
250 18
            $overwrite = false;
251 4
            if (isset($reference['overwrite'])) {
252 4
                $overwrite = $reference['overwrite'];
253 4
            }
254 1
            // q: is the usage of count() and array() correct here ? esp for the case we want scalar refs ?
255
            $this->referenceResolver->addReference($reference['identifier'], count($referencesValues) ? $referencesValues[$reference['identifier']] : array(), $overwrite);
256 4
        }
257 4
258 4
        return true;
259
    }
260 18
261
    /**
262
     * @param mixed $entity
263
     * @param array $referencesDefinition
264
     * @return array the same as $referencesDefinition, with the references already treated having been removed
265 18
     */
266
    protected function setScalarReferences($entity, $referencesDefinition)
267
    {
268
        // allow setting *some* refs even when we have 0 or N matches
269
        foreach ($referencesDefinition as $key => $reference) {
270
            switch($reference['attribute']) {
271
272
                case 'count':
273
                    $value = count($entity);
274
                    $overwrite = false;
275
                    if (isset($reference['overwrite'])) {
276
                        $overwrite = $reference['overwrite'];
277
                    }
278
                    $this->referenceResolver->addReference($reference['identifier'], $value, $overwrite);
279
                    unset($referencesDefinition[$key]);
280
                    break;
281
282
                default:
283
                    // do nothing
284
            }
285
        }
286
287
        return $referencesDefinition;
288
    }
289
290
    /**
291
     * Verifies compatibility between the definition of the references to be set and the data set to extract them from,
292
     * and returns a single entity
293
     *
294
     * @param AbstractCollection|mixed $entity
295
     * @param array $referencesDefinition
296
     * @param MigrationStep $step
297
     * @return AbstractCollection|mixed
298 18
     * @deprecated use validateResultsCount instead
299
     */
300 18
    /*protected function insureSingleEntity($entity, $referencesDefinition, $step)
301
    {
302 13
        $this->insureResultsCountCompatibility($entity, $referencesDefinition, $step);
303 13
304
        if ($entity instanceof AbstractCollection) {
305 13
            return $entity->reset();
306 1
        }
307
308 12
        return $entity;
309
    }*/
310
311
    /**
312 17
     * Courtesy code to avoid reimplementing it in every subclass
313
     * @todo will be moved into the reference resolver classes
314
     */
315
    protected function resolveReferencesRecursively($match)
316
    {
317
        if (is_array($match)) {
318 18
            foreach ($match as $condition => $values) {
319
                $match[$condition] = $this->resolveReferencesRecursively($values);
320 18
            }
321
            return $match;
322
        } else {
323
            return $this->referenceResolver->resolveReference($match);
324
        }
325
    }
326
327 13
    /**
328
     * @param array $referenceDefinition
329 13
     * @return bool
330 13
     */
331
    protected function isScalarReference($referenceDefinition)
332
    {
333 13
        return in_array($referenceDefinition['attribute'], $this->scalarReferences);
334
    }
335
}
336