Completed
Pull Request — master (#135)
by Alex
12:13 queued 03:11
created

ContentMatcher::matchContent()   D

Complexity

Conditions 22
Paths 124

Size

Total Lines 80
Code Lines 48

Duplication

Lines 30
Ratio 37.5 %

Code Coverage

Tests 27
CRAP Score 38.6148

Importance

Changes 0
Metric Value
dl 30
loc 80
ccs 27
cts 40
cp 0.675
rs 4.6803
c 0
b 0
f 0
cc 22
eloc 48
nc 124
nop 1
crap 38.6148

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 View Code Duplication
        if (count($conditions) === 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...
48 3
            $condition = reset($conditions);
49 3
            if (is_int($condition) || ctype_digit($condition)) {
50 3
                return $this->repository->getContentService()->loadContent($condition);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->repository...oadContent($condition); (eZ\Publish\API\Repository\Values\Content\Content) is incompatible with the return type documented by Kaliop\eZMigrationBundle...ntMatcher::matchContent of type Kaliop\eZMigrationBundle...\ContentCollection|null.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
51
            } elseif (is_string($condition)) {
52
                return $this->repository->getContentService()->loadContentByRemoteId($condition);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->repository...ByRemoteId($condition); (eZ\Publish\API\Repository\Values\Content\Content) is incompatible with the return type documented by Kaliop\eZMigrationBundle...ntMatcher::matchContent of type Kaliop\eZMigrationBundle...\ContentCollection|null.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
53 3
            }
54 3
        }
55
56 2
        foreach ($conditions as $key => $values) {
57 1
58
            switch($key) {
59 2
60 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...
61
                    $contentService = $this->repository->getContentService();
62 2
                    $contents = array();
63
                    // allow to specify the objects to relate to using different ways
64
                    $relatedContents = $this->match($values);
65 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...
66
                        foreach($contentService->loadReverseRelations($relatedContent->contentInfo) as $relatingContent) {
67
                            $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...
68 2
                        }
69
                    }
70
                    break;
71 2
72 2 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...
73
                    $contentService = $this->repository->getContentService();
74
                    $contents = array();
75
                    // allow to specify the objects we relate to using different ways
76
                    $relatingContents = $this->match($values);
77
                    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...
78
                        foreach($contentService->loadRelations($relatingContent->contentInfo) as $relatedContent) {
79
                            $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...
80
                        }
81 3
                    }
82
                    break;
83 3
84
                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...
85 3
86
                    // BC support
87 3
                    if ($key == 'content_type') {
88 3
                        if (is_int($values[0]) || ctype_digit($values[0])) {
89 3
                            $key = self::MATCH_CONTENT_TYPE_ID;
90
                        } else {
91 3
                            $key = self::MATCH_CONTENT_TYPE_IDENTIFIER;
92
                        }
93
                    }
94
95
                    $query = new Query();
96
                    $query->limit = self::INT_MAX_16BIT;
97
                    if (isset($query->performCount)) $query->performCount = false;
98
                    $query->filter = $this->getQueryCriterion($key, $values);
99
                    switch ($key) {
100
                        case 'content_type_id':
101
                        case self::MATCH_CONTENT_TYPE_ID:
102
                        case 'content_type_identifier':
103
                        case self::MATCH_CONTENT_TYPE_IDENTIFIER:
104
                            // sort objects by depth, lower to higher, so that deleting them has less chances of failure
105
                            // NB: we only do this in eZP versions that allow depth sorting on content queries
106
                            if (class_exists('eZ\Publish\API\Repository\Values\Content\Query\SortClause\LocationDepth')) {
107
                                $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...
108
                            }
109
                    }
110
                    $results = $this->repository->getSearchService()->findContent($query);
111
112
                    $contents = [];
113
                    foreach ($results->searchHits as $result) {
114
                        // make sure we return every object only once
115 1
                        $contents[$result->valueObject->contentInfo->id] = $result->valueObject;
116
                    }
117 1
            }
118
119 1
            return new ContentCollection($contents);
120 1
121
        }
122 1
    }
123 1
124
    /**
125 1
     * When matching by key, we accept content Id and remote Id only
126
     * @param int|string $key
127
     * @return array
128
     */
129
    protected function getConditionsFromKey($key)
130
    {
131
        if (is_int($key) || ctype_digit($key)) {
132
            return array(self::MATCH_CONTENT_ID => $key);
133
        }
134
        return array(self::MATCH_CONTENT_REMOTE_ID => $key);
135
    }
136
137
    /**
138
     * @param int[] $contentIds
139
     * @return Content[]
140
     * @deprecated
141
     */
142 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...
143
    {
144
        $contents = [];
145
146
        foreach ($contentIds as $contentId) {
147
            // return unique contents
148
            $content = $this->repository->getContentService()->loadContent($contentId);
149
            $contents[$content->contentInfo->id] = $content;
150
        }
151
152
        return $contents;
153
    }
154
155
    /**
156
     * @param string[] $remoteContentIds
157
     * @return Content[]
158
     * @deprecated
159
     */
160 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...
161
    {
162
        $contents = [];
163
164
        foreach ($remoteContentIds as $remoteContentId) {
165
            // return unique contents
166
            $content = $this->repository->getContentService()->loadContentByRemoteId($remoteContentId);
167
            $contents[$content->contentInfo->id] = $content;
168
        }
169
170
        return $contents;
171
    }
172
173
    /**
174
     * @param int[] $locationIds
175
     * @return Content[]
176
     * @deprecated
177
     */
178 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...
179
    {
180
        $contentIds = [];
181
182
        foreach ($locationIds as $locationId) {
183
            $location = $this->repository->getLocationService()->loadLocation($locationId);
184
            // return unique ids
185
            $contentIds[$location->contentId] = $location->contentId;
186 2
        }
187
188 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...
189 2
    }
190 2
191
    /**
192
     * @param string[] $remoteLocationIds
193 2
     * @return Content[]
194 2
     * @deprecated
195
     */
196 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...
197 2
    {
198
        $contentIds = [];
199 2
200 2
        foreach ($remoteLocationIds as $remoteLocationId) {
201
            $location = $this->repository->getLocationService()->loadLocationByRemoteId($remoteLocationId);
202 2
            // return unique ids
203
            $contentIds[$location->contentId] = $location->contentId;
204
        }
205
206
        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...
207
    }
208
209
    /**
210
     * @param int[] $parentLocationIds
211
     * @return Content[]
212
     * @deprecated
213
     */
214 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...
215
    {
216
        $query = new Query();
217
        $query->limit = self::INT_MAX_16BIT;
218
        if (isset($query->performCount)) $query->performCount = false;
219
        $query->filter = new Query\Criterion\ParentLocationId($parentLocationIds);
220
        $results = $this->repository->getSearchService()->findContent($query);
221
222
        $contents = [];
223
        foreach ($results->searchHits as $result) {
224
            // make sure we return every object only once
225
            $contents[$result->valueObject->contentInfo->id] = $result->valueObject;
226
        }
227
228
        return $contents;
229
    }
230
231
    /**
232
     * @param string[] $remoteParentLocationIds
233
     * @return Content[]
234
     * @deprecated
235
     */
236 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...
237
    {
238
        $locationIds = [];
239
240
        foreach ($remoteParentLocationIds as $remoteParentLocationId) {
241
            $location = $this->repository->getLocationService()->loadLocationByRemoteId($remoteParentLocationId);
242
            // unique locations
243
            $locationIds[$location->id] = $location->id;
244
        }
245
246
        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...
247
    }
248
249
    /**
250
     * @param string[] $contentTypeIdentifiers
251
     * @return Content[]
252
     * @deprecated
253
     */
254 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...
255
    {
256
        $query = new Query();
257
        $query->limit = self::INT_MAX_16BIT;
258
        if (isset($query->performCount)) $query->performCount = false;
259
        $query->filter = new Query\Criterion\ContentTypeIdentifier($contentTypeIdentifiers);
260
        // sort objects by depth, lower to higher, so that deleting them has less chances of failure
261
        // NB: we only do this in eZP versions that allow depth sorting on content queries
262
        if (class_exists('eZ\Publish\API\Repository\Values\Content\Query\SortClause\LocationDepth')) {
263
            $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...
264
        }
265
266
        $results = $this->repository->getSearchService()->findContent($query);
267
268
        $contents = [];
269
        foreach ($results->searchHits as $result) {
270
            // make sure we return every object only once
271
            $contents[$result->valueObject->contentInfo->id] = $result->valueObject;
272
        }
273
274
        return $contents;
275
    }
276
277
    /**
278
     * @param int[] $contentTypeIds
279
     * @return Content[]
280
     * @deprecated
281
     */
282 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...
283
    {
284
        $query = new Query();
285
        $query->limit = self::INT_MAX_16BIT;
286
        if (isset($query->performCount)) $query->performCount = false;
287
        $query->filter = new Query\Criterion\ContentTypeId($contentTypeIds);
288
        // sort objects by depth, lower to higher, so that deleting them has less chances of failure
289
        // NB: we only do this in eZP versions that allow depth sorting on content queries
290
        if (class_exists('eZ\Publish\API\Repository\Values\Content\Query\SortClause\LocationDepth')) {
291
            $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...
292
        }
293
        $results = $this->repository->getSearchService()->findContent($query);
294
295
        $contents = [];
296
        foreach ($results->searchHits as $result) {
297
            // make sure we return every object only once
298
            $contents[$result->valueObject->contentInfo->id] = $result->valueObject;
299
        }
300
301
        return $contents;
302
    }
303
304
}
305