Passed
Push — master ( ab917e...27e7e9 )
by Gaetano
05:46
created

ContentMatcher::getDefaultSortClauses()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 12
nc 5
nop 0
dl 0
loc 23
ccs 0
cts 0
cp 0
crap 20
rs 9.8666
c 0
b 0
f 0
1
<?php
2
3
namespace Kaliop\eZMigrationBundle\Core\Matcher;
4
5
use eZ\Publish\API\Repository\Exceptions\NotImplementedException;
6
use eZ\Publish\API\Repository\Values\Content\Content;
7
use eZ\Publish\API\Repository\Values\Content\Query;
8
use Kaliop\eZMigrationBundle\API\Collection\ContentCollection;
9
use Kaliop\eZMigrationBundle\API\Exception\InvalidMatchConditionsException;
10
use Kaliop\eZMigrationBundle\API\Exception\InvalidSortConditionsException;
11
use Kaliop\eZMigrationBundle\API\SortingMatcherInterface;
12
use Kaliop\eZMigrationBundle\API\Exception\InvalidMatchResultsNumberException;
13
14
class ContentMatcher extends QueryBasedMatcher implements SortingMatcherInterface
15
{
16
    use FlexibleKeyMatcherTrait;
17
18
    const MATCH_RELATES_TO = 'relates_to';
19
    const MATCH_RELATED_FROM = 'related_from';
20
21
    protected $allowedConditions = array(
22
        self::MATCH_AND, self::MATCH_OR, self::MATCH_NOT,
23
        self::MATCH_CONTENT_ID, self::MATCH_LOCATION_ID, self::MATCH_CONTENT_REMOTE_ID, self::MATCH_LOCATION_REMOTE_ID,
24
        self::MATCH_ATTRIBUTE, self::MATCH_CONTENT_TYPE_ID, self::MATCH_CONTENT_TYPE_IDENTIFIER, self::MATCH_GROUP,
25
        self::MATCH_CREATION_DATE, self::MATCH_MODIFICATION_DATE, self::MATCH_OBJECT_STATE, self::MATCH_OWNER,
26
        self::MATCH_PARENT_LOCATION_ID, self::MATCH_PARENT_LOCATION_REMOTE_ID, self::MATCH_SECTION, self::MATCH_SUBTREE,
27
        self::MATCH_VISIBILITY,
28
        // aliases
29
        'content_type', 'content_type_id', 'content_type_identifier',
30
        // content-only
31
        self::MATCH_RELATES_TO, self::MATCH_RELATED_FROM
32
    );
33
    protected $returns = 'Content';
34
35
    /**
36
     * @param array $conditions key: condition, value: int / string / int[] / string[]
37
     * @param array $sort
38
     * @param int $offset
39
     * @param int $limit
40
     * @return ContentCollection
41
     * @throws InvalidMatchConditionsException
42
     * @throws InvalidSortConditionsException
43 16
     */
44
    public function match(array $conditions, array $sort = array(), $offset = 0, $limit = 0)
45 16
    {
46
        return $this->matchContent($conditions, $sort, $offset, $limit);
47
    }
48
49
    /**
50
     * @param array $conditions
51
     * @param array $sort
52
     * @param int $offset
53
     * @return Content
54
     * @throws InvalidMatchResultsNumberException
55
     * @throws InvalidMatchConditionsException
56
     * @throws InvalidSortConditionsException
57 2
     */
58
    public function matchOne(array $conditions, array $sort = array(), $offset = 0)
59 2
    {
60 2
        $results = $this->match($conditions, $sort, $offset, 2);
61 2
        $count = count($results);
62
        if ($count !== 1) {
63
            throw new InvalidMatchResultsNumberException("Found $count " . $this->returns . " when expected exactly only one to match the conditions");
64 2
        }
65
        return reset($results);
0 ignored issues
show
Bug introduced by
$results of type Kaliop\eZMigrationBundle...ction\ContentCollection is incompatible with the type array expected by parameter $array of reset(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

65
        return reset(/** @scrutinizer ignore-type */ $results);
Loading history...
66
    }
67
68
    /**
69
     * @param array $conditions key: condition, value: int / string / int[] / string[]
70
     * @param array $sort
71
     * @param int $offset
72
     * @param int $limit
73
     * @return ContentCollection
74
     * @throws InvalidMatchConditionsException
75
     * @throws InvalidSortConditionsException
76 13
     */
77
    public function matchContent(array $conditions, array $sort = array(), $offset = 0, $limit = 0)
78 13
    {
79
        $this->validateConditions($conditions);
80 13
81
        foreach ($conditions as $key => $values) {
82
83
            switch($key) {
84 13
85
                case self::MATCH_RELATES_TO:
86
                    $contentService = $this->repository->getContentService();
87
                    $contents = array();
88
                    // allow to specify the objects to relate to using different ways
89
                    $relatedContents = $this->match($values);
90
                    foreach($relatedContents as $relatedContent) {
91
                        foreach($contentService->loadReverseRelations($relatedContent->contentInfo) as $relatingContent) {
92
                            $contents[$relatingContent->contentInfo->id] = $relatingContent;
93
                        }
94
                    }
95
                    break;
96 13
97
                case self::MATCH_RELATED_FROM:
98
                    $contentService = $this->repository->getContentService();
99
                    $contents = array();
100
                    // allow to specify the objects we relate to using different ways
101
                    $relatingContents = $this->match($values);
102
                    foreach($relatingContents as $relatingContent) {
103
                        foreach($contentService->loadRelations($relatingContent->contentInfo) as $relatedContent) {
104
                            $contents[$relatedContent->contentInfo->id] = $relatedContent;
105
                        }
106
                    }
107
                    break;
108
109
                default:
110
111 13
                    // BC support
112 5
                    if ($key == 'content_type') {
113
                        if (is_int($values[0]) || ctype_digit($values[0])) {
114
                            $key = self::MATCH_CONTENT_TYPE_ID;
115 5
                        } else {
116
                            $key = self::MATCH_CONTENT_TYPE_IDENTIFIER;
117
                        }
118
                    }
119 13
120 13
                    $query = new Query();
121 13
                    $query->limit = $limit != 0 ? $limit : $this->queryLimit;
122 13
                    $query->offset = $offset;
123 13
                    if (isset($query->performCount)) $query->performCount = false;
124 13
                    $query->filter = $this->getQueryCriterion($key, $values);
125
                    if (!empty($sort)) {
126
                        $query->sortClauses = $this->getSortClauses($sort);
127
                    } else {
128 13
                        switch ($key) {
129 12
                            case 'content_type_id':
130 12
                            case self::MATCH_CONTENT_TYPE_ID:
131 10
                            case 'content_type_identifier':
132
                            case self::MATCH_CONTENT_TYPE_IDENTIFIER:
133
                                // sort objects by depth, lower to higher, so that deleting them has less chances of failure
134 11
                                // NB: we only do this in eZP versions that allow depth sorting on content queries
135
                                if ($sortClauses = $this->getDefaultSortClauses()) {
136
                                    $query->sortClauses = $sortClauses;
137
                                }
138
                        }
139
                    }
140 13
141
                    $results = $this->getSearchService()->findContent($query);
142 13
143 13
                    $contents = [];
144
                    foreach ($results->searchHits as $result) {
145 13
                        // make sure we return every object only once
146
                        $contents[$result->valueObject->contentInfo->id] = $result->valueObject;
147
                    }
148
            }
149 13
150
            return new ContentCollection($contents);
151
152
        }
153
    }
154
155
    /**
156
     * When matching by key, we accept content Id and remote Id only
157
     * @param int|string $key
158
     * @return array
159 2
     */
160
    protected function getConditionsFromKey($key)
161 2
    {
162 2
        if (is_int($key) || ctype_digit($key)) {
163
            return array(self::MATCH_CONTENT_ID => $key);
164
        }
165
        return array(self::MATCH_CONTENT_REMOTE_ID => $key);
166
    }
167
168
    /**
169
     * @param int[] $contentIds
170
     * @return Content[]
171
     * @deprecated
172
     */
173
    protected function findContentsByContentIds(array $contentIds)
174
    {
175
        $contents = [];
176
177
        foreach ($contentIds as $contentId) {
178
            // return unique contents
179
            $content = $this->repository->getContentService()->loadContent($contentId);
180
            $contents[$content->contentInfo->id] = $content;
181
        }
182
183
        return $contents;
184
    }
185
186
    /**
187
     * @param string[] $remoteContentIds
188
     * @return Content[]
189
     * @deprecated
190
     */
191
    protected function findContentsByContentRemoteIds(array $remoteContentIds)
192
    {
193
        $contents = [];
194
195
        foreach ($remoteContentIds as $remoteContentId) {
196
            // return unique contents
197
            $content = $this->repository->getContentService()->loadContentByRemoteId($remoteContentId);
198
            $contents[$content->contentInfo->id] = $content;
199
        }
200
201
        return $contents;
202
    }
203
204
    /**
205
     * @param int[] $locationIds
206
     * @return Content[]
207
     * @deprecated
208
     */
209
    protected function findContentsByLocationIds(array $locationIds)
210
    {
211
        $contentIds = [];
212
213
        foreach ($locationIds as $locationId) {
214
            $location = $this->repository->getLocationService()->loadLocation($locationId);
215
            // return unique ids
216
            $contentIds[$location->contentId] = $location->contentId;
217
        }
218
219
        return $this->findContentsByContentIds($contentIds);
0 ignored issues
show
Deprecated Code introduced by
The function Kaliop\eZMigrationBundle...dContentsByContentIds() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

219
        return /** @scrutinizer ignore-deprecated */ $this->findContentsByContentIds($contentIds);
Loading history...
220
    }
221
222
    /**
223
     * @param string[] $remoteLocationIds
224
     * @return Content[]
225
     * @deprecated
226
     */
227
    protected function findContentsByLocationRemoteIds($remoteLocationIds)
228
    {
229
        $contentIds = [];
230
231
        foreach ($remoteLocationIds as $remoteLocationId) {
232
            $location = $this->repository->getLocationService()->loadLocationByRemoteId($remoteLocationId);
233
            // return unique ids
234
            $contentIds[$location->contentId] = $location->contentId;
235
        }
236
237
        return $this->findContentsByContentIds($contentIds);
0 ignored issues
show
Deprecated Code introduced by
The function Kaliop\eZMigrationBundle...dContentsByContentIds() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

237
        return /** @scrutinizer ignore-deprecated */ $this->findContentsByContentIds($contentIds);
Loading history...
238
    }
239
240
    /**
241
     * @param int[] $parentLocationIds
242
     * @return Content[]
243
     * @deprecated
244
     */
245
    protected function findContentsByParentLocationIds($parentLocationIds)
246
    {
247
        $query = new Query();
248
        $query->limit = $this->queryLimit;
249
        if (isset($query->performCount)) $query->performCount = false;
250
        $query->filter = new Query\Criterion\ParentLocationId($parentLocationIds);
251
        $results = $this->getSearchService()->findContent($query);
252
253
        $contents = [];
254
        foreach ($results->searchHits as $result) {
255
            // make sure we return every object only once
256
            $contents[$result->valueObject->contentInfo->id] = $result->valueObject;
257
        }
258
259
        return $contents;
260
    }
261
262
    /**
263
     * @param string[] $remoteParentLocationIds
264
     * @return Content[]
265
     * @deprecated
266
     */
267
    protected function findContentsByParentLocationRemoteIds($remoteParentLocationIds)
268
    {
269
        $locationIds = [];
270
271
        foreach ($remoteParentLocationIds as $remoteParentLocationId) {
272
            $location = $this->repository->getLocationService()->loadLocationByRemoteId($remoteParentLocationId);
273
            // unique locations
274
            $locationIds[$location->id] = $location->id;
275
        }
276
277
        return $this->findContentsByParentLocationIds($locationIds);
0 ignored issues
show
Deprecated Code introduced by
The function Kaliop\eZMigrationBundle...tsByParentLocationIds() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

277
        return /** @scrutinizer ignore-deprecated */ $this->findContentsByParentLocationIds($locationIds);
Loading history...
278
    }
279
280
    /**
281
     * @param string[] $contentTypeIdentifiers
282
     * @return Content[]
283
     * @deprecated
284
     */
285
    protected function findContentsByContentTypeIdentifiers(array $contentTypeIdentifiers)
286
    {
287
        $query = new Query();
288
        $query->limit = $this->queryLimit;
289
        if (isset($query->performCount)) $query->performCount = false;
290
        $query->filter = new Query\Criterion\ContentTypeIdentifier($contentTypeIdentifiers);
291
        // sort objects by depth, lower to higher, so that deleting them has less chances of failure
292
        // NB: we only do this in eZP versions that allow depth sorting on content queries
293
        if ($sortClauses = $this->getDefaultSortClauses()) {
294
            $query->sortClauses = $sortClauses;
295
        }
296
297
        $results = $this->getSearchService()->findContent($query);
298
299
        $contents = [];
300
        foreach ($results->searchHits as $result) {
301
            // make sure we return every object only once
302
            $contents[$result->valueObject->contentInfo->id] = $result->valueObject;
303
        }
304
305
        return $contents;
306
    }
307
308
    /**
309
     * @param int[] $contentTypeIds
310
     * @return Content[]
311
     * @deprecated
312
     */
313
    protected function findContentsByContentTypeIds(array $contentTypeIds)
314
    {
315
        $query = new Query();
316
        $query->limit = $this->queryLimit;
317
        if (isset($query->performCount)) $query->performCount = false;
318
        $query->filter = new Query\Criterion\ContentTypeId($contentTypeIds);
319
        // sort objects by depth, lower to higher, so that deleting them has less chances of failure
320
        // NB: we only do this in eZP versions that allow depth sorting on content queries
321
        if ($sortClauses = $this->getDefaultSortClauses()) {
322
            $query->sortClauses = $sortClauses;
323
        }
324
        $results = $this->getSearchService()->findContent($query);
325
326
        $contents = [];
327
        foreach ($results->searchHits as $result) {
328
            // make sure we return every object only once
329
            $contents[$result->valueObject->contentInfo->id] = $result->valueObject;
330
        }
331
332
        return $contents;
333
    }
334
335
    /**
336
     * @return false|\eZ\Publish\API\Repository\Values\Content\Query\SortClause[]
337
     */
338
    protected function getDefaultSortClauses()
339
    {
340
        if (class_exists('eZ\Publish\API\Repository\Values\Content\Query\SortClause\LocationDepth')) {
341
            // Work around the fact that, on eZP 5.4 with recent ezplatform-solr-search-engine versions, sort class
342
            // LocationDepth does exist, but it is not supported by the Solr-based search engine.
343
            // The best workaround that we found so far: test a dummy query!
344
            $searchService = $this->getSearchService();
0 ignored issues
show
Unused Code introduced by
The assignment to $searchService is dead and can be removed.
Loading history...
345
            if (class_exists('EzSystems\EzPlatformSolrSearchEngine\Handler') /*&& $searchService instanceof */) {
346
                try {
347
                    $query = new Query();
348
                    $query->limit = 1;
349
                    $query->performCount = false;
350
                    $query->filter = new Query\Criterion\ContentTypeIdentifier('this_is_a_very_unlikely_content_type_identifier');
351
                    //$query->filter = new Query\Criterion\ContentTypeId($contentTypeIds);
352
                } catch(NotImplementedException $e) {
353
                    return false;
354
                }
355
            }
356
357
            return 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: Since 5.3, use Location search instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

357
            return array(/** @scrutinizer ignore-deprecated */ new Query\SortClause\LocationDepth(Query::SORT_DESC));
Loading history...
358
        }
359
360
        return false;
361
    }
362
}
363