QueryBasedMatcher::getQueryCriterion()   F
last analyzed

Complexity

Conditions 45
Paths 88

Size

Total Lines 158
Code Lines 107

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 81
CRAP Score 64.7346

Importance

Changes 0
Metric Value
cc 45
eloc 107
nc 88
nop 2
dl 0
loc 158
ccs 81
cts 103
cp 0.7864
crap 64.7346
rs 3.3333
c 0
b 0
f 0

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\Query;
6
use eZ\Publish\API\Repository\Values\Content\Query\SortClause;
7
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator;
8
use eZ\Publish\API\Repository\Repository;
9
use eZ\Publish\Core\QueryType\QueryTypeRegistry;
0 ignored issues
show
Bug introduced by
The type eZ\Publish\Core\QueryType\QueryTypeRegistry was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
10
use Kaliop\eZMigrationBundle\API\KeyMatcherInterface;
11
use Kaliop\eZMigrationBundle\API\Exception\InvalidSortConditionsException;
12
use Kaliop\eZMigrationBundle\API\Exception\InvalidMatchConditionsException;
13
use PhpParser\Node\Expr\Isset_;
14
15
/**
16
 * @todo extend to allow matching by modifier, language code, content_type_group_id
17
 */
18
abstract class QueryBasedMatcher extends RepositoryMatcher
19
{
20
    const MATCH_CONTENT_ID = 'content_id';
21
    const MATCH_LOCATION_ID = 'location_id';
22
    const MATCH_CONTENT_REMOTE_ID = 'content_remote_id';
23
    const MATCH_LOCATION_REMOTE_ID = 'location_remote_id';
24
    const MATCH_ATTRIBUTE = 'attribute';
25
    const MATCH_CONTENT_TYPE_ID = 'content_type_id';
26
    const MATCH_CONTENT_TYPE_IDENTIFIER = 'content_type_identifier';
27
    const MATCH_CREATION_DATE = 'creation_date';
28
    const MATCH_GROUP = 'group';
29
    const MATCH_LANGUAGE_CODE = 'lang';
30
    const MATCH_MODIFICATION_DATE = 'modification_date';
31
    const MATCH_OBJECT_STATE = 'object_state';
32
    const MATCH_OWNER = 'owner';
33
    const MATCH_PARENT_LOCATION_ID = 'parent_location_id';
34
    const MATCH_PARENT_LOCATION_REMOTE_ID = 'parent_location_remote_id';
35
    const MATCH_QUERY_TYPE = 'query_type';
36
    const MATCH_SECTION = 'section';
37
    const MATCH_SUBTREE = 'subtree';
38
    const MATCH_VISIBILITY = 'visibility';
39
40
    const SORT_CONTENT_ID = 'content_id';
41
    const SORT_CONTENT_NAME = 'name';
42
    const SORT_DATE_MODIFIED = 'modified';
43
    const SORT_DATE_PUBLISHED = 'published';
44
    const SORT_LOCATION_DEPTH = 'depth';
45
    const SORT_LOCATION_ID = 'node_id';
46
    const SORT_LOCATION_ISMAIN = 'is_main';
47
    const SORT_LOCATION_PATH = 'path';
48
    const SORT_LOCATION_PRIORITY = 'priority';
49
    const SORT_LOCATION_VISIBILITY = 'visibility';
50
    const SORT_SECTION_IDENTIFIER = 'section_identifier';
51
    const SORT_SECTION_NAME = 'section_name';
52
53
    // useful f.e. when talking to Solr, which defaults to java integers for max nr of items for queries
54
    const INT_MAX_16BIT = 2147483647;
55
56
    static protected $operatorsMap = array(
57
        'eq' => Operator::EQ,
58
        'gt' => Operator::GT,
59
        'gte' => Operator::GTE,
60
        'lt' => Operator::LT,
61
        'lte' => Operator::LTE,
62
        'in' => Operator::IN,
63
        'between' => Operator::BETWEEN,
64
        'like' => Operator::LIKE,
65
        'contains' => Operator::CONTAINS,
66
        Operator::EQ => Operator::EQ,
67
        Operator::GT => Operator::GT,
68
        Operator::GTE => Operator::GTE,
69
        Operator::LT => Operator::LT,
70
        Operator::LTE => Operator::LTE,
71
    );
72
73
    /** @var  KeyMatcherInterface $groupMatcher */
74
    protected $groupMatcher;
75
    /** @var  KeyMatcherInterface $sectionMatcher */
76
    protected $sectionMatcher;
77
    /** @var  KeyMatcherInterface $stateMatcher */
78
    protected $stateMatcher;
79
    /** @var  KeyMatcherInterface $userMatcher */
80
    protected $userMatcher;
81
    /** @var int $queryLimit */
82
    protected $queryLimit;
83
    /** @var QueryTypeRegistry|null */
84
    protected $queryTypeRegistry;
85
86
    /**
87
     * @param Repository $repository
88
     * @param KeyMatcherInterface $groupMatcher
89
     * @param KeyMatcherInterface $sectionMatcher
90
     * @param KeyMatcherInterface $stateMatcher
91
     * @param KeyMatcherInterface $userMatcher
92
     * @param int $queryLimit passed to the repo as max. number of results to fetch. Important to avoid SOLR errors
93
     * @todo inject the services needed, not the whole repository
94
     */
95 149
    public function __construct(Repository $repository, KeyMatcherInterface $groupMatcher = null,
96
        KeyMatcherInterface $sectionMatcher = null, KeyMatcherInterface $stateMatcher = null,
97
        KeyMatcherInterface $userMatcher = null, $queryLimit = null, $queryTypeRegistry = null)
98
    {
99 149
        parent::__construct($repository);
100 149
        $this->groupMatcher = $groupMatcher;
101 149
        $this->sectionMatcher = $sectionMatcher;
102 149
        $this->stateMatcher = $stateMatcher;
103 149
        $this->userMatcher = $userMatcher;
104 149
        $this->queryTypeRegistry = $queryTypeRegistry;
105
106 149
        if ($queryLimit !== null) {
107 149
            $this->queryLimit = (int)$queryLimit;
108
        } else {
109
            $this->queryLimit = self::INT_MAX_16BIT;
110
        }
111 149
    }
112
113
    /**
114
     * @param $key
115
     * @param $values
116
     * @return mixed should it be \eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface ?
117
     * @throws InvalidMatchConditionsException for unsupported keys
118
     */
119 22
    protected function getQueryCriterion($key, $values)
120
    {
121 22
        if (!is_array($values)) {
122 22
            $values = array($values);
123
        }
124
125
        switch ($key) {
126 22
            case self::MATCH_CONTENT_ID:
127 3
                return new Query\Criterion\ContentId($values);
128
129 22
            case self::MATCH_LOCATION_ID:
130
                // NB: seems to cause problems with EZP 2014.3
131 7
                return new Query\Criterion\LocationId(reset($values));
132
133 18
            case self::MATCH_CONTENT_REMOTE_ID:
134 1
                return new Query\Criterion\RemoteId($values);
135
136 18
            case self::MATCH_LOCATION_REMOTE_ID:
137 2
                return new Query\Criterion\LocationRemoteId($values);
138
139 18
            case self::MATCH_ATTRIBUTE:
140
                /// @todo support filtering on multiple fields - in that case wrap many criteria in an AND one
141 1
                if (count($values) > 1) {
142
                    throw new InvalidMatchConditionsException("Can not use " . self::MATCH_ATTRIBUTE . " as comparison operator for multiple attributes. Use an AND condition instead");
143
                }
144 1
                $spec = reset($values);
145 1
                $attribute = key($values);
146 1
                if (is_array($spec)) {
147 1
                    if (count($spec) > 1) {
148
                        throw new InvalidMatchConditionsException("Can not use multiple operators for matching attribute $attribute");
149
                    }
150 1
                    $match = reset($spec);
151 1
                    $operator = key($spec);
152 1
                    if (!isset(self::$operatorsMap[$operator])) {
153
                        throw new InvalidMatchConditionsException("Can not use '$operator' as comparison operator for attributes");
154
                    }
155 1
                    return new Query\Criterion\Field($attribute, self::$operatorsMap[$operator], $match);
156
                } else {
157
                    /// @todo make the list of unary operators more flexible
158
                    switch ($spec) {
159
                        case 'empty':
160
                            return new Query\Criterion\IsFieldEmpty($attribute);
0 ignored issues
show
Bug introduced by
The type eZ\Publish\API\Repositor...\Criterion\IsFieldEmpty was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
161
                        default:
162
                            throw new InvalidMatchConditionsException("Can not use '$spec' as comparison operator for attributes");
163
                    }
164
                }
165
166 18
            case 'contenttype_id':
167 18
            case self::MATCH_CONTENT_TYPE_ID:
168 3
                return new Query\Criterion\ContentTypeId($values);
169
170 17
            case 'contenttype_identifier':
171 14
            case self::MATCH_CONTENT_TYPE_IDENTIFIER:
172 17
                return new Query\Criterion\ContentTypeIdentifier($values);
173
174 3
            case self::MATCH_CREATION_DATE:
175 1
                $match = reset($values);
176 1
                $operator = key($values);
177 1
                if (!isset(self::$operatorsMap[$operator])) {
178
                    throw new InvalidMatchConditionsException("Can not use '$operator' as comparison operator for dates");
179
                }
180 1
                return new Query\Criterion\DateMetadata(Query\Criterion\DateMetadata::CREATED, self::$operatorsMap[$operator], $match);
181
182 3
            case self::MATCH_GROUP:
183 1
                foreach ($values as &$value) {
184 1
                    if (!ctype_digit($value)) {
185 1
                        $value = $this->groupMatcher->matchOneByKey($value)->id;
186
                    }
187
                }
188 1
                return new Query\Criterion\UserMetadata(Query\Criterion\UserMetadata::GROUP, Operator::IN, $values);
189
190 2
            case self::MATCH_LANGUAGE_CODE:
191 1
                return new Query\Criterion\LanguageCode($values);
192
193 2
            case self::MATCH_MODIFICATION_DATE:
194
                $match = reset($values);
195
                $operator = key($values);
196
                if (!isset(self::$operatorsMap[$operator])) {
197
                    throw new InvalidMatchConditionsException("Can not use '$operator' as comparison operator for dates");
198
                }
199
                return new Query\Criterion\DateMetadata(Query\Criterion\DateMetadata::MODIFIED, self::$operatorsMap[$operator], $match);
200
201 2
            case self::MATCH_OBJECT_STATE:
202
                foreach ($values as &$value) {
203
                    if (!ctype_digit($value)) {
204
                        $value = $this->stateMatcher->matchOneByKey($value)->id;
205
                    }
206
                }
207
                return new Query\Criterion\ObjectStateId($values);
208
209 2
            case self::MATCH_OWNER:
210 1
                foreach ($values as &$value) {
211 1
                    if (!ctype_digit($value)) {
212 1
                        $value = $this->userMatcher->matchOneByKey($value)->id;
213
                    }
214
                }
215 1
                return new Query\Criterion\UserMetadata(Query\Criterion\UserMetadata::OWNER, Operator::IN, $values);
216
217 2
            case self::MATCH_PARENT_LOCATION_ID:
218 1
                return new Query\Criterion\ParentLocationId($values);
219
220 2
            case self::MATCH_PARENT_LOCATION_REMOTE_ID:
221 1
                $locationIds = [];
222 1
                foreach ($values as $remoteParentLocationId) {
223 1
                    $location = $this->repository->getLocationService()->loadLocationByRemoteId($remoteParentLocationId);
224
                    // unique locations
225 1
                    $locationIds[$location->id] = $location->id;
226
                }
227 1
                return new Query\Criterion\ParentLocationId($locationIds);
228
229 2
            case self::MATCH_SECTION:
230 2
                foreach ($values as &$value) {
231 2
                    if (!ctype_digit($value)) {
232 2
                        $value = $this->sectionMatcher->matchOneByKey($value)->id;
233
                    }
234
                }
235 2
                return new Query\Criterion\SectionId($values);
236
237 2
            case self::MATCH_SUBTREE:
238 1
                return new Query\Criterion\Subtree($values);
239
240 2
            case self::MATCH_VISIBILITY:
241
                /// @todo error/warning if there is more than 1 value...
242 2
                $value = reset($values);
243 2
                if ($value) {
244
                    return new Query\Criterion\Visibility(Query\Criterion\Visibility::VISIBLE);
245
                } else {
246 2
                    return new Query\Criterion\Visibility(Query\Criterion\Visibility::HIDDEN);
247
                }
248
249 2
            case self::MATCH_AND:
250 2
                $subCriteria = array();
251 2
                foreach ($values as $subCriterion) {
252 2
                    $value = reset($subCriterion);
253 2
                    $subCriteria[] = $this->getQueryCriterion(key($subCriterion), $value);
254
                }
255 2
                return new Query\Criterion\LogicalAnd($subCriteria);
256
257 2
            case self::MATCH_OR:
258 1
                $subCriteria = array();
259 1
                foreach ($values as $subCriterion) {
260 1
                    $value = reset($subCriterion);
261 1
                    $subCriteria[] = $this->getQueryCriterion(key($subCriterion), $value);
262
                }
263 1
                return new Query\Criterion\LogicalOr($subCriteria);
264
265 2
            case self::MATCH_NOT:
266
                /// @todo throw if more than one sub-criteria found
267 2
                $value = reset($values);
268 2
                $subCriterion = $this->getQueryCriterion(key($values), $value);
269 2
                return new Query\Criterion\LogicalNot($subCriterion);
270
271
            case self::MATCH_QUERY_TYPE:
272
                throw new InvalidMatchConditionsException($this->returns . " can not use a QueryType as sub-condition");
273
274
            default:
275
                throw new InvalidMatchConditionsException($this->returns . " can not be matched because matching condition '$key' is not supported. Supported conditions are: " .
276
                    implode(', ', $this->allowedConditions));
277
        }
278
    }
279
280
    /**
281
     * @param array $sortDefinition
282
     * @return array
283
     * @throws InvalidSortConditionsException
284
     */
285
    protected function getSortClauses(array $sortDefinition)
286
    {
287
        $out = array();
288
289
        foreach ($sortDefinition as $sortItem) {
290
291
            if (is_string($sortItem)) {
292
                $sortItem = array('sort_field' => $sortItem);
293
            }
294
            if (!is_array($sortItem) || !isset($sortItem['sort_field'])) {
295
                throw new InvalidSortConditionsException("Missing sort_field element in sorting definition");
296
            }
297
            if (!isset($sortItem['sort_order'])) {
298
                // we have to pick a default ;-)
299
                $sortItem['sort_order'] = 'ASC';
300
            }
301
302
            $direction = $this->hash2SortOrder($sortItem['sort_order']);
303
304
            switch ($sortItem['sort_field']) {
305
                case self::SORT_CONTENT_ID:
306
                    $out[] = new SortClause\ContentId($direction);
307
                    break;
308
                case self::SORT_CONTENT_NAME:
309
                    $out[] = new SortClause\ContentName($direction);
310
                    break;
311
                case self::SORT_DATE_MODIFIED:
312
                    $out[] = new SortClause\DateModified($direction);
313
                    break;
314
                case self::SORT_DATE_PUBLISHED:
315
                    $out[] = new SortClause\DatePublished($direction);
316
                    break;
317
                /// @todo
318
                //case self::SORT_FIELD:
319
                //    $out[] = new SortClause\Field($direction);
320
                //    break;
321
                case self::SORT_LOCATION_DEPTH:
322
                    $out[] = new SortClause\Location\Depth($direction);
323
                    break;
324
                case self::SORT_LOCATION_ID:
325
                    $out[] = new SortClause\Location\Id($direction);
326
                    break;
327
                case self::SORT_LOCATION_ISMAIN:
328
                    $out[] = new SortClause\Location\IsMainLocation($direction);
329
                    break;
330
                case self::SORT_LOCATION_PATH:
331
                    $out[] = new SortClause\Location\Path($direction);
332
                    break;
333
                case self::SORT_LOCATION_PRIORITY:
334
                    $out[] = new SortClause\Location\Priority($direction);
335
                    break;
336
                case self::SORT_LOCATION_VISIBILITY:
337
                    $out[] = new SortClause\Location\Visibility($direction);
338
                    break;
339
                case self::SORT_SECTION_IDENTIFIER:
340
                    $out[] = new SortClause\SectionIdentifier($direction);
341
                    break;
342
                case self::SORT_SECTION_NAME:
343
                    $out[] = new SortClause\SectionName($direction);
344
                    break;
345
                default:
346
                    throw new InvalidSortConditionsException("Sort field '{$sortItem['sort_field']}' not implemented");
347
            }
348
        }
349
350
        return $out;
351
    }
352
353
    /**
354
     * @param $queryTypeDef
355
     * @return Query
356
     * @throws InvalidMatchConditionsException
357
     */
358 1
    protected function getQueryByQueryType($queryTypeDef)
359
    {
360 1
        if ($this->queryTypeRegistry == null) {
361
            throw new InvalidMatchConditionsException('Matching by query_type is not supported with this eZP version');
362
        }
363 1
        if (is_string($queryTypeDef)) {
364
            $queryTypeDef = array('name' => $queryTypeDef);
365
        }
366 1
        if (!isset($queryTypeDef['name'])) {
367
            throw new InvalidMatchConditionsException("Matching by query_type is not supported without 'name'");
368
        }
369
370 1
        $qt = $this->queryTypeRegistry->getQueryType($queryTypeDef['name']);
371 1
        $q = $qt->getQuery(isset($queryTypeDef['parameters']) ? $queryTypeDef['parameters'] : array());
372 1
        return $q;
373
    }
374
375
    /**
376
     * @todo investigate how to better return the 'legacy' (db based) search engine even when a Solr-based one is available
377
     * @return \eZ\Publish\API\Repository\SearchService
378
     */
379 23
    protected function getSearchService()
380
    {
381 23
        return $this->repository->getSearchService();
382
    }
383
384
    protected function hash2SortOrder($value)
385
    {
386
        $sortOrder = null;
387
388
        if ($value !== null) {
389
            if (strtoupper($value) === 'ASC') {
390
                $sortOrder = Query::SORT_ASC;
391
            } else {
392
                $sortOrder = Query::SORT_DESC;
393
            }
394
        }
395
396
        return $sortOrder;
397
    }
398
399
    /**
400
     * @param int $value
401
     * @return string
402
     */
403
    protected function sortOrder2Hash($value)
404
    {
405
        if ($value === Query::SORT_ASC) {
0 ignored issues
show
introduced by
The condition $value === eZ\Publish\AP...Content\Query::SORT_ASC is always false.
Loading history...
406
            return 'ASC';
407
        } else {
408
            return 'DESC';
409
        }
410
    }
411
}
412