Passed
Push — main ( 2a1ce4...db05ee )
by Gaetano
09:12
created

RepositoryExecutor   A

Complexity

Total Complexity 42

Size/Duplication

Total Lines 312
Duplicated Lines 0 %

Test Coverage

Coverage 94.62%

Importance

Changes 3
Bugs 2 Features 0
Metric Value
eloc 94
dl 0
loc 312
ccs 88
cts 93
cp 0.9462
rs 9.0399
c 3
b 2
f 0
wmc 42

14 Methods

Rating   Name   Duplication   Size   Complexity  
A setReferenceResolver() 0 3 1
A setConfigResolver() 0 3 1
A setRepository() 0 3 1
A execute() 0 31 4
A getLanguageCode() 0 3 2
A getUserGroupContentTypeFromContext() 0 3 2
A setScalarReferences() 0 23 4
C setReferences() 0 65 14
A getUserContentTypeFromContext() 0 3 2
A getUserContentType() 0 3 2
A isScalarReference() 0 3 1
A getAdminUserIdentifierFromContext() 0 7 2
A getLanguageCodeFromContext() 0 12 4
A getUserGroupContentType() 0 3 2

How to fix   Complexity   

Complex Class

Complex classes like RepositoryExecutor often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use RepositoryExecutor, and based on these observations, apply Extract Interface, too.

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