Completed
Push — master ( ac3b8b...3c857d )
by Gaetano
18:21
created

QueryBasedMatcher   C

Complexity

Total Complexity 40

Size/Duplication

Total Lines 209
Duplicated Lines 30.62 %

Coupling/Cohesion

Components 1
Dependencies 22

Importance

Changes 0
Metric Value
dl 64
loc 209
c 0
b 0
f 0
wmc 40
lcom 1
cbo 22
rs 5.0605

2 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 1
D getQueryCriterion() 64 137 39

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 QueryBasedMatcher 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 QueryBasedMatcher, 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\Query;
6
use eZ\Publish\API\Repository\Repository;
7
use Kaliop\eZMigrationBundle\API\KeyMatcherInterface;
8
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator;
9
10
/**
11
 * @todo extend to allow matching by modifier, language code, content_type_group_id
12
 */
13
abstract class QueryBasedMatcher extends RepositoryMatcher
14
{
15
    const MATCH_CONTENT_ID = 'content_id';
16
    const MATCH_LOCATION_ID = 'location_id';
17
    const MATCH_CONTENT_REMOTE_ID = 'content_remote_id';
18
    const MATCH_LOCATION_REMOTE_ID = 'location_remote_id';
19
    const MATCH_ATTRIBUTE = 'attribute';
20
    const MATCH_CONTENT_TYPE_ID = 'contenttype_id';
21
    const MATCH_CONTENT_TYPE_IDENTIFIER = 'contenttype_identifier';
22
    const MATCH_CREATION_DATE = 'creation_date';
23
    const MATCH_GROUP = 'group';
24
    const MATCH_MODIFICATION_DATE = 'modification_date';
25
    const MATCH_OBJECT_STATE = 'object_state';
26
    const MATCH_OWNER = 'owner';
27
    const MATCH_PARENT_LOCATION_ID = 'parent_location_id';
28
    const MATCH_PARENT_LOCATION_REMOTE_ID = 'parent_location_remote_id';
29
    const MATCH_SECTION = 'section';
30
    const MATCH_SUBTREE = 'subtree';
31
    const MATCH_VISIBILITY = 'visibility';
32
33
    static protected $operatorsMap = array(
34
        'eq' => Operator::EQ,
35
        'gt' => Operator::GT,
36
        'gte' => Operator::GTE,
37
        'lt' => Operator::LT,
38
        'lte' => Operator::LTE,
39
        'in' => Operator::IN,
40
        'between' => Operator::BETWEEN,
41
        'like' => Operator::LIKE,
42
        'contains' => Operator::CONTAINS,
43
        Operator::EQ => Operator::EQ,
44
        Operator::GT => Operator::GT,
45
        Operator::GTE => Operator::GTE,
46
        Operator::LT => Operator::LT,
47
        Operator::LTE => Operator::LTE,
48
    );
49
50
    /** @var  KeyMatcherInterface $groupMatcher */
51
    protected $groupMatcher;
52
    /** @var  KeyMatcherInterface $sectionMatcher */
53
    protected $sectionMatcher;
54
    /** @var  KeyMatcherInterface $stateMatcher */
55
    protected $stateMatcher;
56
    /** @var  KeyMatcherInterface $userMatcher */
57
    protected $userMatcher;
58
59
    /**
60
     * @param Repository $repository
61
     * @param KeyMatcherInterface $groupMatcher
62
     * @param KeyMatcherInterface $sectionMatcher
63
     * @param KeyMatcherInterface $stateMatcher
64
     * @param KeyMatcherInterface $userMatcher
65
     * @todo inject the services needed, not the whole repository
66
     */
67
    public function __construct(Repository $repository, KeyMatcherInterface $groupMatcher = null,
0 ignored issues
show
Unused Code introduced by
The parameter $groupMatcher is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
68
        KeyMatcherInterface $sectionMatcher = null, KeyMatcherInterface $stateMatcher = null,
69
        KeyMatcherInterface $userMatcher = null)
70
    {
71
        parent::__construct($repository);
72
        $this->userMatcher = $userMatcher;
73
        $this->sectionMatcher = $sectionMatcher;
74
        $this->stateMatcher = $stateMatcher;
75
        $this->userMatcher = $userMatcher;
76
    }
77
78
    /**
79
     * @param $key
80
     * @param $values
81
     * @return mixed should it be \eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface ?
82
     * @throws \Exception for unsupported keys
83
     */
84
    protected function getQueryCriterion($key, $values)
85
    {
86
        if (!is_array($values)) {
87
            $values = array($values);
88
        }
89
90
        switch ($key) {
91
            case self::MATCH_CONTENT_ID:
92
                return new Query\Criterion\ContentId($values);
93
94
            case self::MATCH_LOCATION_ID:
95
                // NB: seems to cause problems with EZP 2014.3
96
                return new Query\Criterion\LocationId(reset($values));
97
98
            case self::MATCH_CONTENT_REMOTE_ID:
99
                return new Query\Criterion\RemoteId($values);
100
101
            case self::MATCH_LOCATION_REMOTE_ID:
102
                return new Query\Criterion\LocationRemoteId($values);
103
104
            case self::MATCH_ATTRIBUTE:
105
                $spec = reset($values);
106
                $attribute = key($values);
107
                $match = reset($spec);
108
                $operator = key($spec);
109
                if (!isset(self::$operatorsMap[$operator])) {
110
                    throw new \Exception("Can not use '$operator' as comparison operator for attributes");
111
                }
112
                return new Query\Criterion\Field($attribute, self::$operatorsMap[$operator], $match);
113
114
            case 'content_type_id':
115
            case self::MATCH_CONTENT_TYPE_ID:
116
                return new Query\Criterion\ContentTypeId($values);
117
118
            case 'content_type_identifier':
119
            case self::MATCH_CONTENT_TYPE_IDENTIFIER:
120
                return new Query\Criterion\ContentTypeIdentifier($values);
121
122 View Code Duplication
            case self::MATCH_CREATION_DATE:
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...
123
                $match = reset($values);
124
                $operator = key($values);
125
                if (!isset(self::$operatorsMap[$operator])) {
126
                    throw new \Exception("Can not use '$operator' as comparison operator for dates");
127
                }
128
                return new Query\Criterion\DateMetadata(Query\Criterion\DateMetadata::CREATED, self::$operatorsMap[$operator], $match);
129
130 View Code Duplication
            case self::MATCH_GROUP:
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...
131
                foreach($values as &$value) {
132
                    if (!ctype_digit($value)) {
133
                        $value = $this->groupMatcher->matchOneByKey($value)->id;
134
                    }
135
                }
136
                return new Query\Criterion\UserMetadata(Query\Criterion\UserMetadata::GROUP, Operator::IN, $values);
137
138 View Code Duplication
            case self::MATCH_MODIFICATION_DATE:
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...
139
                $match = reset($values);
140
                $operator = key($values);
141
                if (!isset(self::$operatorsMap[$operator])) {
142
                    throw new \Exception("Can not use '$operator' as comparison operator for dates");
143
                }
144
                return new Query\Criterion\DateMetadata(Query\Criterion\DateMetadata::MODIFIED, self::$operatorsMap[$operator], $match);
145
146 View Code Duplication
            case self::MATCH_OBJECT_STATE:
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...
147
                foreach($values as &$value) {
148
                    if (!ctype_digit($value)) {
149
                        $value = $this->stateMatcher->matchOneByKey($value)->id;
150
                    }
151
                }
152
                return new Query\Criterion\ObjectStateId($values);
153
154 View Code Duplication
            case self::MATCH_OWNER:
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...
155
                foreach($values as &$value) {
156
                    if (!ctype_digit($value)) {
157
                        $value = $this->userMatcher->matchOneByKey($value)->id;
158
                    }
159
                }
160
                return new Query\Criterion\UserMetadata(Query\Criterion\UserMetadata::OWNER, Operator::IN, $values);
161
162
            case self::MATCH_PARENT_LOCATION_ID:
163
                return new Query\Criterion\ParentLocationId($values);
164
165
            case self::MATCH_PARENT_LOCATION_REMOTE_ID:
166
                $locationIds = [];
167
                foreach ($values as $remoteParentLocationId) {
168
                    $location = $this->repository->getLocationService()->loadLocationByRemoteId($remoteParentLocationId);
169
                    // unique locations
170
                    $locationIds[$location->id] = $location->id;
171
                }
172
                return new Query\Criterion\ParentLocationId($locationIds);
173
174 View Code Duplication
            case self::MATCH_SECTION:
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...
175
                foreach($values as &$value) {
176
                    if (!ctype_digit($value)) {
177
                        $value = $this->sectionMatcher->matchOneByKey($value)->id;
178
                    }
179
                }
180
                return new Query\Criterion\SectionId($values);
181
182
            case self::MATCH_SUBTREE:
183
                return new Query\Criterion\Subtree($values);
184
185 View Code Duplication
            case self::MATCH_VISIBILITY:
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...
Coding Style introduced by
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
186
                /// @todo error/warning if there is more than 1 value...
187
                $value = reset($values);
188
                if ($value) {
189
                    return new Query\Criterion\Visibility(Query\Criterion\Visibility::VISIBLE);
190
                } else {
191
                    return new Query\Criterion\Visibility(Query\Criterion\Visibility::HIDDEN);
192
                }
193
194 View Code Duplication
            case self::MATCH_AND:
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...
195
                $subCriteria = array();
196
                foreach($values as $subCriterion) {
197
                    $value = reset($subCriterion);
198
                    $subCriteria[] = $this->getQueryCriterion(key($subCriterion), $value);
199
                }
200
                return new Query\Criterion\LogicalAnd($subCriteria);
201
202 View Code Duplication
            case self::MATCH_OR:
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...
203
                $subCriteria = array();
204
                foreach($values as $subCriterion) {
205
                    $value = reset($subCriterion);
206
                    $subCriteria[] = $this->getQueryCriterion(key($subCriterion), $value);
207
                }
208
                return new Query\Criterion\LogicalOr($subCriteria);
209
210
            case self::MATCH_NOT:
211
                /// @todo throw if more than one sub-criteria found
212
                $value = reset($values);
213
                $subCriterion = $this->getQueryCriterion(key($values), $value);
214
                return new Query\Criterion\LogicalNot($subCriterion);
215
216
            default:
217
                throw new \Exception($this->returns . " can not be matched because matching condition '$key' is not supported. Supported conditions are: " .
218
                    implode(', ', $this->allowedConditions));
219
        }
220
    }
221
}
222