Completed
Push — master ( 172340...b8107c )
by Gaetano
09:56
created

RepositoryExecutor   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 254
Duplicated Lines 6.69 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 34
c 0
b 0
f 0
lcom 1
cbo 6
dl 17
loc 254
rs 9.2
ccs 0
cts 115
cp 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
A setRepository() 0 4 1
A setConfigResolver() 0 4 1
A setReferenceResolver() 0 4 1
B execute() 0 37 5
setReferences() 0 1 ?
A getLanguageCode() 0 4 2
A getLanguageCodeFromContext() 0 13 4
A getUserContentType() 0 4 2
A getUserContentTypeFromContext() 0 4 2
A getAdminUserIdentifierFromContext() 0 8 2
B setReferencesCommon() 0 23 4
B insureSingleEntity() 6 20 5
A getSelfName() 0 8 1
A getCollectionName() 0 8 1
A resolveReferencesRecursively() 11 11 3

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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;
18
    use IgnorableStepExecutorTrait;
19
20
    /**
21
     * Constant defining the default language code (used if not specified by the migration or on the command line)
22
     */
23
    const DEFAULT_LANGUAGE_CODE = 'eng-GB';
24
25
    /**
26
     * The default Admin user Id, used when no Admin user is specified
27
     */
28
    const ADMIN_USER_ID = 14;
29
30
    /** Used if not specified by the migration */
31
    const USER_CONTENT_TYPE = 'user';
32
33
    /**
34
     * @var array $dsl The parsed DSL instruction array
35
     */
36
    //protected $dsl;
37
38
    /** @var array $context The context (configuration) for the execution of the current step */
39
    //protected $context;
40
41
    /**
42
     * The eZ Publish 5 API repository.
43
     *
44
     * @var \eZ\Publish\API\Repository\Repository
45
     */
46
    protected $repository;
47
48
    protected $configResolver;
49
50
    /** @var ReferenceResolverBagInterface $referenceResolver */
51
    protected $referenceResolver;
52
53
    // to redefine in subclasses if they don't support all methods, or if they support more...
54
    protected $supportedActions = array(
55
        'create', 'update', 'delete'
56
    );
57
58
    public function setRepository(Repository $repository)
59
    {
60
        $this->repository = $repository;
61
    }
62
63
    public function setConfigResolver(ConfigResolverInterface $configResolver)
64
    {
65
        $this->configResolver = $configResolver;
66
    }
67
68
    public function setReferenceResolver(ReferenceResolverBagInterface $referenceResolver)
69
    {
70
        $this->referenceResolver = $referenceResolver;
71
    }
72
73
    public function execute(MigrationStep $step)
74
    {
75
        // base checks
76
        parent::execute($step);
77
78
        if (!isset($step->dsl['mode'])) {
79
            throw new \Exception("Invalid step definition: missing 'mode'");
80
        }
81
82
        // q: should we convert snake_case to camelCase ?
83
        $action = $step->dsl['mode'];
84
85
        if (!in_array($action, $this->supportedActions)) {
86
            throw new \Exception("Invalid step definition: value '$action' is not allowed for 'mode'");
87
        }
88
89
        if (method_exists($this, $action)) {
90
91
            $this->skipStepIfNeeded($step);
92
93
            $previousUserId = $this->loginUser($this->getAdminUserIdentifierFromContext($step->context));
94
95
            try {
96
                $output = $this->$action($step);
97
            } catch (\Exception $e) {
98
                $this->loginUser($previousUserId);
99
                throw $e;
100
            }
101
102
            // reset the environment as much as possible as we had found it before the migration
103
            $this->loginUser($previousUserId);
104
105
            return $output;
106
        } else {
107
            throw new \Exception("Invalid step definition: value '$action' is not a method of " . get_class($this));
108
        }
109
    }
110
111
    /**
112
     * Method that each executor (subclass) has to implement.
113
     *
114
     * It is used to set references based on the DSL instructions executed in the current step, for later steps to reuse.
115
     *
116
     * @throws \InvalidArgumentException when trying to set a reference to an unsupported attribute.
117
     * @param $object
118
     * @return boolean
119
     */
120
    abstract protected function setReferences($object, $step);
121
122
    /**
123
     * @param MigrationStep $step
124
     * @return string
125
     */
126
    protected function getLanguageCode($step)
127
    {
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
     */
135
    protected function getLanguageCodeFromContext($context)
136
    {
137
        if (is_array($context) && isset($context['defaultLanguageCode'])) {
138
            return $context['defaultLanguageCode'];
139
        }
140
141
        if ($this->configResolver) {
142
            $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
     */
153
    protected function getUserContentType($step)
154
    {
155
        return isset($step->dsl['user_content_type']) ? $step->dsl['user_content_type'] : $this->getUserContentTypeFromContext($step->context);
156
    }
157
158
    /**
159
     * @param array $context
160
     * @return string
161
     */
162
    protected function getUserContentTypeFromContext($context)
163
    {
164
        return isset($context['userContentType']) ? $context['userContentType'] : self::USER_CONTENT_TYPE;
165
    }
166
167
    /**
168
     * @param array $context we have to return FALSE if that is set as adminUserLogin, whereas if NULL is set, we return the default admin
169
     * @return int|string|false
170
     */
171
    protected function getAdminUserIdentifierFromContext($context)
172
    {
173
        if (isset($context['adminUserLogin'])) {
174
            return $context['adminUserLogin'];
175
        }
176
177
        return self::ADMIN_USER_ID;
178
    }
179
180
    /**
181
     * @param mixed $entity
182
     * @param array $referencesDefinition
183
     * @return array the same as $referencesDefinition, with the references already treated having been removed
184
     */
185
    protected function setReferencesCommon($entity, $referencesDefinition)
186
    {
187
        // allow setting *some* refs even when we have 0 or N matches
188
        foreach ($referencesDefinition as $key => $reference) {
189
            switch($reference['attribute']) {
190
191
                case 'count':
192
                    $value = count($entity);
193
                    $overwrite = false;
194
                    if (isset($reference['overwrite'])) {
195
                        $overwrite = $reference['overwrite'];
196
                    }
197
                    $this->referenceResolver->addReference($reference['identifier'], $value, $overwrite);
198
                    unset($referencesDefinition[$key]);
199
                    break;
200
201
                default:
202
                    // do nothing
203
            }
204
        }
205
206
        return $referencesDefinition;
207
    }
208
209
    /**
210
     * @param AbstractCollection|mixed $entity
211
     * @param array $referencesDefinition
212
     * @return AbstractCollection|mixed
213
     */
214
    protected function insureSingleEntity($entity, $referencesDefinition)
215
    {
216
        if ($entity instanceof AbstractCollection) {
217
218
            $needSingleRef = (count($referencesDefinition) > 0);
219
220
            if ($needSingleRef) {
221 View Code Duplication
                if (count($entity) > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
222
                    throw new \InvalidArgumentException($this->getSelfName() . ' does not support setting references for multiple ' . $this->getCollectionName($entity) . 's');
223
                }
224 View Code Duplication
                if (count($entity) == 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
225
                    throw new \InvalidArgumentException($this->getSelfName() . ' does not support setting references for no ' . $this->getCollectionName($entity). 's');
226
                }
227
            }
228
229
            $entity = reset($entity);
230
        }
231
232
        return $entity;
233
    }
234
235
    protected function getSelfName()
236
    {
237
        $className = get_class($this);
238
        $array = explode('\\', $className);
239
        $className = end($array);
240
        // CamelCase to Camel Case using negative look-behind in regexp
241
        return preg_replace('/(?<!^)[A-Z]/', ' $0', $className);
242
    }
243
244
    protected function getCollectionName($collection)
245
    {
246
        $className = get_class($collection);
247
        $array = explode('\\', $className);
248
        $className = str_replace('Collection', '', end($array));
249
        // CamelCase to snake case using negative look-behind in regexp
250
        return strtolower(preg_replace('/(?<!^)[A-Z]/', ' $0', $className));
251
    }
252
253
    /**
254
     * Courtesy code to avoid reimplementing it in every subclass
255
     * @todo will be moved into the reference resolver classes
256
     */
257 View Code Duplication
    protected function resolveReferencesRecursively($match)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
258
    {
259
        if (is_array($match)) {
260
            foreach ($match as $condition => $values) {
261
                $match[$condition] = $this->resolveReferencesRecursively($values);
262
            }
263
            return $match;
264
        } else {
265
            return $this->referenceResolver->resolveReference($match);
266
        }
267
    }
268
}
269