Passed
Push — main ( 2bf33d...adc87c )
by Gaetano
08:33
created

RepositoryExecutor::setReferences()   C

Complexity

Conditions 14
Paths 72

Size

Total Lines 65
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 32
CRAP Score 14

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 14
eloc 34
c 2
b 1
f 0
nc 72
nop 2
dl 0
loc 65
ccs 32
cts 32
cp 1
crap 14
rs 6.2666

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Exception\InvalidStepDefinitionException;
9
use Kaliop\eZMigrationBundle\API\Exception\MigrationBundleException;
10
use Kaliop\eZMigrationBundle\API\ReferenceResolverBagInterface;
11
use Kaliop\eZMigrationBundle\API\Value\MigrationStep;
12
use Kaliop\eZMigrationBundle\Core\RepositoryUserSetterTrait;
13
14
/**
15
 * The core manager class that all migration action managers inherit from.
16
 */
17
abstract class RepositoryExecutor extends AbstractExecutor
18
{
19
    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...
20
    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...
21
    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...
22
23
    protected $scalarReferences = array('count');
24
25
    /**
26
     * Constant defining the default language code (used if not specified by the migration or on the command line)
27
     */
28
    const DEFAULT_LANGUAGE_CODE = 'eng-GB';
29
30
    /**
31
     * The default Admin user Id, used when no Admin user is specified
32
     */
33
    const ADMIN_USER_ID = 14;
34
35
    /** Used if not specified by the migration */
36
    const USER_CONTENT_TYPE = 'user';
37
    /** Used if not specified by the migration */
38
    const USERGROUP_CONTENT_TYPE = 'user_group';
39
40
    /**
41
     * The eZ Publish 5 API repository.
42
     *
43
     * @var \eZ\Publish\API\Repository\Repository
44
     */
45
    protected $repository;
46
47
    protected $configResolver;
48
49
    /** @var ReferenceResolverBagInterface $referenceResolver */
50
    protected $referenceResolver;
51
52
    // to redefine in subclasses if they don't support all methods, or if they support more...
53
    protected $supportedActions = array(
54
        'create', 'update', 'delete'
55
    );
56 149
57
    public function setRepository(Repository $repository)
58 149
    {
59 149
        $this->repository = $repository;
60
    }
61 149
62
    public function setConfigResolver(ConfigResolverInterface $configResolver)
63 149
    {
64 149
        $this->configResolver = $configResolver;
65
    }
66 149
67
    public function setReferenceResolver(ReferenceResolverBagInterface $referenceResolver)
68 149
    {
69 149
        $this->referenceResolver = $referenceResolver;
70
    }
71 43
72
    public function execute(MigrationStep $step)
73
    {
74 43
        // base checks
75
        parent::execute($step);
76 43
77
        if (!isset($step->dsl['mode'])) {
78
            throw new InvalidStepDefinitionException("Invalid step definition: missing 'mode'");
79
        }
80
81 43
        // q: should we convert snake_case to camelCase ?
82
        $action = $step->dsl['mode'];
83 43
84
        if (!in_array($action, $this->supportedActions)) {
85
            throw new InvalidStepDefinitionException("Invalid step definition: value '$action' is not allowed for 'mode'");
86
        }
87 43
88
        if (!method_exists($this, $action)) {
89
            throw new InvalidStepDefinitionException("Invalid step definition: value '$action' is not a method of " . get_class($this));
90
        }
91 43
92
        $this->skipStepIfNeeded($step);
93 43
94
        $previousUserId = $this->loginUser($this->getAdminUserIdentifierFromContext($step->context));
95
96 42
        try {
97 15
            $output = $this->$action($step);
98 15
        } catch (\Exception $e) {
99 15
            $this->loginUser($previousUserId);
100
            throw $e;
101
        }
102
103 31
        // reset the environment as much as possible as we had found it before the migration
104
        $this->loginUser($previousUserId);
105 31
106
        return $output;
107
    }
108
109
    /**
110
     * Method that each executor (subclass) has to implement.
111
     *
112
     * It is used to get values for references based on the DSL instructions executed in the current step, for later steps to reuse.
113
     *
114
     * @throws \InvalidArgumentException when trying to set a reference to an unsupported attribute.
115
     * @param mixed $object a single element to extract reference values from
116
     * @param array $referencesDefinitions the definitions of the references to extract
117
     * @param MigrationStep $step
118
     * @return array key: the reference name (taken from $referencesDefinitions[n]['identifier'], value: the ref. value
119
     */
120
    abstract protected function getReferencesValues($object, array $referencesDefinitions, $step);
121
122
    /**
123
     * @param MigrationStep $step
124
     * @return string
125 21
     */
126
    protected function getLanguageCode($step)
127 21
    {
128
        return isset($step->dsl['lang']) ? $step->dsl['lang'] : $this->getLanguageCodeFromContext($step->context);
129
    }
130
131
    /**
132
     * @param array|null $context
133
     * @return string
134 27
     */
135
    protected function getLanguageCodeFromContext($context)
136 27
    {
137 1
        if (is_array($context) && isset($context['defaultLanguageCode'])) {
138
            return $context['defaultLanguageCode'];
139
        }
140 26
141 26
        if ($this->configResolver) {
142 26
            $locales = $this->configResolver->getParameter('languages');
143
            return reset($locales);
144
        }
145
146
        return self::DEFAULT_LANGUAGE_CODE;
147
    }
148
149
    /**
150
     * @param MigrationStep $step
151
     * @return string
152 3
     */
153
    protected function getUserContentType($step)
154 3
    {
155
        return isset($step->dsl['user_content_type']) ? $this->referenceResolver->resolveReference($step->dsl['user_content_type']) : $this->getUserContentTypeFromContext($step->context);
156
    }
157
158
    /**
159
     * @param MigrationStep $step
160
     * @return string
161 2
     */
162
    protected function getUserGroupContentType($step)
163 2
    {
164
        return isset($step->dsl['usergroup_content_type']) ? $this->referenceResolver->resolveReference($step->dsl['usergroup_content_type']) : $this->getUserGroupContentTypeFromContext($step->context);
165
    }
166
167
    /**
168
     * @param array $context
169
     * @return string
170 3
     */
171
    protected function getUserContentTypeFromContext($context)
172 3
    {
173
        return isset($context['userContentType']) ? $context['userContentType'] : self::USER_CONTENT_TYPE;
174
    }
175
176
    /**
177
     * @param array $context
178
     * @return string
179 2
     */
180
    protected function getUserGroupContentTypeFromContext($context)
181 2
    {
182
        return isset($context['userGroupContentType']) ? $context['userGroupContentType'] : self::USERGROUP_CONTENT_TYPE;
183
    }
184
185
    /**
186
     * @param array $context we have to return FALSE if that is set as adminUserLogin, whereas if NULL is set, we return the default admin
187
     * @return int|string|false
188 77
     */
189
    protected function getAdminUserIdentifierFromContext($context)
190 77
    {
191 1
        if (isset($context['adminUserLogin'])) {
192
            return $context['adminUserLogin'];
193
        }
194 76
195
        return self::ADMIN_USER_ID;
196
    }
197
198
    /**
199
     * Sets references to certain attributes of the items returned by steps.
200
     * Should be called after validateResultsCount in cases where one or many items could be used to get reference values from
201
     *
202
     * @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...
203
     * @param MigrationStep $step
204
     * @return boolean
205
     * @throws \InvalidArgumentException When trying to set a reference to an unsupported attribute
206 31
     * @todo should we allow to be passed in plain arrays, ArrayIterators and ObjectIterators as well as Collections?
207
     * @todo move to NonScalarReferenceSetterTrait?
208 31
     */
209 26
    protected function setReferences($item, $step)
210
    {
211
        if (!array_key_exists('references', $step->dsl) || !count($step->dsl['references'])) {
212 24
            return false;
213
        }
214
215
        $referencesDefs = $this->setScalarReferences($item, $step->dsl['references']);
216
217 24
        if (!count($referencesDefs)) {
218
            // Save some cpu and return early.
219 24
            // We return true because some scalar ref was set, or we would have exited at the top of the method
220 17
            return true;
221
        }
222 19
223
        // this check is now done immediately after matching
224
        //$this->insureResultsCountCompatibility($item, $referencesDefs, $step);
225 24
226 24
        // NB: there is no valid yml atm which could result in expectedResultsType returning 'unspecified' here, but
227 23
        // we should take care in case this changes in the future
228 23
        $multivalued = ($this->expectedResultsType($step) == self::$RESULT_TYPE_MULTIPLE);
229 20
230
        if ($item instanceof AbstractCollection || is_array($item)) {
231 8
            $items = $item;
232 1
        } else {
233 1
            $items = array($item);
234
        }
235 1
236
        $referencesValues = array();
237
        foreach ($items as $item) {
0 ignored issues
show
introduced by
$item is overwriting one of the parameters of this function.
Loading history...
238
            $itemReferencesValues = $this->getReferencesValues($item, $referencesDefs, $step);
239
            if (!$multivalued) {
240
                // $itemReferencesValues will be an array with key=refName
241 24
                $referencesValues = $itemReferencesValues;
242 21
            } else {
243 21
                foreach ($itemReferencesValues as $refName => $refValue) {
244 21
                    if (!isset($referencesValues[$refName])) {
245 2
                        $referencesValues[$refName] = array($refValue);
246
                    } else {
247
                        $referencesValues[$refName][] = $refValue;
248 21
                    }
249
                }
250
            }
251 24
        }
252
253
        foreach ($referencesDefs as $key => $reference) {
254
            $reference = $this->parseReferenceDefinition($key, $reference);
255
            $overwrite = false;
256
            if (isset($reference['overwrite'])) {
257
                $overwrite = $reference['overwrite'];
258
            }
259
            $referenceIdentifier = $reference['identifier'];
260 24
            if (!array_key_exists($referenceIdentifier, $referencesValues)) {
261
                if (count($items)) {
262
                    // internal error
263 24
                    throw new MigrationBundleException("Interanl error: getReferencesValues did not return reference '$referenceIdentifier' in class " . get_class($this));
264 24
                } else {
265 24
                    // we "know" this ia a request for multivalued refs. If we were expecting only one item, and got none,
266
                    // an exception would have been thrown before reaching here
267 24
                    $referencesValues[$referenceIdentifier] = array();
268 10
                }
269 10
            }
270 10
            $this->referenceResolver->addReference($referenceIdentifier, $referencesValues[$referenceIdentifier], $overwrite);
271 1
        }
272
273 10
        return true;
274 10
    }
275 10
276
    /**
277
     * @param mixed $entity
278
     * @param array $referencesDefinition
279
     * @return array the same as $referencesDefinition, with the references already treated having been removed
280
     * @throws InvalidStepDefinitionException
281
     */
282 24
    protected function setScalarReferences($entity, $referencesDefinition)
283
    {
284
        // allow setting *some* refs even when we have 0 or N matches
285
        foreach ($referencesDefinition as $key => $reference) {
286
            $reference = $this->parseReferenceDefinition($key, $reference);
287
            switch ($reference['attribute']) {
288
289
                case 'count':
290
                    $value = count($entity);
291
                    $overwrite = false;
292
                    if (isset($reference['overwrite'])) {
293
                        $overwrite = $reference['overwrite'];
294
                    }
295
                    $this->referenceResolver->addReference($reference['identifier'], $value, $overwrite);
296
                    unset($referencesDefinition[$key]);
297
                    break;
298
299
                default:
300
                    // do nothing
301
            }
302
        }
303
304
        return $referencesDefinition;
305
    }
306
307
    /**
308
     * Verifies compatibility between the definition of the references to be set and the data set to extract them from,
309
     * and returns a single entity
310 36
     *
311
     * @param AbstractCollection|mixed $entity
312 36
     * @param array $referencesDefinition
313 36
     * @param MigrationStep $step
314 36
     * @return AbstractCollection|mixed
315
     * @deprecated use validateResultsCount instead
316 35
     */
317
    /*protected function insureSingleEntity($entity, $referencesDefinition, $step)
318 36
    {
319
        $this->insureResultsCountCompatibility($entity, $referencesDefinition, $step);
320
321
        if ($entity instanceof AbstractCollection) {
322
            return $entity->reset();
323
        }
324
325
        return $entity;
326 25
    }*/
327
328 25
    /**
329
     * Courtesy code to avoid reimplementing it in every subclass
330
     * @todo will be moved into the reference resolver classes
331
     */
332
    protected function resolveReferencesRecursively($match)
333
    {
334
        if (is_array($match)) {
335
            foreach ($match as $condition => $values) {
336
                $match[$condition] = $this->resolveReferencesRecursively($values);
337
            }
338
            return $match;
339
        } else {
340
            return $this->referenceResolver->resolveReference($match);
341
        }
342
    }
343
344
    /**
345
     * @param array $referenceDefinition
346
     * @return bool
347
     */
348
    protected function isScalarReference($referenceDefinition)
349
    {
350
        return in_array($referenceDefinition['attribute'], $this->scalarReferences);
351
    }
352
}
353