Completed
Push — master ( b2c38b...f910d1 )
by Gaetano
07:22
created

QueryBasedMatcher::hash2SortOrder()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14

Duplication

Lines 14
Ratio 100 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 14
loc 14
c 0
b 0
f 0
ccs 0
cts 0
cp 0
rs 9.7998
cc 3
nc 3
nop 1
crap 12
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\Values\Content\Query\SortClause;
7
use eZ\Publish\API\Repository\Repository;
8
use Kaliop\eZMigrationBundle\API\KeyMatcherInterface;
9
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator;
10
11
/**
12
 * @todo extend to allow matching by modifier, language code, content_type_group_id
13
 */
14
abstract class QueryBasedMatcher extends RepositoryMatcher
15
{
16
    const MATCH_CONTENT_ID = 'content_id';
17
    const MATCH_LOCATION_ID = 'location_id';
18
    const MATCH_CONTENT_REMOTE_ID = 'content_remote_id';
19
    const MATCH_LOCATION_REMOTE_ID = 'location_remote_id';
20
    const MATCH_ATTRIBUTE = 'attribute';
21
    const MATCH_CONTENT_TYPE_ID = 'contenttype_id';
22
    const MATCH_CONTENT_TYPE_IDENTIFIER = 'contenttype_identifier';
23
    const MATCH_CREATION_DATE = 'creation_date';
24
    const MATCH_GROUP = 'group';
25
    const MATCH_LANGUAGE_CODE = 'lang';
26
    const MATCH_MODIFICATION_DATE = 'modification_date';
27
    const MATCH_OBJECT_STATE = 'object_state';
28
    const MATCH_OWNER = 'owner';
29
    const MATCH_PARENT_LOCATION_ID = 'parent_location_id';
30
    const MATCH_PARENT_LOCATION_REMOTE_ID = 'parent_location_remote_id';
31
    const MATCH_SECTION = 'section';
32
    const MATCH_SUBTREE = 'subtree';
33
    const MATCH_VISIBILITY = 'visibility';
34
35
    const SORT_CONTENT_ID = 'content_id';
36
    const SORT_CONTENT_NAME = 'name';
37
    const SORT_DATE_MODIFIED = 'modified';
38
    const SORT_DATE_PUBLISHED = 'published';
39
    const SORT_LOCATION_DEPTH = 'depth';
40
    const SORT_LOCATION_ID = 'node_id';
41
    const SORT_LOCATION_ISMAIN = 'is_main';
42
    const SORT_LOCATION_PATH = 'path';
43
    const SORT_LOCATION_PRIORITY = 'priority';
44
    const SORT_LOCATION_VISIBILITY = 'visibility';
45
    const SORT_SECTION_IDENTIFIER = 'section_identifier';
46
    const SORT_SECTION_NAME = 'section_name';
47
48
    // useful f.e. when talking to Solr, which defaults to java integers for max nr of items for queries
49
    const INT_MAX_16BIT = 2147483647;
50
51
    static protected $operatorsMap = array(
52
        'eq' => Operator::EQ,
53
        'gt' => Operator::GT,
54
        'gte' => Operator::GTE,
55
        'lt' => Operator::LT,
56
        'lte' => Operator::LTE,
57
        'in' => Operator::IN,
58
        'between' => Operator::BETWEEN,
59
        'like' => Operator::LIKE,
60
        'contains' => Operator::CONTAINS,
61
        Operator::EQ => Operator::EQ,
62
        Operator::GT => Operator::GT,
63
        Operator::GTE => Operator::GTE,
64
        Operator::LT => Operator::LT,
65
        Operator::LTE => Operator::LTE,
66
    );
67
68
    /** @var  KeyMatcherInterface $groupMatcher */
69
    protected $groupMatcher;
70
    /** @var  KeyMatcherInterface $sectionMatcher */
71 80
    protected $sectionMatcher;
72
    /** @var  KeyMatcherInterface $stateMatcher */
73
    protected $stateMatcher;
74
    /** @var  KeyMatcherInterface $userMatcher */
75 80
    protected $userMatcher;
76 80
77 80
    /**
78 80
     * @param Repository $repository
79 80
     * @param KeyMatcherInterface $groupMatcher
80 80
     * @param KeyMatcherInterface $sectionMatcher
81
     * @param KeyMatcherInterface $stateMatcher
82
     * @param KeyMatcherInterface $userMatcher
83
     * @todo inject the services needed, not the whole repository
84
     */
85
    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...
86
        KeyMatcherInterface $sectionMatcher = null, KeyMatcherInterface $stateMatcher = null,
87
        KeyMatcherInterface $userMatcher = null)
88 13
    {
89
        parent::__construct($repository);
90 13
        $this->userMatcher = $userMatcher;
91 13
        $this->sectionMatcher = $sectionMatcher;
92
        $this->stateMatcher = $stateMatcher;
93
        $this->userMatcher = $userMatcher;
94
    }
95 13
96 3
    /**
97
     * @param $key
98 13
     * @param $values
99
     * @return mixed should it be \eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface ?
100 5
     * @throws \Exception for unsupported keys
101
     */
102 11
    protected function getQueryCriterion($key, $values)
103 1
    {
104
        if (!is_array($values)) {
105 11
            $values = array($values);
106 1
        }
107
108 11
        switch ($key) {
109 1
            case self::MATCH_CONTENT_ID:
110 1
                return new Query\Criterion\ContentId($values);
111 1
112 1
            case self::MATCH_LOCATION_ID:
113 1
                // NB: seems to cause problems with EZP 2014.3
114
                return new Query\Criterion\LocationId(reset($values));
115
116 1
            case self::MATCH_CONTENT_REMOTE_ID:
117
                return new Query\Criterion\RemoteId($values);
118 11
119 10
            case self::MATCH_LOCATION_REMOTE_ID:
120 3
                return new Query\Criterion\LocationRemoteId($values);
121
122 10
            case self::MATCH_ATTRIBUTE:
123 8
                $spec = reset($values);
124 10
                $attribute = key($values);
125
                $match = reset($spec);
126 2
                $operator = key($spec);
127 1
                if (!isset(self::$operatorsMap[$operator])) {
128 1
                    throw new \Exception("Can not use '$operator' as comparison operator for attributes");
129 1
                }
130
                return new Query\Criterion\Field($attribute, self::$operatorsMap[$operator], $match);
131
132 1
            case 'content_type_id':
133
            case self::MATCH_CONTENT_TYPE_ID:
134 2
                return new Query\Criterion\ContentTypeId($values);
135
136
            case 'content_type_identifier':
137
            case self::MATCH_CONTENT_TYPE_IDENTIFIER:
138
                return new Query\Criterion\ContentTypeIdentifier($values);
139
140 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...
141
                $match = reset($values);
142 2
                $operator = key($values);
143 1
                if (!isset(self::$operatorsMap[$operator])) {
144
                    throw new \Exception("Can not use '$operator' as comparison operator for dates");
145 2
                }
146
                return new Query\Criterion\DateMetadata(Query\Criterion\DateMetadata::CREATED, self::$operatorsMap[$operator], $match);
147
148 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...
149
                foreach($values as &$value) {
150
                    if (!ctype_digit($value)) {
151
                        $value = $this->groupMatcher->matchOneByKey($value)->id;
152
                    }
153 2
                }
154
                return new Query\Criterion\UserMetadata(Query\Criterion\UserMetadata::GROUP, Operator::IN, $values);
155
156
            case self::MATCH_LANGUAGE_CODE:
157
                return new Query\Criterion\LanguageCode($values);
158
159 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...
160
                $match = reset($values);
161 2
                $operator = key($values);
162 1
                if (!isset(self::$operatorsMap[$operator])) {
163 1
                    throw new \Exception("Can not use '$operator' as comparison operator for dates");
164 1
                }
165
                return new Query\Criterion\DateMetadata(Query\Criterion\DateMetadata::MODIFIED, self::$operatorsMap[$operator], $match);
166
167 1 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...
168
                foreach($values as &$value) {
169 2
                    if (!ctype_digit($value)) {
170 1
                        $value = $this->stateMatcher->matchOneByKey($value)->id;
171
                    }
172 2
                }
173 1
                return new Query\Criterion\ObjectStateId($values);
174 1
175 1 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...
176
                foreach($values as &$value) {
177 1
                    if (!ctype_digit($value)) {
178
                        $value = $this->userMatcher->matchOneByKey($value)->id;
179 1
                    }
180
                }
181 2
                return new Query\Criterion\UserMetadata(Query\Criterion\UserMetadata::OWNER, Operator::IN, $values);
182 2
183 2
            case self::MATCH_PARENT_LOCATION_ID:
184 2
                return new Query\Criterion\ParentLocationId($values);
185
186
            case self::MATCH_PARENT_LOCATION_REMOTE_ID:
187 2
                $locationIds = [];
188
                foreach ($values as $remoteParentLocationId) {
189 2
                    $location = $this->repository->getLocationService()->loadLocationByRemoteId($remoteParentLocationId);
190 1
                    // unique locations
191
                    $locationIds[$location->id] = $location->id;
192 2
                }
193
                return new Query\Criterion\ParentLocationId($locationIds);
194 2
195 2 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...
196
                foreach($values as &$value) {
197
                    if (!ctype_digit($value)) {
198 2
                        $value = $this->sectionMatcher->matchOneByKey($value)->id;
199
                    }
200
                }
201 2
                return new Query\Criterion\SectionId($values);
202 2
203 2
            case self::MATCH_SUBTREE:
204 2
                return new Query\Criterion\Subtree($values);
205 2
206
            case self::MATCH_VISIBILITY:
207 2
                /// @todo error/warning if there is more than 1 value...
208
                $value = reset($values);
209 2
                if ($value) {
210 1
                    return new Query\Criterion\Visibility(Query\Criterion\Visibility::VISIBLE);
211 1
                } else {
212 1
                    return new Query\Criterion\Visibility(Query\Criterion\Visibility::HIDDEN);
213 1
                }
214
215 1 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...
216
                $subCriteria = array();
217 2
                foreach($values as $subCriterion) {
218
                    $value = reset($subCriterion);
219 2
                    $subCriteria[] = $this->getQueryCriterion(key($subCriterion), $value);
220 2
                }
221 2
                return new Query\Criterion\LogicalAnd($subCriteria);
222
223 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...
224
                $subCriteria = array();
225
                foreach($values as $subCriterion) {
226
                    $value = reset($subCriterion);
227
                    $subCriteria[] = $this->getQueryCriterion(key($subCriterion), $value);
228
                }
229
                return new Query\Criterion\LogicalOr($subCriteria);
230
231
            case self::MATCH_NOT:
232
                /// @todo throw if more than one sub-criteria found
233
                $value = reset($values);
234
                $subCriterion = $this->getQueryCriterion(key($values), $value);
235
                return new Query\Criterion\LogicalNot($subCriterion);
236
237
            default:
238
                throw new \Exception($this->returns . " can not be matched because matching condition '$key' is not supported. Supported conditions are: " .
239
                    implode(', ', $this->allowedConditions));
240
        }
241
    }
242
243
    protected function getSortClauses(array $sortDefinition)
244
    {
245
        $out = array();
246
247
        foreach ($sortDefinition as $sortItem) {
248
249
            if (is_string($sortItem)) {
250
                $sortItem = array('sort_field' => $sortItem);
251
            }
252
            if (!is_array($sortItem) || !isset($sortItem['sort_field'])) {
253
                throw new \Exception("Missing sort_field element in sorting definition");
254
            }
255
            if (!isset($sortItem['sort_order'])) {
256
                // we have to pick a default ;-)
257
                $sortItem['sort_order'] = 'ASC';
258
            }
259
260
            $direction = $this->hash2SortOrder($sortItem['sort_order']);
261
262
            switch($sortItem['sort_field']) {
263
                case SELF::SORT_CONTENT_ID:
264
                    $out[] = new SortClause\ContentId($direction);
265
                    break;
266
                case SELF::SORT_CONTENT_NAME:
267
                    $out[] = new SortClause\ContentName($direction);
268
                    break;
269
                case SELF::SORT_DATE_MODIFIED:
270
                    $out[] = new SortClause\DateModified($direction);
271
                    break;
272
                case SELF::SORT_DATE_PUBLISHED:
273
                    $out[] = new SortClause\DatePublished($direction);
274
                    break;
275
                /// @todo
276
                //case SELF::SORT_FIELD:
277
                //    $out[] = new SortClause\Field($direction);
278
                //    break;
279
                case SELF::SORT_LOCATION_DEPTH:
280
                    $out[] = new SortClause\Location\Depth($direction);
281
                    break;
282
                case SELF::SORT_LOCATION_ID:
283
                    $out[] = new SortClause\Location\Id($direction);
284
                    break;
285
                case SELF::SORT_LOCATION_ISMAIN:
286
                    $out[] = new SortClause\Location\IsMainLocation($direction);
287
                    break;
288
                case SELF::SORT_LOCATION_PATH:
289
                    $out[] = new SortClause\Location\Path($direction);
290
                    break;
291
                case SELF::SORT_LOCATION_PRIORITY:
292
                    $out[] = new SortClause\Location\Priority($direction);
293
                    break;
294
                case SELF::SORT_LOCATION_VISIBILITY:
295
                    $out[] = new SortClause\Location\Visibility($direction);
296
                    break;
297
                case SELF::SORT_SECTION_IDENTIFIER:
298
                    $out[] = new SortClause\SectionIdentifier($direction);
299
                    break;
300
                case SELF::SORT_SECTION_NAME:
301
                    $out[] = new SortClause\SectionName($direction);
302
                    break;
303
                default:
304
                    throw new \Exception("Sort field '{$sortItem['sort_field']}' not implemented");
305
            }
306
        }
307
308
        return $out;
309
    }
310
311 View Code Duplication
    protected function hash2SortOrder($value)
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...
312
    {
313
        $sortOrder = null;
314
315
        if ($value !== null) {
316
            if (strtoupper($value) === 'ASC') {
317
                $sortOrder = Query::SORT_ASC;
318
            } else {
319
                $sortOrder = Query::SORT_DESC;
320
            }
321
        }
322
323
        return $sortOrder;
324
    }
325
326
    /**
327
     * @param int $value
328
     * @return string
329
     */
330
    protected function sortOrder2Hash($value)
331
    {
332
        if ($value === Query::SORT_ASC) {
333
            return 'ASC';
334
        } else {
335
            return 'DESC';
336
        }
337
    }
338
}
339