Completed
Push — master ( e2d26f...9ff6cb )
by André
24:47
created

SearchService   B

Complexity

Total Complexity 34

Size/Duplication

Total Lines 313
Duplicated Lines 1.92 %

Coupling/Cohesion

Components 2
Dependencies 15

Importance

Changes 0
Metric Value
dl 6
loc 313
rs 8.4332
c 0
b 0
f 0
wmc 34
lcom 2
cbo 15

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 18 1
A findContent() 0 17 4
A findContentInfo() 0 11 2
B internalFindContentInfo() 3 31 6
A validateContentCriteria() 0 14 4
A validateContentSortClauses() 0 8 3
B findSingle() 0 18 5
A suggest() 0 3 1
C findLocations() 3 35 7
A getSortClauseFromLocation() 0 4 1

How to fix   Duplicated Code   

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:

1
<?php
2
3
/**
4
 * File containing the eZ\Publish\Core\Repository\SearchService class.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 */
9
namespace eZ\Publish\Core\Repository;
10
11
use eZ\Publish\API\Repository\SearchService as SearchServiceInterface;
12
use eZ\Publish\API\Repository\SearchServiceSortClause as SearchServiceSortClauseInterface;
13
use eZ\Publish\API\Repository\Values\Content\Location;
14
use eZ\Publish\API\Repository\Values\Content\Query\Criterion;
15
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalOperator;
16
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Location as LocationCriterion;
17
use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location as LocationSortClause;
18
use eZ\Publish\API\Repository\Values\Content\Query;
19
use eZ\Publish\API\Repository\Values\Content\LocationQuery;
20
use eZ\Publish\API\Repository\Repository as RepositoryInterface;
21
use eZ\Publish\API\Repository\Values\Content\Search\SearchResult;
22
use eZ\Publish\Core\Base\Exceptions\NotFoundException;
23
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException;
24
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType;
25
use eZ\Publish\Core\Repository\Mapper\SortClauseMapper;
26
use eZ\Publish\SPI\Search\Handler;
27
28
/**
29
 * Search service.
30
 */
31
class SearchService implements SearchServiceInterface, SearchServiceSortClauseInterface
0 ignored issues
show
Deprecated Code introduced by
The interface eZ\Publish\API\Repository\SearchServiceSortClause has been deprecated with message: in 6.7, will be moved into SearchService in 7.0

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...
32
{
33
    /**
34
     * @var \eZ\Publish\Core\Repository\Repository
35
     */
36
    protected $repository;
37
38
    /**
39
     * @var \eZ\Publish\SPI\Search\Handler
40
     */
41
    protected $searchHandler;
42
43
    /**
44
     * @var array
45
     */
46
    protected $settings;
47
48
    /**
49
     * @var \eZ\Publish\Core\Repository\Helper\DomainMapper
50
     */
51
    protected $domainMapper;
52
53
    /**
54
     * @var \eZ\Publish\Core\Repository\PermissionsCriterionHandler
55
     */
56
    protected $permissionsCriterionHandler;
57
58
    /**
59
     * @var \eZ\Publish\Core\Repository\Mapper\SortClauseMapper
60
     */
61
    protected $sortClauseMapper;
62
63
    /**
64
     * Setups service with reference to repository object that created it & corresponding handler.
65
     *
66
     * @param \eZ\Publish\API\Repository\Repository $repository
67
     * @param \eZ\Publish\SPI\Search\Handler $searchHandler
68
     * @param \eZ\Publish\Core\Repository\Helper\DomainMapper $domainMapper
69
     * @param \eZ\Publish\Core\Repository\PermissionsCriterionHandler $permissionsCriterionHandler
70
     * @param \eZ\Publish\Core\Repository\Mapper\SortClauseMapper $sortClauseMapper
71
     * @param array $settings
72
     */
73
    public function __construct(
74
        RepositoryInterface $repository,
75
        Handler $searchHandler,
76
        Helper\DomainMapper $domainMapper,
77
        PermissionsCriterionHandler $permissionsCriterionHandler,
78
        SortClauseMapper $sortClauseMapper,
79
        array $settings = array()
80
    ) {
81
        $this->repository = $repository;
0 ignored issues
show
Documentation Bug introduced by
$repository is of type object<eZ\Publish\API\Repository\Repository>, but the property $repository was declared to be of type object<eZ\Publish\Core\Repository\Repository>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
82
        $this->searchHandler = $searchHandler;
83
        $this->domainMapper = $domainMapper;
84
        // Union makes sure default settings are ignored if provided in argument
85
        $this->settings = $settings + array(
86
            //'defaultSetting' => array(),
87
        );
88
        $this->permissionsCriterionHandler = $permissionsCriterionHandler;
89
        $this->sortClauseMapper = $sortClauseMapper;
90
    }
91
92
    /**
93
     * Finds content objects for the given query.
94
     *
95
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if query is not valid
96
     *
97
     * @param \eZ\Publish\API\Repository\Values\Content\Query $query
98
     * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on.
99
     *        Currently supports: <code>array("languages" => array(<language1>,..), "useAlwaysAvailable" => bool)</code>
100
     *                            useAlwaysAvailable defaults to true to avoid exceptions on missing translations.
101
     * @param bool $filterOnUserPermissions if true only the objects which the user is allowed to read are returned.
102
     *
103
     * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult
104
     */
105
    public function findContent(Query $query, array $languageFilter = array(), $filterOnUserPermissions = true)
106
    {
107
        $contentService = $this->repository->getContentService();
108
        $result = $this->internalFindContentInfo($query, $languageFilter, $filterOnUserPermissions);
109
        foreach ($result->searchHits as $hit) {
110
            // As we get ContentInfo from SPI, we need to load full content (avoids getting stale content data)
111
            $hit->valueObject = $contentService->internalLoadContent(
0 ignored issues
show
Bug introduced by
The method internalLoadContent() does not exist on eZ\Publish\API\Repository\ContentService. Did you maybe mean loadContent()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
112
                $hit->valueObject->id,
113
                (!empty($languageFilter['languages']) ? $languageFilter['languages'] : null),
114
                null,
115
                false,
116
                (isset($languageFilter['useAlwaysAvailable']) ? $languageFilter['useAlwaysAvailable'] : true)
117
            );
118
        }
119
120
        return $result;
121
    }
122
123
    /**
124
     * Finds contentInfo objects for the given query.
125
     *
126
     * @see SearchServiceInterface::findContentInfo()
127
     *
128
     * @since 5.4.5
129
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if query is not valid
130
     *
131
     * @param \eZ\Publish\API\Repository\Values\Content\Query $query
132
     * @param array $languageFilter - a map of filters for the returned fields.
133
     *        Currently supports: <code>array("languages" => array(<language1>,..), "useAlwaysAvailable" => bool)</code>
134
     *                            useAlwaysAvailable defaults to true to avoid exceptions on missing translations.
135
     * @param bool $filterOnUserPermissions if true (default) only the objects which is the user allowed to read are returned.
136
     *
137
     * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult
138
     */
139
    public function findContentInfo(Query $query, array $languageFilter = array(), $filterOnUserPermissions = true)
140
    {
141
        $result = $this->internalFindContentInfo($query, $languageFilter, $filterOnUserPermissions);
142
        foreach ($result->searchHits as $hit) {
143
            $hit->valueObject = $this->domainMapper->buildContentInfoDomainObject(
144
                $hit->valueObject
145
            );
146
        }
147
148
        return $result;
149
    }
150
151
    /**
152
     * Finds SPI content info objects for the given query.
153
     *
154
     * Internal for use by {@link findContent} and {@link findContentInfo}.
155
     *
156
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if query is not valid
157
     *
158
     * @param \eZ\Publish\API\Repository\Values\Content\Query $query
159
     * @param array $languageFilter - a map of filters for the returned fields.
160
     *        Currently supports: <code>array("languages" => array(<language1>,..), "useAlwaysAvailable" => bool)</code>
161
     *                            useAlwaysAvailable defaults to true to avoid exceptions on missing translations.
162
     * @param bool $filterOnUserPermissions if true only the objects which is the user allowed to read are returned.
163
     *
164
     * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult With "raw" SPI contentInfo objects in result
165
     */
166
    protected function internalFindContentInfo(Query $query, array $languageFilter = array(), $filterOnUserPermissions = true)
167
    {
168
        if (!is_int($query->offset)) {
169
            throw new InvalidArgumentType(
170
                '$query->offset',
171
                'integer',
172
                $query->offset
173
            );
174
        }
175
176
        if (!is_int($query->limit)) {
177
            throw new InvalidArgumentType(
178
                '$query->limit',
179
                'integer',
180
                $query->limit
181
            );
182
        }
183
184
        $query = clone $query;
185
        $query->filter = $query->filter ?: new Criterion\MatchAll();
186
187
        $this->validateContentCriteria(array($query->query), '$query');
188
        $this->validateContentCriteria(array($query->filter), '$query');
189
        $this->validateContentSortClauses($query);
190
191 View Code Duplication
        if ($filterOnUserPermissions && !$this->permissionsCriterionHandler->addPermissionsCriterion($query->filter)) {
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...
192
            return new SearchResult(array('time' => 0, 'totalCount' => 0));
193
        }
194
195
        return $this->searchHandler->findContent($query, $languageFilter);
196
    }
197
198
    /**
199
     * Checks that $criteria does not contain Location criterions.
200
     *
201
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
202
     *
203
     * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion[] $criteria
204
     * @param string $argumentName
205
     */
206
    protected function validateContentCriteria(array $criteria, $argumentName)
207
    {
208
        foreach ($criteria as $criterion) {
209
            if ($criterion instanceof LocationCriterion) {
210
                throw new InvalidArgumentException(
211
                    $argumentName,
212
                    'Location criterions cannot be used in Content search'
213
                );
214
            }
215
            if ($criterion instanceof LogicalOperator) {
216
                $this->validateContentCriteria($criterion->criteria, $argumentName);
217
            }
218
        }
219
    }
220
221
    /**
222
     * Checks that $query does not contain Location sort clauses.
223
     *
224
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
225
     *
226
     * @param \eZ\Publish\API\Repository\Values\Content\Query $query
227
     */
228
    protected function validateContentSortClauses(Query $query)
229
    {
230
        foreach ($query->sortClauses as $sortClause) {
231
            if ($sortClause instanceof LocationSortClause) {
232
                throw new InvalidArgumentException('$query', 'Location sort clauses cannot be used in Content search');
233
            }
234
        }
235
    }
236
237
    /**
238
     * Performs a query for a single content object.
239
     *
240
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the object was not found by the query or due to permissions
241
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if criterion is not valid
242
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if there is more than one result matching the criterions
243
     *
244
     * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $filter
245
     * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on.
246
     *        Currently supports: <code>array("languages" => array(<language1>,..), "useAlwaysAvailable" => bool)</code>
247
     *                            useAlwaysAvailable defaults to true to avoid exceptions on missing translations.
248
     * @param bool $filterOnUserPermissions if true only the objects which is the user allowed to read are returned.
249
     *
250
     * @return \eZ\Publish\API\Repository\Values\Content\Content
251
     */
252
    public function findSingle(Criterion $filter, array $languageFilter = array(), $filterOnUserPermissions = true)
253
    {
254
        $this->validateContentCriteria(array($filter), '$filter');
255
256
        if ($filterOnUserPermissions && !$this->permissionsCriterionHandler->addPermissionsCriterion($filter)) {
257
            throw new NotFoundException('Content', '*');
258
        }
259
260
        $contentInfo = $this->searchHandler->findSingle($filter, $languageFilter);
261
262
        return $this->repository->getContentService()->internalLoadContent(
0 ignored issues
show
Bug introduced by
The method internalLoadContent() does not exist on eZ\Publish\API\Repository\ContentService. Did you maybe mean loadContent()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
263
            $contentInfo->id,
264
            (!empty($languageFilter['languages']) ? $languageFilter['languages'] : null),
265
            null,
266
            false,
267
            (isset($languageFilter['useAlwaysAvailable']) ? $languageFilter['useAlwaysAvailable'] : true)
268
        );
269
    }
270
271
    /**
272
     * Suggests a list of values for the given prefix.
273
     *
274
     * @param string $prefix
275
     * @param string[] $fieldPaths
276
     * @param int $limit
277
     * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $filter
278
     */
279
    public function suggest($prefix, $fieldPaths = array(), $limit = 10, Criterion $filter = null)
280
    {
281
    }
282
283
    /**
284
     * Finds Locations for the given query.
285
     *
286
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if query is not valid
287
     *
288
     * @param \eZ\Publish\API\Repository\Values\Content\LocationQuery $query
289
     * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on.
290
     *        Currently supports: <code>array("languages" => array(<language1>,..), "useAlwaysAvailable" => bool)</code>
291
     *                            useAlwaysAvailable defaults to true to avoid exceptions on missing translations
292
     * @param bool $filterOnUserPermissions if true only the objects which is the user allowed to read are returned.
293
     *
294
     * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult
295
     */
296
    public function findLocations(LocationQuery $query, array $languageFilter = array(), $filterOnUserPermissions = true)
297
    {
298
        if (!is_int($query->offset)) {
299
            throw new InvalidArgumentType(
300
                '$query->offset',
301
                'integer',
302
                $query->offset
303
            );
304
        }
305
306
        if (!is_int($query->limit)) {
307
            throw new InvalidArgumentType(
308
                '$query->limit',
309
                'integer',
310
                $query->limit
311
            );
312
        }
313
314
        $query = clone $query;
315
        $query->filter = $query->filter ?: new Criterion\MatchAll();
316
317 View Code Duplication
        if ($filterOnUserPermissions && !$this->permissionsCriterionHandler->addPermissionsCriterion($query->filter)) {
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...
318
            return new SearchResult(array('time' => 0, 'totalCount' => 0));
319
        }
320
321
        $result = $this->searchHandler->findLocations($query, $languageFilter);
322
323
        foreach ($result->searchHits as $hit) {
324
            $hit->valueObject = $this->domainMapper->buildLocationDomainObject(
325
                $hit->valueObject
326
            );
327
        }
328
329
        return $result;
330
    }
331
332
    /**
333
     * Get SortClause objects built from $location's sort options.
334
     *
335
     * @param \eZ\Publish\API\Repository\Values\Content\Location $location
336
     *
337
     * @return \eZ\Publish\API\Repository\Values\Content\Query\SortClause[]
338
     */
339
    public function getSortClauseFromLocation(Location $location)
340
    {
341
        return $this->sortClauseMapper->getSortClauseFromLocation($location);
342
    }
343
}
344