Completed
Push — master ( a682b4...c5625a )
by André
14:36
created

SearchService::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 16
Code Lines 11

Duplication

Lines 16
Ratio 100 %

Importance

Changes 0
Metric Value
cc 1
eloc 11
nc 1
nop 5
dl 16
loc 16
rs 9.4285
c 0
b 0
f 0
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\Values\Content\Query\Criterion;
13
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalOperator;
14
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Location as LocationCriterion;
15
use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location as LocationSortClause;
16
use eZ\Publish\API\Repository\Values\Content\Query;
17
use eZ\Publish\API\Repository\Values\Content\LocationQuery;
18
use eZ\Publish\API\Repository\Repository as RepositoryInterface;
19
use eZ\Publish\API\Repository\Values\Content\Search\SearchResult;
20
use eZ\Publish\Core\Base\Exceptions\NotFoundException;
21
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException;
22
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType;
23
use eZ\Publish\SPI\Search\Handler;
24
25
/**
26
 * Search service.
27
 */
28
class SearchService implements SearchServiceInterface
29
{
30
    /**
31
     * @var \eZ\Publish\Core\Repository\Repository
32
     */
33
    protected $repository;
34
35
    /**
36
     * @var \eZ\Publish\SPI\Search\Handler
37
     */
38
    protected $searchHandler;
39
40
    /**
41
     * @var array
42
     */
43
    protected $settings;
44
45
    /**
46
     * @var \eZ\Publish\Core\Repository\Helper\DomainMapper
47
     */
48
    protected $domainMapper;
49
50
    /**
51
     * @var \eZ\Publish\Core\Repository\PermissionsCriterionHandler
52
     */
53
    protected $permissionsCriterionHandler;
54
55
    /**
56
     * Setups service with reference to repository object that created it & corresponding handler.
57
     *
58
     * @param \eZ\Publish\API\Repository\Repository $repository
59
     * @param \eZ\Publish\SPI\Search\Handler $searchHandler
60
     * @param \eZ\Publish\Core\Repository\Helper\DomainMapper $domainMapper
61
     * @param \eZ\Publish\Core\Repository\PermissionsCriterionHandler $permissionsCriterionHandler
62
     * @param array $settings
63
     */
64 View Code Duplication
    public function __construct(
65
        RepositoryInterface $repository,
66
        Handler $searchHandler,
67
        Helper\DomainMapper $domainMapper,
68
        PermissionsCriterionHandler $permissionsCriterionHandler,
69
        array $settings = array()
70
    ) {
71
        $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...
72
        $this->searchHandler = $searchHandler;
73
        $this->domainMapper = $domainMapper;
74
        // Union makes sure default settings are ignored if provided in argument
75
        $this->settings = $settings + array(
76
            //'defaultSetting' => array(),
77
        );
78
        $this->permissionsCriterionHandler = $permissionsCriterionHandler;
79
    }
80
81
    /**
82
     * Finds content objects for the given query.
83
     *
84
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if query is not valid
85
     *
86
     * @param \eZ\Publish\API\Repository\Values\Content\Query $query
87
     * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on.
88
     *        Currently supports: <code>array("languages" => array(<language1>,..), "useAlwaysAvailable" => bool)</code>
89
     *                            useAlwaysAvailable defaults to true to avoid exceptions on missing translations.
90
     * @param bool $filterOnUserPermissions if true only the objects which the user is allowed to read are returned.
91
     *
92
     * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult
93
     */
94
    public function findContent(Query $query, array $languageFilter = array(), $filterOnUserPermissions = true)
95
    {
96
        $contentService = $this->repository->getContentService();
97
        $result = $this->internalFindContentInfo($query, $languageFilter, $filterOnUserPermissions);
98
        foreach ($result->searchHits as $hit) {
99
            // As we get ContentInfo from SPI, we need to load full content (avoids getting stale content data)
100
            $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...
101
                $hit->valueObject->id,
102
                (!empty($languageFilter['languages']) ? $languageFilter['languages'] : null),
103
                null,
104
                false,
105
                (isset($languageFilter['useAlwaysAvailable']) ? $languageFilter['useAlwaysAvailable'] : true)
106
            );
107
        }
108
109
        return $result;
110
    }
111
112
    /**
113
     * Finds contentInfo objects for the given query.
114
     *
115
     * @see SearchServiceInterface::findContentInfo()
116
     *
117
     * @since 5.4.5
118
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if query is not valid
119
     *
120
     * @param \eZ\Publish\API\Repository\Values\Content\Query $query
121
     * @param array $languageFilter - a map of filters for the returned fields.
122
     *        Currently supports: <code>array("languages" => array(<language1>,..), "useAlwaysAvailable" => bool)</code>
123
     *                            useAlwaysAvailable defaults to true to avoid exceptions on missing translations.
124
     * @param bool $filterOnUserPermissions if true (default) only the objects which is the user allowed to read are returned.
125
     *
126
     * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult
127
     */
128
    public function findContentInfo(Query $query, array $languageFilter = array(), $filterOnUserPermissions = true)
129
    {
130
        $result = $this->internalFindContentInfo($query, $languageFilter, $filterOnUserPermissions);
131
        foreach ($result->searchHits as $hit) {
132
            $hit->valueObject = $this->domainMapper->buildContentInfoDomainObject(
133
                $hit->valueObject
134
            );
135
        }
136
137
        return $result;
138
    }
139
140
    /**
141
     * Finds SPI content info objects for the given query.
142
     *
143
     * Internal for use by {@link findContent} and {@link findContentInfo}.
144
     *
145
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if query is not valid
146
     *
147
     * @param \eZ\Publish\API\Repository\Values\Content\Query $query
148
     * @param array $languageFilter - a map of filters for the returned fields.
149
     *        Currently supports: <code>array("languages" => array(<language1>,..), "useAlwaysAvailable" => bool)</code>
150
     *                            useAlwaysAvailable defaults to true to avoid exceptions on missing translations.
151
     * @param bool $filterOnUserPermissions if true only the objects which is the user allowed to read are returned.
152
     *
153
     * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult With "raw" SPI contentInfo objects in result
154
     */
155
    protected function internalFindContentInfo(Query $query, array $languageFilter = array(), $filterOnUserPermissions = true)
156
    {
157
        if (!is_int($query->offset)) {
158
            throw new InvalidArgumentType(
159
                '$query->offset',
160
                'integer',
161
                $query->offset
162
            );
163
        }
164
165
        if (!is_int($query->limit)) {
166
            throw new InvalidArgumentType(
167
                '$query->limit',
168
                'integer',
169
                $query->limit
170
            );
171
        }
172
173
        $query = clone $query;
174
        $query->filter = $query->filter ?: new Criterion\MatchAll();
175
176
        $this->validateContentCriteria(array($query->query), '$query');
177
        $this->validateContentCriteria(array($query->filter), '$query');
178
        $this->validateContentSortClauses($query);
179
180 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...
181
            return new SearchResult(array('time' => 0, 'totalCount' => 0));
182
        }
183
184
        return $this->searchHandler->findContent($query, $languageFilter);
185
    }
186
187
    /**
188
     * Checks that $criteria does not contain Location criterions.
189
     *
190
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
191
     *
192
     * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion[] $criteria
193
     * @param string $argumentName
194
     */
195
    protected function validateContentCriteria(array $criteria, $argumentName)
196
    {
197
        foreach ($criteria as $criterion) {
198
            if ($criterion instanceof LocationCriterion) {
199
                throw new InvalidArgumentException(
200
                    $argumentName,
201
                    'Location criterions cannot be used in Content search'
202
                );
203
            }
204
            if ($criterion instanceof LogicalOperator) {
205
                $this->validateContentCriteria($criterion->criteria, $argumentName);
206
            }
207
        }
208
    }
209
210
    /**
211
     * Checks that $query does not contain Location sort clauses.
212
     *
213
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
214
     *
215
     * @param \eZ\Publish\API\Repository\Values\Content\Query $query
216
     */
217
    protected function validateContentSortClauses(Query $query)
218
    {
219
        foreach ($query->sortClauses as $sortClause) {
220
            if ($sortClause instanceof LocationSortClause) {
221
                throw new InvalidArgumentException('$query', 'Location sort clauses cannot be used in Content search');
222
            }
223
        }
224
    }
225
226
    /**
227
     * Performs a query for a single content object.
228
     *
229
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the object was not found by the query or due to permissions
230
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if criterion is not valid
231
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if there is more than one result matching the criterions
232
     *
233
     * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $filter
234
     * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on.
235
     *        Currently supports: <code>array("languages" => array(<language1>,..), "useAlwaysAvailable" => bool)</code>
236
     *                            useAlwaysAvailable defaults to true to avoid exceptions on missing translations.
237
     * @param bool $filterOnUserPermissions if true only the objects which is the user allowed to read are returned.
238
     *
239
     * @return \eZ\Publish\API\Repository\Values\Content\Content
240
     */
241
    public function findSingle(Criterion $filter, array $languageFilter = array(), $filterOnUserPermissions = true)
242
    {
243
        $this->validateContentCriteria(array($filter), '$filter');
244
245
        if ($filterOnUserPermissions && !$this->permissionsCriterionHandler->addPermissionsCriterion($filter)) {
246
            throw new NotFoundException('Content', '*');
247
        }
248
249
        $contentInfo = $this->searchHandler->findSingle($filter, $languageFilter);
250
251
        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...
252
            $contentInfo->id,
253
            (!empty($languageFilter['languages']) ? $languageFilter['languages'] : null),
254
            null,
255
            false,
256
            (isset($languageFilter['useAlwaysAvailable']) ? $languageFilter['useAlwaysAvailable'] : true)
257
        );
258
    }
259
260
    /**
261
     * Suggests a list of values for the given prefix.
262
     *
263
     * @param string $prefix
264
     * @param string[] $fieldPaths
265
     * @param int $limit
266
     * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $filter
267
     */
268
    public function suggest($prefix, $fieldPaths = array(), $limit = 10, Criterion $filter = null)
269
    {
270
    }
271
272
    /**
273
     * Finds Locations for the given query.
274
     *
275
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if query is not valid
276
     *
277
     * @param \eZ\Publish\API\Repository\Values\Content\LocationQuery $query
278
     * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on.
279
     *        Currently supports: <code>array("languages" => array(<language1>,..), "useAlwaysAvailable" => bool)</code>
280
     *                            useAlwaysAvailable defaults to true to avoid exceptions on missing translations
281
     * @param bool $filterOnUserPermissions if true only the objects which is the user allowed to read are returned.
282
     *
283
     * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult
284
     */
285
    public function findLocations(LocationQuery $query, array $languageFilter = array(), $filterOnUserPermissions = true)
286
    {
287
        if (!is_int($query->offset)) {
288
            throw new InvalidArgumentType(
289
                '$query->offset',
290
                'integer',
291
                $query->offset
292
            );
293
        }
294
295
        if (!is_int($query->limit)) {
296
            throw new InvalidArgumentType(
297
                '$query->limit',
298
                'integer',
299
                $query->limit
300
            );
301
        }
302
303
        $query = clone $query;
304
        $query->filter = $query->filter ?: new Criterion\MatchAll();
305
306 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...
307
            return new SearchResult(array('time' => 0, 'totalCount' => 0));
308
        }
309
310
        $result = $this->searchHandler->findLocations($query, $languageFilter);
311
312
        foreach ($result->searchHits as $hit) {
313
            $hit->valueObject = $this->domainMapper->buildLocationDomainObject(
314
                $hit->valueObject
315
            );
316
        }
317
318
        return $result;
319
    }
320
}
321