Passed
Push — main ( f754fa...2a1ce4 )
by Gaetano
09:34
created

TagManager   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 360
Duplicated Lines 0 %

Test Coverage

Coverage 68.11%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 196
dl 0
loc 360
ccs 126
cts 185
cp 0.6811
rs 4.5599
c 1
b 0
f 0
wmc 58

11 Methods

Rating   Name   Duplication   Size   Complexity  
A matchTags() 0 12 3
A load() 0 11 1
A delete() 0 22 4
A __construct() 0 4 1
C getReferencesValues() 0 47 13
B update() 0 48 8
B create() 0 49 10
A checkTagsBundleInstall() 0 5 2
C generateMigration() 0 89 13
A getTagKeywords() 0 7 2
A listAllowedConditions() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like TagManager 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 TagManager, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Kaliop\eZMigrationBundle\Core\Executor;
4
5
use Kaliop\eZMigrationBundle\API\Collection\TagCollection;
6
use Kaliop\eZMigrationBundle\API\EnumerableMatcherInterface;
7
use Kaliop\eZMigrationBundle\API\Exception\InvalidMatchConditionsException;
8
use Kaliop\eZMigrationBundle\API\Exception\InvalidStepDefinitionException;
9
use Kaliop\eZMigrationBundle\API\Exception\MigrationBundleException;
10
use Kaliop\eZMigrationBundle\API\MigrationGeneratorInterface;
11
use Kaliop\eZMigrationBundle\Core\Matcher\TagMatcher;
12
use Netgen\TagsBundle\API\Repository\Values\Tags\Tag;
0 ignored issues
show
Bug introduced by
The type Netgen\TagsBundle\API\Repository\Values\Tags\Tag 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...
13
14
/**
15
 * Handles tag migrations.
16
 */
17
class TagManager extends RepositoryExecutor implements MigrationGeneratorInterface, EnumerableMatcherInterface
18
{
19
    protected $supportedStepTypes = array('tag');
20
    protected $supportedActions = array('create', 'load', 'update', 'delete');
21
22
    /** @var \Netgen\TagsBundle\API\Repository\TagsService $tagService */
0 ignored issues
show
Bug introduced by
The type Netgen\TagsBundle\API\Repository\TagsService 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...
23
    protected $tagService;
24
    /** @var TagMatcher $tagMatcher */
25
    protected $tagMatcher;
26
27
    /**
28
     * @param TagMatcher $tagMatcher
29
     * @param \Netgen\TagsBundle\API\Repository\TagsService $tagService
30
     */
31 149
    public function __construct(TagMatcher $tagMatcher, $tagService = null)
32
    {
33 149
        $this->tagMatcher = $tagMatcher;
34 149
        $this->tagService = $tagService;
35 149
    }
36
37
    /**
38
     * @return \Netgen\TagsBundle\API\Repository\Values\Tags\Tag
39
     * @throws \Exception
40
     * @todo if lang is not specified, use current language
41
     */
42 1
    protected function create($step)
43
    {
44 1
        $this->checkTagsBundleInstall();
45
46 1
        $alwaysAvail = isset($step->dsl['always_available']) ? $step->dsl['always_available'] : true;
47 1
        $parentTagId = 0;
48 1
        if (isset($step->dsl['parent_tag_id'])) {
49
            $parentTagId = $step->dsl['parent_tag_id'];
50
            $parentTagId = $this->resolveReference($parentTagId);
51
            // allow remote-ids to be used to reference parent tag
52
            if (!is_int($parentTagId) && !ctype_digit($parentTagId)) {
53
                $parentTag = $this->tagMatcher->matchOneByKey($parentTagId);
54
                $parentTagId = $parentTag->id;
55
            }
56
        }
57 1
        $remoteId = isset($step->dsl['remote_id']) ? $step->dsl['remote_id'] : null;
58
59 1
        if (isset($step->dsl['lang'])) {
60
            $lang = $step->dsl['lang'];
61 1
        } elseif (isset($step->dsl['main_language_code'])) {
62
            // deprecated tag
63 1
            $lang = $step->dsl['main_language_code'];
64
        } else {
65
            $lang = $this->getLanguageCode($step);
66
        }
67
68
        $tagCreateArray = array(
69 1
            'parentTagId' => $parentTagId,
70 1
            'mainLanguageCode' => $lang,
71 1
            'alwaysAvailable' => $alwaysAvail,
72 1
            'remoteId' => $remoteId
73
        );
74 1
        $tagCreateStruct = new \Netgen\TagsBundle\API\Repository\Values\Tags\TagCreateStruct($tagCreateArray);
0 ignored issues
show
Bug introduced by
The type Netgen\TagsBundle\API\Re...es\Tags\TagCreateStruct 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...
75
76 1
        if (is_string($step->dsl['keywords'])) {
77
            $keyword = $this->resolveReference($step->dsl['keywords']);
78
            $tagCreateStruct->setKeyword($keyword);
79
        } else {
80 1
            foreach ($step->dsl['keywords'] as $langCode => $keyword)
81
            {
82 1
                $keyword = $this->resolveReference($keyword);
83 1
                $tagCreateStruct->setKeyword($keyword, $langCode);
84
            }
85
        }
86
87 1
        $tag = $this->tagService->createTag($tagCreateStruct);
88 1
        $this->setReferences($tag, $step);
89
90 1
        return $tag;
91
    }
92
93 1
    protected function load($step)
94
    {
95 1
        $this->checkTagsBundleInstall();
96
97 1
        $tagsCollection = $this->matchTags('load', $step);
98
99 1
        $this->validateResultsCount($tagsCollection, $step);
100
101 1
        $this->setReferences($tagsCollection, $step);
102
103 1
        return $tagsCollection;
104
    }
105
106 1
    protected function update($step)
107
    {
108 1
        $this->checkTagsBundleInstall();
109
110 1
        $tagsCollection = $this->matchTags('update', $step);
111
112 1
        $this->validateResultsCount($tagsCollection, $step);
113
114 1
        foreach ($tagsCollection as $key => $tag) {
115
116 1
            $alwaysAvail = isset($step->dsl['always_available']) ? $step->dsl['always_available'] : true;
117
118 1
            $remoteId = isset($step->dsl['remote_id']) ? $step->dsl['remote_id'] : null;
119
120 1
            if (isset($step->dsl['lang'])) {
121
                $lang = $step->dsl['lang'];
122 1
            } elseif (isset($step->dsl['main_language_code'])) {
123
                // deprecated tag
124
                $lang = $step->dsl['main_language_code'];
125
            } else {
126 1
                $lang = $this->getLanguageCode($step);
127
            }
128
129
            $tagUpdateArray = array(
130 1
                'mainLanguageCode' => $lang,
131 1
                'alwaysAvailable' => $alwaysAvail,
132 1
                'remoteId' => $remoteId
133
            );
134 1
            $tagUpdateStruct = new \Netgen\TagsBundle\API\Repository\Values\Tags\TagUpdateStruct($tagUpdateArray);
0 ignored issues
show
Bug introduced by
The type Netgen\TagsBundle\API\Re...es\Tags\TagUpdateStruct 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...
135
136 1
            if (is_string($step->dsl['keywords'])) {
137
                $keyword = $this->resolveReference($step->dsl['keywords']);
138
                $tagUpdateStruct->setKeyword($keyword);
139
            } else {
140 1
                foreach ($step->dsl['keywords'] as $langCode => $keyword) {
141 1
                    $keyword = $this->resolveReference($keyword);
142 1
                    $tagUpdateStruct->setKeyword($keyword, $langCode);
143
                }
144
            }
145
146 1
            $tag = $this->tagService->updateTag($tag, $tagUpdateStruct);
147
148 1
            $tagsCollection[$key] = $tag;
149
        }
150
151 1
        $this->setReferences($tagsCollection, $step);
152
153 1
        return $tagsCollection;
154
    }
155
156 1
    protected function delete($step)
157
    {
158 1
        $this->checkTagsBundleInstall();
159
160 1
        $tagsCollection = $this->matchTags('delete', $step);
161
162 1
        $this->validateResultsCount($tagsCollection, $step);
163
164 1
        $this->setReferences($tagsCollection, $step);
165
166
        // sort tags by depth so that there will be no errors in case we are deleting parent and child
167 1
        $tagsCollection = $tagsCollection->getArrayCopy();
168
        uasort($tagsCollection, function ($t1, $t2) {
169
            if ($t1->depth == $t2->depth) return 0;
170
            return ($t1->depth > $t2->depth) ? -1 : 1;
171 1
        });
172
173 1
        foreach ($tagsCollection as $tag) {
174 1
            $this->tagService->deleteTag($tag);
175
        }
176
177 1
        return $tagsCollection;
178
    }
179
180
    /**
181
     * @param string $action
182
     * @return TagCollection
183
     * @throws \Exception
184
     */
185 1
    protected function matchTags($action, $step)
186
    {
187 1
        if (!isset($step->dsl['match'])) {
188
            throw new InvalidStepDefinitionException("A match condition is required to $action a Tag");
189
        }
190
191
        // convert the references passed in the match
192 1
        $match = $this->resolveReferencesRecursively($step->dsl['match']);
193
194 1
        $tolerateMisses = isset($step->dsl['match_tolerate_misses']) ? $this->resolveReference($step->dsl['match_tolerate_misses']) : false;
195
196 1
        return $this->tagMatcher->match($match, $tolerateMisses);
197
    }
198
199
    /**
200
     * @param Tag $tag
201
     * @param array $references the definitions of the references to set
202
     * @throws InvalidStepDefinitionException
203
     * @return array key: the reference names, values: the reference values
204
     */
205 1
    protected function getReferencesValues($tag, array $references, $step)
206
    {
207 1
        $lang = $this->getLanguageCode($step);
208 1
        $refs = array();
209
210 1
        foreach ($references as $key => $reference) {
211 1
            $reference = $this->parseReferenceDefinition($key, $reference);
212 1
            switch ($reference['attribute']) {
213 1
                case 'id':
214 1
                case 'tag_id':
215 1
                    $value = $tag->id;
216 1
                    break;
217 1
                case 'always_available':
218 1
                    $value = $tag->alwaysAvailable;
219 1
                    break;
220 1
                case 'depth':
221 1
                    $value = $tag->depth;
222 1
                    break;
223 1
                case 'keyword':
224 1
                    $value = $tag->getKeyword($lang);
225 1
                    break;
226 1
                case 'main_language_code':
227 1
                    $value = $tag->mainLanguageCode;
228 1
                    break;
229 1
                case 'main_tag_id':
230 1
                    $value = $tag->mainTagId;
231 1
                    break;
232 1
                case 'modification_date':
233 1
                    $value = $tag->modificationDate->getTimestamp();
234 1
                    break;
235 1
                case 'path':
236 1
                    $value = $tag->pathString;
237 1
                    break;
238 1
                case 'parent_tag_id':
239 1
                    $value = $tag->parentTagId;
240 1
                    break;
241 1
                case 'remote_id':
242 1
                    $value = $tag->remoteId;
243 1
                    break;
244
                default:
245
                    throw new InvalidStepDefinitionException('Tag Manager does not support setting references for attribute ' . $reference['attribute']);
246
            }
247
248 1
            $refs[$reference['identifier']] = $value;
249
        }
250
251 1
        return $refs;
252
    }
253
254
    /**
255
     * Generates a migration definition in array format
256
     *
257
     * @param array $matchConditions
258
     * @param string $mode
259
     * @param array $context
260
     * @return array Migration data
261
     * @throws InvalidMatchConditionsException
262
     * @throws \Exception
263
     */
264 3
    public function generateMigration(array $matchConditions, $mode, array $context = array())
265
    {
266 3
        $data = array();
267
        $previousUserId = $this->loginUser($this->getAdminUserIdentifierFromContext($context));
268 3
        try {
269 3
            $tagCollection = $this->tagMatcher->match($matchConditions);
270
271 3
            switch ($mode) {
272 3
                case 'create':
273
                    // sort top to bottom
274 1
                    $tagCollection = $tagCollection->getArrayCopy();
275
                    uasort($tagCollection, function ($t1, $t2) {
276
                        if ($t1->depth == $t2->depth) return 0;
277
                        return ($t1->depth > $t2->depth) ? 1 : -1;
278 1
                    });
279 1
                    break;
280 2
                case 'delete':
281
                    // sort bottom to top
282 1
                    $tagCollection = $tagCollection->getArrayCopy();
283
                    uasort($tagCollection, function ($t1, $t2) {
284
                        if ($t1->depth == $t2->depth) return 0;
285
                        return ($t1->depth > $t2->depth) ? -1 : 1;
286 1
                    });
287 1
                    break;
288
            }
289
290
            /** @var Tag $tag */
291 3
            foreach ($tagCollection as $tag) {
292
293
                $tagData = array(
294
                    'type' => reset($this->supportedStepTypes),
295
                    'mode' => $mode
296
                );
297
298
                switch ($mode) {
299
                    case 'create':
300
                        if ($tag->parentTagId != 0) {
301
                            $parentTagRid = $this->tagMatcher->matchOneByKey($tag->parentTagId)->remoteId;
302
                        } else {
303
                            $parentTagRid = 0;
304
                        }
305
                        $tagData = array_merge(
306
                            $tagData,
307
                            array(
308
                                'parent_tag_id' => $parentTagRid,
309
                                'always_available' => $tag->alwaysAvailable,
310
                                'lang' => $tag->mainLanguageCode,
311
                                'keywords' => $this->getTagKeywords($tag),
312
                                'remote_id' => $tag->remoteId
313
                            )
314
                        );
315
                        break;
316
                    case 'update':
317
                        $tagData = array_merge(
318
                            $tagData,
319
                            array(
320
                                'match' => array(
321
                                    TagMatcher::MATCH_TAG_REMOTE_ID => $tag->remoteId
322
                                ),
323
                                'always_available' => $tag->alwaysAvailable,
324
                                'lang' => $tag->mainLanguageCode,
325
                                'keywords' => $this->getTagKeywords($tag),
326
                            )
327
                        );
328
                        break;
329
                    case 'delete':
330
                        $tagData = array_merge(
331
                            $tagData,
332
                            array(
333
                                'match' => array(
334
                                    TagMatcher::MATCH_TAG_REMOTE_ID => $tag->remoteId
335
                                )
336
                            )
337
                        );
338
                        break;
339
                    default:
340
                        throw new InvalidStepDefinitionException("Executor 'tag' doesn't support mode '$mode'");
341
                }
342
343
                $data[] = $tagData;
344
            }
345
346 3
            $this->loginUser($previousUserId);
347 3
        } catch (\Exception $e) {
348
            $this->loginUser($previousUserId);
349
            throw $e;
350
        }
351
352
        return $data;
353
    }
354
355
    /**
356
     * @return string[]
357
     */
358
    public function listAllowedConditions()
359
    {
360
        return $this->tagMatcher->listAllowedConditions();
361
    }
362
363
    protected function getTagKeywords($tag)
364
    {
365
        $out = array();
366
        foreach ($tag->languageCodes as $languageCode) {
367 1
            $out[$languageCode] = $tag->getKeyword($languageCode);
368
        }
369 1
        return $out;
370
    }
371
372
    protected function checkTagsBundleInstall()
373 1
    {
374
        if (!$this->tagService)
375
        {
376
            throw new MigrationBundleException('To manipulate tags you must have NetGen Tags Bundle installed');
377
        }
378
    }
379
}
380