Completed
Push — master ( 8c5037...eed80c )
by Gaetano
07:22
created

ContentMatcher   B

Complexity

Total Complexity 40

Size/Duplication

Total Lines 259
Duplicated Lines 39 %

Coupling/Cohesion

Components 1
Dependencies 15

Test Coverage

Coverage 53.75%

Importance

Changes 0
Metric Value
dl 101
loc 259
c 0
b 0
f 0
wmc 40
lcom 1
cbo 15
ccs 43
cts 80
cp 0.5375
rs 7.6556

11 Methods

Rating   Name   Duplication   Size   Complexity  
A match() 0 4 1
C matchContent() 0 54 18
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() 0 15 2
A findContentsByParentLocationRemoteIds() 12 12 2
A findContentsByContentTypeIdentifiers() 21 21 3
A findContentsByContentTypeIds() 20 20 3

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
/**
10
 * @todo extend to allow matching by visibility, subtree, depth, object state, section, creation/modification date...
11
 * @todo optimize the matches on multiple conditions (and, or) by compiling them in a single query
12
 */
13
class ContentMatcher extends RepositoryMatcher
14
{
15
    use FlexibleKeyMatcherTrait;
16
17
    const MATCH_CONTENT_ID = 'content_id';
18
    const MATCH_LOCATION_ID = 'location_id';
19
    const MATCH_CONTENT_REMOTE_ID = 'content_remote_id';
20
    const MATCH_LOCATION_REMOTE_ID = 'location_remote_id';
21
    const MATCH_PARENT_LOCATION_ID = 'parent_location_id';
22
    const MATCH_PARENT_LOCATION_REMOTE_ID = 'parent_location_remote_id';
23
    const MATCH_CONTENT_TYPE_ID = 'contenttype_id';
24
    const MATCH_CONTENT_TYPE_IDENTIFIER = 'contenttype_identifier';
25
26
    protected $allowedConditions = array(
27
        self::MATCH_AND, self::MATCH_OR,
28
        self::MATCH_CONTENT_ID, self::MATCH_LOCATION_ID, self::MATCH_CONTENT_REMOTE_ID, self::MATCH_LOCATION_REMOTE_ID,
29
        self::MATCH_PARENT_LOCATION_ID, self::MATCH_PARENT_LOCATION_REMOTE_ID, self::MATCH_CONTENT_TYPE_IDENTIFIER,
30
        // aliases
31
        'content_type', 'content_type_id', 'content_type_identifier'
32
    );
33 3
    protected $returns = 'Content';
34
35 3
    /**
36
     * @param array $conditions key: condition, value: int / string / int[] / string[]
37
     * @return ContentCollection
38
     */
39
    public function match(array $conditions)
40
    {
41
        return $this->matchContent($conditions);
42 3
    }
43
44 3
    /**
45
     * @param array $conditions key: condition, value: int / string / int[] / string[]
46 3
     * @return ContentCollection
47
     */
48 3
    public function matchContent(array $conditions)
49 3
    {
50 3
        $this->validateConditions($conditions);
51
52
        foreach ($conditions as $key => $values) {
53 3
54 3
            if (!is_array($values)) {
55
                $values = array($values);
56 2
            }
57 1
58
            // BC support
59 2
            if ($key == 'content_type') {
60
                if (is_int($values[0]) || ctype_digit($values[0])) {
61
                    $key = self::MATCH_CONTENT_TYPE_ID;
62 2
                } else {
63
                    $key = self::MATCH_CONTENT_TYPE_IDENTIFIER;
64
                }
65 2
            }
66
67
            switch ($key) {
68 2
                case self::MATCH_CONTENT_ID:
69
                   return new ContentCollection($this->findContentsByContentIds($values));
70
71 2
                case self::MATCH_LOCATION_ID:
72 2
                    return new ContentCollection($this->findContentsByLocationIds($values));
73
74
                case self::MATCH_CONTENT_REMOTE_ID:
75
                    return new ContentCollection($this->findContentsByContentRemoteIds($values));
76
77
                case self::MATCH_LOCATION_REMOTE_ID:
78
                    return new ContentCollection($this->findContentsByLocationRemoteIds($values));
79
80
                case self::MATCH_PARENT_LOCATION_ID:
81 3
                    return new ContentCollection($this->findContentsByParentLocationIds($values));
82
83 3
                case self::MATCH_PARENT_LOCATION_REMOTE_ID:
84
                    return new ContentCollection($this->findContentsByParentLocationRemoteIds($values));
85 3
86
                case 'content_type_id':
87 3
                case self::MATCH_CONTENT_TYPE_ID:
88 3
                    return new ContentCollection($this->findContentsByContentTypeIds($values));
89 3
90
                case 'content_type_identifier':
91 3
                case self::MATCH_CONTENT_TYPE_IDENTIFIER:
92
                    return new ContentCollection($this->findContentsByContentTypeIdentifiers($values));
93
94
                case self::MATCH_AND:
95
                    return $this->matchAnd($values);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->matchAnd($values); of type object|array adds the type array to the return on line 95 which is incompatible with the return type documented by Kaliop\eZMigrationBundle...ntMatcher::matchContent of type Kaliop\eZMigrationBundle...\ContentCollection|null.
Loading history...
96
97
                case self::MATCH_OR:
98
                    return $this->matchOr($values);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->matchOr($values); of type object|array adds the type array to the return on line 98 which is incompatible with the return type documented by Kaliop\eZMigrationBundle...ntMatcher::matchContent of type Kaliop\eZMigrationBundle...\ContentCollection|null.
Loading history...
99
            }
100
        }
101
    }
102
103
    /**
104
     * When matching by key, we accept content Id and remote Id only
105
     * @param int|string $key
106
     * @return array
107
     */
108
    protected function getConditionsFromKey($key)
109
    {
110
        if (is_int($key) || ctype_digit($key)) {
111
            return array(self::MATCH_CONTENT_ID => $key);
112
        }
113
        return array(self::MATCH_CONTENT_REMOTE_ID => $key);
114
    }
115 1
116
    /**
117 1
     * @param int[] $contentIds
118
     * @return Content[]
119 1
     */
120 1 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...
121
    {
122 1
        $contents = [];
123 1
124
        foreach ($contentIds as $contentId) {
125 1
            // return unique contents
126
            $content = $this->repository->getContentService()->loadContent($contentId);
127
            $contents[$content->contentInfo->id] = $content;
128
        }
129
130
        return $contents;
131
    }
132
133
    /**
134
     * @param string[] $remoteContentIds
135
     * @return Content[]
136
     */
137 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...
138
    {
139
        $contents = [];
140
141
        foreach ($remoteContentIds as $remoteContentId) {
142
            // return unique contents
143
            $content = $this->repository->getContentService()->loadContentByRemoteId($remoteContentId);
144
            $contents[$content->contentInfo->id] = $content;
145
        }
146
147
        return $contents;
148
    }
149
150
    /**
151
     * @param int[] $locationIds
152
     * @return Content[]
153
     */
154 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...
155
    {
156
        $contentIds = [];
157
158
        foreach ($locationIds as $locationId) {
159
            $location = $this->repository->getLocationService()->loadLocation($locationId);
160
            // return unique ids
161
            $contentIds[$location->contentId] = $location->contentId;
162
        }
163
164
        return $this->findContentsByContentIds($contentIds);
165
    }
166
167
    /**
168
     * @param string[] $remoteLocationIds
169
     * @return Content[]
170
     */
171 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...
172
    {
173
        $contentIds = [];
174
175
        foreach ($remoteLocationIds as $remoteLocationId) {
176
            $location = $this->repository->getLocationService()->loadLocationByRemoteId($remoteLocationId);
177
            // return unique ids
178
            $contentIds[$location->contentId] = $location->contentId;
179
        }
180
181
        return $this->findContentsByContentIds($contentIds);
182
    }
183
184
    /**
185
     * @param int[] $parentLocationIds
186 2
     * @return Content[]
187
     */
188 2
    protected function findContentsByParentLocationIds($parentLocationIds)
189 2
    {
190 2
        $query = new Query();
191
        $query->limit = PHP_INT_MAX;
192
        $query->filter = new Query\Criterion\ParentLocationId($parentLocationIds);
193 2
        $results = $this->repository->getSearchService()->findContent($query);
194 2
195
        $contents = [];
196 2
        foreach ($results->searchHits as $result) {
197 2
            // make sure we return every object only once
198
            $contents[$result->valueObject->contentInfo->id] = $result->valueObject;
199 2
        }
200 2
201
        return $contents;
202 2
    }
203
204
    /**
205
     * @param string[] $remoteParentLocationIds
206
     * @return Content[]
207
     */
208 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...
209
    {
210
        $locationIds = [];
211
212
        foreach ($remoteParentLocationIds as $remoteParentLocationId) {
213
            $location = $this->repository->getLocationService()->loadLocationByRemoteId($remoteParentLocationId);
214
            // unique locations
215
            $locationIds[$location->id] = $location->id;
216
        }
217
218
        return $this->findContentsByParentLocationIds($locationIds);
219
    }
220
221
    /**
222
     * @param string[] $contentTypeIdentifiers
223
     * @return Content[]
224
     */
225 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...
226
    {
227
        $query = new Query();
228
        $query->limit = PHP_INT_MAX;
229
        $query->filter = new Query\Criterion\ContentTypeIdentifier($contentTypeIdentifiers);
230
        // sort objects by depth, lower to higher, so that deleting them has less chances of failure
231
        // NB: we only do this in eZP versions that allow depth sorting on content queries
232
        if (class_exists('eZ\Publish\API\Repository\Values\Content\Query\SortClause\LocationDepth')) {
233
            $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...
234
        }
235
236
        $results = $this->repository->getSearchService()->findContent($query);
237
238
        $contents = [];
239
        foreach ($results->searchHits as $result) {
240
            // make sure we return every object only once
241
            $contents[$result->valueObject->contentInfo->id] = $result->valueObject;
242
        }
243
244
        return $contents;
245
    }
246
247
    /**
248
     * @param int[] $contentTypeIds
249
     * @return Content[]
250
     */
251 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...
252
    {
253
        $query = new Query();
254
        $query->limit = PHP_INT_MAX;
255
        $query->filter = new Query\Criterion\ContentTypeId($contentTypeIds);
256
        // sort objects by depth, lower to higher, so that deleting them has less chances of failure
257
        // NB: we only do this in eZP versions that allow depth sorting on content queries
258
        if (class_exists('eZ\Publish\API\Repository\Values\Content\Query\SortClause\LocationDepth')) {
259
            $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...
260
        }
261
        $results = $this->repository->getSearchService()->findContent($query);
262
263
        $contents = [];
264
        foreach ($results->searchHits as $result) {
265
            // make sure we return every object only once
266
            $contents[$result->valueObject->contentInfo->id] = $result->valueObject;
267
        }
268
269
        return $contents;
270
    }
271
}
272