Completed
Pull Request — master (#135)
by Alex
07:43
created

ContentMatcher   B

Complexity

Total Complexity 46

Size/Duplication

Total Lines 302
Duplicated Lines 49.34 %

Coupling/Cohesion

Components 1
Dependencies 15

Test Coverage

Coverage 53.75%

Importance

Changes 0
Metric Value
wmc 46
lcom 1
cbo 15
dl 149
loc 302
ccs 43
cts 80
cp 0.5375
rs 7.4918
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A match() 0 4 1
D matchContent() 30 86 21
A getConditionsFromKey() 0 7 3
A findContentsByContentIds() 12 12 2
A findContentsByContentRemoteIds() 12 12 2
A findContentsByLocationIds() 12 12 2
A findContentsByLocationRemoteIds() 12 12 2
A findContentsByParentLocationIds() 16 16 3
A findContentsByParentLocationRemoteIds() 12 12 2
B findContentsByContentTypeIdentifiers() 22 22 4
A findContentsByContentTypeIds() 21 21 4

How to fix   Duplicated Code    Complexity   

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:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like ContentMatcher 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 ContentMatcher, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Kaliop\eZMigrationBundle\Core\Matcher;
4
5
use eZ\Publish\API\Repository\Values\Content\Content;
6
use eZ\Publish\API\Repository\Values\Content\Query;
7
use Kaliop\eZMigrationBundle\API\Collection\ContentCollection;
8
9
class ContentMatcher extends QueryBasedMatcher
10
{
11
    use FlexibleKeyMatcherTrait;
12
13
    const MATCH_RELATES_TO = 'relates_to';
14
    const MATCH_RELATED_FROM = 'related_from';
15
16
    protected $allowedConditions = array(
17
        self::MATCH_AND, self::MATCH_OR, self::MATCH_NOT,
18
        self::MATCH_CONTENT_ID, self::MATCH_LOCATION_ID, self::MATCH_CONTENT_REMOTE_ID, self::MATCH_LOCATION_REMOTE_ID,
19
        self::MATCH_ATTRIBUTE, self::MATCH_CONTENT_TYPE_ID, self::MATCH_CONTENT_TYPE_IDENTIFIER, self::MATCH_GROUP,
20
        self::MATCH_CREATION_DATE, self::MATCH_MODIFICATION_DATE, self::MATCH_OBJECT_STATE, self::MATCH_OWNER,
21
        self::MATCH_PARENT_LOCATION_ID, self::MATCH_PARENT_LOCATION_REMOTE_ID, self::MATCH_SECTION, self::MATCH_SUBTREE,
22
        self::MATCH_VISIBILITY,
23
        // aliases
24
        'content_type', 'content_type_id', 'content_type_identifier',
25
        // content-only
26
        self::MATCH_RELATES_TO, self::MATCH_RELATED_FROM
27
    );
28
    protected $returns = 'Content';
29
30
    /**
31
     * @param array $conditions key: condition, value: int / string / int[] / string[]
32
     * @return ContentCollection
33 3
     */
34
    public function match(array $conditions)
35 3
    {
36
        return $this->matchContent($conditions);
37
    }
38
39
    /**
40
     * @param array $conditions key: condition, value: int / string / int[] / string[]
41
     * @return ContentCollection
42 3
     */
43
    public function matchContent(array $conditions)
44 3
    {
45
        $this->validateConditions($conditions);
46 3
47
        if (count($conditions) === 1) {
48 3
            $key = array_keys($conditions)[0];
49 3
            switch ($key) {
50 3 View Code Duplication
                case self::MATCH_CONTENT_ID:
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...
51
                    $location = $this->repository->getContentService()->loadContent($conditions[$key]);
52
                    return new ContentCollection(array($location));
53 3
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
54 3
55 View Code Duplication
                case self::MATCH_CONTENT_REMOTE_ID:
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...
56 2
                    $location = $this->repository->getContentService()->loadContentByRemoteId($conditions[$key]);
57 1
                    return new ContentCollection(array($location));
58
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
59 2
            }
60
        }
61
62 2
        foreach ($conditions as $key => $values) {
63
64
            switch($key) {
65 2
66 View Code Duplication
                case self::MATCH_RELATES_TO:
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...
67
                    $contentService = $this->repository->getContentService();
68 2
                    $contents = array();
69
                    // allow to specify the objects to relate to using different ways
70
                    $relatedContents = $this->match($values);
71 2
                    foreach($relatedContents as $relatedContent) {
0 ignored issues
show
Bug introduced by
The expression $relatedContents of type object<Kaliop\eZMigratio...ContentCollection>|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
72 2
                        foreach($contentService->loadReverseRelations($relatedContent->contentInfo) as $relatingContent) {
73
                            $contents[$relatingContent->contentInfo->id] = $relatingContent;
0 ignored issues
show
Bug introduced by
The property contentInfo does not seem to exist. Did you mean destinationContentInfo?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
74
                        }
75
                    }
76
                    break;
77
78 View Code Duplication
                case self::MATCH_RELATED_FROM:
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...
79
                    $contentService = $this->repository->getContentService();
80
                    $contents = array();
81 3
                    // allow to specify the objects we relate to using different ways
82
                    $relatingContents = $this->match($values);
83 3
                    foreach($relatingContents as $relatingContent) {
0 ignored issues
show
Bug introduced by
The expression $relatingContents of type object<Kaliop\eZMigratio...ContentCollection>|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
84
                        foreach($contentService->loadRelations($relatingContent->contentInfo) as $relatedContent) {
85 3
                            $contents[$relatedContent->contentInfo->id] = $relatedContent;
0 ignored issues
show
Bug introduced by
The property contentInfo does not seem to exist. Did you mean destinationContentInfo?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
86
                        }
87 3
                    }
88 3
                    break;
89 3
90
                default:
0 ignored issues
show
Coding Style introduced by
The default body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a default statement must start on the line immediately following the statement.

switch ($expr) {
    default:
        doSomething(); //right
        break;
}


switch ($expr) {
    default:

        doSomething(); //wrong
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
91 3
92
                    // BC support
93
                    if ($key == 'content_type') {
94
                        if (is_int($values[0]) || ctype_digit($values[0])) {
95
                            $key = self::MATCH_CONTENT_TYPE_ID;
96
                        } else {
97
                            $key = self::MATCH_CONTENT_TYPE_IDENTIFIER;
98
                        }
99
                    }
100
101
                    $query = new Query();
102
                    $query->limit = self::INT_MAX_16BIT;
103
                    if (isset($query->performCount)) $query->performCount = false;
104
                    $query->filter = $this->getQueryCriterion($key, $values);
105
                    switch ($key) {
106
                        case 'content_type_id':
107
                        case self::MATCH_CONTENT_TYPE_ID:
108
                        case 'content_type_identifier':
109
                        case self::MATCH_CONTENT_TYPE_IDENTIFIER:
110
                            // sort objects by depth, lower to higher, so that deleting them has less chances of failure
111
                            // NB: we only do this in eZP versions that allow depth sorting on content queries
112
                            if (class_exists('eZ\Publish\API\Repository\Values\Content\Query\SortClause\LocationDepth')) {
113
                                $query->sortClauses = array(new Query\SortClause\LocationDepth(Query::SORT_DESC));
0 ignored issues
show
Deprecated Code introduced by
The class eZ\Publish\API\Repositor...ortClause\LocationDepth has been deprecated with message: Since 5.3, use Location search instead

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
114
                            }
115 1
                    }
116
                    $results = $this->repository->getSearchService()->findContent($query);
117 1
118
                    $contents = [];
119 1
                    foreach ($results->searchHits as $result) {
120 1
                        // make sure we return every object only once
121
                        $contents[$result->valueObject->contentInfo->id] = $result->valueObject;
122 1
                    }
123 1
            }
124
125 1
            return new ContentCollection($contents);
126
127
        }
128
    }
129
130
    /**
131
     * When matching by key, we accept content Id and remote Id only
132
     * @param int|string $key
133
     * @return array
134
     */
135
    protected function getConditionsFromKey($key)
136
    {
137
        if (is_int($key) || ctype_digit($key)) {
138
            return array(self::MATCH_CONTENT_ID => $key);
139
        }
140
        return array(self::MATCH_CONTENT_REMOTE_ID => $key);
141
    }
142
143
    /**
144
     * @param int[] $contentIds
145
     * @return Content[]
146
     * @deprecated
147
     */
148 View Code Duplication
    protected function findContentsByContentIds(array $contentIds)
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...
149
    {
150
        $contents = [];
151
152
        foreach ($contentIds as $contentId) {
153
            // return unique contents
154
            $content = $this->repository->getContentService()->loadContent($contentId);
155
            $contents[$content->contentInfo->id] = $content;
156
        }
157
158
        return $contents;
159
    }
160
161
    /**
162
     * @param string[] $remoteContentIds
163
     * @return Content[]
164
     * @deprecated
165
     */
166 View Code Duplication
    protected function findContentsByContentRemoteIds(array $remoteContentIds)
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...
167
    {
168
        $contents = [];
169
170
        foreach ($remoteContentIds as $remoteContentId) {
171
            // return unique contents
172
            $content = $this->repository->getContentService()->loadContentByRemoteId($remoteContentId);
173
            $contents[$content->contentInfo->id] = $content;
174
        }
175
176
        return $contents;
177
    }
178
179
    /**
180
     * @param int[] $locationIds
181
     * @return Content[]
182
     * @deprecated
183
     */
184 View Code Duplication
    protected function findContentsByLocationIds(array $locationIds)
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...
185
    {
186 2
        $contentIds = [];
187
188 2
        foreach ($locationIds as $locationId) {
189 2
            $location = $this->repository->getLocationService()->loadLocation($locationId);
190 2
            // return unique ids
191
            $contentIds[$location->contentId] = $location->contentId;
192
        }
193 2
194 2
        return $this->findContentsByContentIds($contentIds);
0 ignored issues
show
Deprecated Code introduced by
The method Kaliop\eZMigrationBundle...dContentsByContentIds() has been deprecated.

This method has been deprecated.

Loading history...
195
    }
196 2
197 2
    /**
198
     * @param string[] $remoteLocationIds
199 2
     * @return Content[]
200 2
     * @deprecated
201
     */
202 2 View Code Duplication
    protected function findContentsByLocationRemoteIds($remoteLocationIds)
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...
203
    {
204
        $contentIds = [];
205
206
        foreach ($remoteLocationIds as $remoteLocationId) {
207
            $location = $this->repository->getLocationService()->loadLocationByRemoteId($remoteLocationId);
208
            // return unique ids
209
            $contentIds[$location->contentId] = $location->contentId;
210
        }
211
212
        return $this->findContentsByContentIds($contentIds);
0 ignored issues
show
Deprecated Code introduced by
The method Kaliop\eZMigrationBundle...dContentsByContentIds() has been deprecated.

This method has been deprecated.

Loading history...
213
    }
214
215
    /**
216
     * @param int[] $parentLocationIds
217
     * @return Content[]
218
     * @deprecated
219
     */
220 View Code Duplication
    protected function findContentsByParentLocationIds($parentLocationIds)
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...
221
    {
222
        $query = new Query();
223
        $query->limit = self::INT_MAX_16BIT;
224
        if (isset($query->performCount)) $query->performCount = false;
225
        $query->filter = new Query\Criterion\ParentLocationId($parentLocationIds);
226
        $results = $this->repository->getSearchService()->findContent($query);
227
228
        $contents = [];
229
        foreach ($results->searchHits as $result) {
230
            // make sure we return every object only once
231
            $contents[$result->valueObject->contentInfo->id] = $result->valueObject;
232
        }
233
234
        return $contents;
235
    }
236
237
    /**
238
     * @param string[] $remoteParentLocationIds
239
     * @return Content[]
240
     * @deprecated
241
     */
242 View Code Duplication
    protected function findContentsByParentLocationRemoteIds($remoteParentLocationIds)
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...
243
    {
244
        $locationIds = [];
245
246
        foreach ($remoteParentLocationIds as $remoteParentLocationId) {
247
            $location = $this->repository->getLocationService()->loadLocationByRemoteId($remoteParentLocationId);
248
            // unique locations
249
            $locationIds[$location->id] = $location->id;
250
        }
251
252
        return $this->findContentsByParentLocationIds($locationIds);
0 ignored issues
show
Deprecated Code introduced by
The method Kaliop\eZMigrationBundle...tsByParentLocationIds() has been deprecated.

This method has been deprecated.

Loading history...
253
    }
254
255
    /**
256
     * @param string[] $contentTypeIdentifiers
257
     * @return Content[]
258
     * @deprecated
259
     */
260 View Code Duplication
    protected function findContentsByContentTypeIdentifiers(array $contentTypeIdentifiers)
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...
261
    {
262
        $query = new Query();
263
        $query->limit = self::INT_MAX_16BIT;
264
        if (isset($query->performCount)) $query->performCount = false;
265
        $query->filter = new Query\Criterion\ContentTypeIdentifier($contentTypeIdentifiers);
266
        // sort objects by depth, lower to higher, so that deleting them has less chances of failure
267
        // NB: we only do this in eZP versions that allow depth sorting on content queries
268
        if (class_exists('eZ\Publish\API\Repository\Values\Content\Query\SortClause\LocationDepth')) {
269
            $query->sortClauses = array(new Query\SortClause\LocationDepth(Query::SORT_DESC));
0 ignored issues
show
Deprecated Code introduced by
The class eZ\Publish\API\Repositor...ortClause\LocationDepth has been deprecated with message: Since 5.3, use Location search instead

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
270
        }
271
272
        $results = $this->repository->getSearchService()->findContent($query);
273
274
        $contents = [];
275
        foreach ($results->searchHits as $result) {
276
            // make sure we return every object only once
277
            $contents[$result->valueObject->contentInfo->id] = $result->valueObject;
278
        }
279
280
        return $contents;
281
    }
282
283
    /**
284
     * @param int[] $contentTypeIds
285
     * @return Content[]
286
     * @deprecated
287
     */
288 View Code Duplication
    protected function findContentsByContentTypeIds(array $contentTypeIds)
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...
289
    {
290
        $query = new Query();
291
        $query->limit = self::INT_MAX_16BIT;
292
        if (isset($query->performCount)) $query->performCount = false;
293
        $query->filter = new Query\Criterion\ContentTypeId($contentTypeIds);
294
        // sort objects by depth, lower to higher, so that deleting them has less chances of failure
295
        // NB: we only do this in eZP versions that allow depth sorting on content queries
296
        if (class_exists('eZ\Publish\API\Repository\Values\Content\Query\SortClause\LocationDepth')) {
297
            $query->sortClauses = array(new Query\SortClause\LocationDepth(Query::SORT_DESC));
0 ignored issues
show
Deprecated Code introduced by
The class eZ\Publish\API\Repositor...ortClause\LocationDepth has been deprecated with message: Since 5.3, use Location search instead

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
298
        }
299
        $results = $this->repository->getSearchService()->findContent($query);
300
301
        $contents = [];
302
        foreach ($results->searchHits as $result) {
303
            // make sure we return every object only once
304
            $contents[$result->valueObject->contentInfo->id] = $result->valueObject;
305
        }
306
307
        return $contents;
308
    }
309
310
}
311