Completed
Push — signal_search_issues ( 5556b2...f328ba )
by André
63:06 queued 07:22
created

Handler::findContent()   B

Complexity

Conditions 7
Paths 48

Size

Total Lines 54
Code Lines 34

Duplication

Lines 54
Ratio 100 %

Importance

Changes 0
Metric Value
cc 7
eloc 34
nc 48
nop 2
dl 54
loc 54
rs 7.8331
c 0
b 0
f 0

How to fix   Long Method   

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
/**
4
 * File containing the Content Search handler 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
 * @version //autogentag//
10
 */
11
namespace eZ\Publish\Core\Search\Legacy\Content;
12
13
use eZ\Publish\SPI\Persistence\Content;
14
use eZ\Publish\SPI\Persistence\Content\Location;
15
use eZ\Publish\SPI\Search\Handler as SearchHandlerInterface;
16
use eZ\Publish\Core\Persistence\Legacy\Content\Mapper as ContentMapper;
17
use eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper as LocationMapper;
18
use eZ\Publish\Core\Search\Legacy\Content\Location\Gateway as LocationGateway;
19
use eZ\Publish\API\Repository\Exceptions\NotImplementedException;
20
use eZ\Publish\API\Repository\Values\Content\Search\SearchResult;
21
use eZ\Publish\API\Repository\Values\Content\Search\SearchHit;
22
use eZ\Publish\API\Repository\Values\Content\Query\Criterion;
23
use eZ\Publish\API\Repository\Values\Content\Query;
24
use eZ\Publish\API\Repository\Values\Content\LocationQuery;
25
use eZ\Publish\Core\Base\Exceptions\NotFoundException;
26
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException;
27
use eZ\Publish\SPI\Persistence\Content\Language\Handler as LanguageHandler;
28
29
/**
30
 * The Content Search handler retrieves sets of of Content objects, based on a
31
 * set of criteria.
32
 *
33
 * The basic idea of this class is to do the following:
34
 *
35
 * 1) The find methods retrieve a recursive set of filters, which define which
36
 * content objects to retrieve from the database. Those may be combined using
37
 * boolean operators.
38
 *
39
 * 2) This recursive criterion definition is visited into a query, which limits
40
 * the content retrieved from the database. We might not be able to create
41
 * sensible queries from all criterion definitions.
42
 *
43
 * 3) The query might be possible to optimize (remove empty statements),
44
 * reduce singular and and or constructs…
45
 *
46
 * 4) Additionally we might need a post-query filtering step, which filters
47
 * content objects based on criteria, which could not be converted in to
48
 * database statements.
49
 */
50
class Handler implements SearchHandlerInterface
51
{
52
    /**
53
     * Content locator gateway.
54
     *
55
     * @var \eZ\Publish\Core\Search\Legacy\Content\Gateway
56
     */
57
    protected $gateway;
58
59
    /**
60
     * Location locator gateway.
61
     *
62
     * @var \eZ\Publish\Core\Search\Legacy\Content\Location\Gateway
63
     */
64
    protected $locationGateway;
65
66
    /**
67
     * Content mapper.
68
     *
69
     * @var \eZ\Publish\Core\Persistence\Legacy\Content\Mapper
70
     */
71
    protected $contentMapper;
72
73
    /**
74
     * Location locationMapper.
75
     *
76
     * @var \eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper
77
     */
78
    protected $locationMapper;
79
80
    /**
81
     * Language handler.
82
     *
83
     * @var \eZ\Publish\SPI\Persistence\Content\Language\Handler
84
     */
85
    protected $languageHandler;
86
87
    /**
88
     * Creates a new content handler.
89
     *
90
     * @param \eZ\Publish\Core\Search\Legacy\Content\Gateway $gateway
91
     * @param \eZ\Publish\Core\Search\Legacy\Content\Location\Gateway $locationGateway
92
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\Mapper $contentMapper
93
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper $locationMapper
94
     * @param \eZ\Publish\SPI\Persistence\Content\Language\Handler $languageHandler
95
     */
96
    public function __construct(
97
        Gateway $gateway,
98
        LocationGateway $locationGateway,
99
        ContentMapper $contentMapper,
100
        LocationMapper $locationMapper,
101
        LanguageHandler $languageHandler
102
    ) {
103
        $this->gateway = $gateway;
104
        $this->locationGateway = $locationGateway;
105
        $this->contentMapper = $contentMapper;
106
        $this->locationMapper = $locationMapper;
107
        $this->languageHandler = $languageHandler;
108
    }
109
110
    /**
111
     * Finds content objects for the given query.
112
     *
113
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if Query criterion is not applicable to its target
114
     *
115
     * @param \eZ\Publish\API\Repository\Values\Content\Query $query
116
     * @param array $languageFilter - a map of language related filters specifying languages query will be performed on.
117
     *        Also used to define which field languages are loaded for the returned content.
118
     *        Currently supports: <code>array("languages" => array(<language1>,..), "useAlwaysAvailable" => bool)</code>
119
     *                            useAlwaysAvailable defaults to true to avoid exceptions on missing translations
120
     *
121
     * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult
122
     */
123 View Code Duplication
    public function findContent(Query $query, array $languageFilter = array())
124
    {
125
        if (!isset($languageFilter['languages'])) {
126
            $languageFilter['languages'] = array();
127
        }
128
129
        if (!isset($languageFilter['useAlwaysAvailable'])) {
130
            $languageFilter['useAlwaysAvailable'] = true;
131
        }
132
133
        $start = microtime(true);
134
        $query->filter = $query->filter ?: new Criterion\MatchAll();
135
        $query->query = $query->query ?: new Criterion\MatchAll();
136
137
        if (count($query->facetBuilders)) {
138
            throw new NotImplementedException('Facets are not supported by the legacy search engine.');
139
        }
140
141
        // The legacy search does not know about scores, so that we just
142
        // combine the query with the filter
143
        $filter = new Criterion\LogicalAnd(array($query->query, $query->filter));
144
145
        $data = $this->gateway->find(
146
            $filter,
147
            $query->offset,
148
            $query->limit,
149
            $query->sortClauses,
150
            $languageFilter,
151
            $query->performCount
152
        );
153
154
        $result = new SearchResult();
155
        $result->time = microtime(true) - $start;
0 ignored issues
show
Documentation Bug introduced by
The property $time was declared of type integer, but microtime(true) - $start is of type double. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
156
        $result->totalCount = $data['count'];
0 ignored issues
show
Documentation Bug introduced by
It seems like $data['count'] of type array<integer,*> is incompatible with the declared type integer|null of property $totalCount.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
157
        $contentInfoList = $this->contentMapper->extractContentInfoFromRows(
158
            $data['rows'],
159
            '',
160
            'main_tree_'
161
        );
162
163
        foreach ($contentInfoList as $index => $contentInfo) {
164
            $searchHit = new SearchHit();
165
            $searchHit->valueObject = $contentInfo;
166
            $searchHit->matchedTranslation = $this->extractMatchedLanguage(
167
                $data['rows'][$index]['language_mask'],
168
                $data['rows'][$index]['initial_language_id'],
169
                $languageFilter
170
            );
171
172
            $result->searchHits[] = $searchHit;
173
        }
174
175
        return $result;
176
    }
177
178
    protected function extractMatchedLanguage($languageMask, $mainLanguageId, $languageSettings)
179
    {
180
        foreach ($languageSettings['languages'] as $languageCode) {
181
            if ($languageMask & $this->languageHandler->loadByLanguageCode($languageCode)->id) {
182
                return $languageCode;
183
            }
184
        }
185
186
        if ($languageMask & 1 || empty($languageSettings['languages'])) {
187
            return $this->languageHandler->load($mainLanguageId)->languageCode;
188
        }
189
190
        return null;
191
    }
192
193
    /**
194
     * Performs a query for a single content object.
195
     *
196
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the object was not found by the query or due to permissions
197
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if Criterion is not applicable to its target
198
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if there is more than than one result matching the criterions
199
     *
200
     * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $filter
201
     * @param array $languageFilter - a map of language related filters specifying languages query will be performed on.
202
     *        Also used to define which field languages are loaded for the returned content.
203
     *        Currently supports: <code>array("languages" => array(<language1>,..), "useAlwaysAvailable" => bool)</code>
204
     *                            useAlwaysAvailable defaults to true to avoid exceptions on missing translations
205
     *
206
     * @return \eZ\Publish\SPI\Persistence\Content\ContentInfo
207
     */
208
    public function findSingle(Criterion $filter, array $languageFilter = array())
209
    {
210
        if (!isset($languageFilter['languages'])) {
211
            $languageFilter['languages'] = array();
212
        }
213
214
        if (!isset($languageFilter['useAlwaysAvailable'])) {
215
            $languageFilter['useAlwaysAvailable'] = true;
216
        }
217
218
        $searchQuery = new Query();
219
        $searchQuery->filter = $filter;
220
        $searchQuery->query = new Criterion\MatchAll();
221
        $searchQuery->offset = 0;
222
        $searchQuery->limit = 2;// Because we optimize away the count query below
223
        $searchQuery->performCount = true;
224
        $searchQuery->sortClauses = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array<integer,object<eZ\...tent\Query\SortClause>> of property $sortClauses.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
225
        $result = $this->findContent($searchQuery, $languageFilter);
226
227
        if (empty($result->searchHits)) {
228
            throw new NotFoundException('Content', 'findSingle() found no content for given $criterion');
229
        } elseif (isset($result->searchHits[1])) {
230
            throw new InvalidArgumentException('totalCount', 'findSingle() found more then one item for given $criterion');
231
        }
232
233
        $first = reset($result->searchHits);
234
235
        return $first->valueObject;
236
    }
237
238
    /**
239
     * @see \eZ\Publish\SPI\Search\Handler::findLocations
240
     */
241 View Code Duplication
    public function findLocations(LocationQuery $query, array $languageFilter = array())
242
    {
243
        if (!isset($languageFilter['languages'])) {
244
            $languageFilter['languages'] = array();
245
        }
246
247
        if (!isset($languageFilter['useAlwaysAvailable'])) {
248
            $languageFilter['useAlwaysAvailable'] = true;
249
        }
250
251
        $start = microtime(true);
252
        $query->filter = $query->filter ?: new Criterion\MatchAll();
253
        $query->query = $query->query ?: new Criterion\MatchAll();
254
255
        if (count($query->facetBuilders)) {
256
            throw new NotImplementedException('Facets are not supported by the legacy search engine.');
257
        }
258
259
        // The legacy search does not know about scores, so we just
260
        // combine the query with the filter
261
        $data = $this->locationGateway->find(
262
            new Criterion\LogicalAnd(array($query->query, $query->filter)),
263
            $query->offset,
264
            $query->limit,
265
            $query->sortClauses,
266
            $languageFilter,
267
            $query->performCount
268
        );
269
270
        $result = new SearchResult();
271
        $result->time = microtime(true) - $start;
0 ignored issues
show
Documentation Bug introduced by
The property $time was declared of type integer, but microtime(true) - $start is of type double. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
272
        $result->totalCount = $data['count'];
0 ignored issues
show
Documentation Bug introduced by
It seems like $data['count'] of type array<integer,*> is incompatible with the declared type integer|null of property $totalCount.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
273
        $locationList = $this->locationMapper->createLocationsFromRows($data['rows']);
274
275
        foreach ($locationList as $index => $location) {
276
            $searchHit = new SearchHit();
277
            $searchHit->valueObject = $location;
278
            $searchHit->matchedTranslation = $this->extractMatchedLanguage(
279
                $data['rows'][$index]['language_mask'],
280
                $data['rows'][$index]['initial_language_id'],
281
                $languageFilter
282
            );
283
284
            $result->searchHits[] = $searchHit;
285
        }
286
287
        return $result;
288
    }
289
290
    /**
291
     * Suggests a list of values for the given prefix.
292
     *
293
     * @param string $prefix
294
     * @param string[] $fieldPaths
295
     * @param int $limit
296
     * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $filter
297
     */
298
    public function suggest($prefix, $fieldPaths = array(), $limit = 10, Criterion $filter = null)
299
    {
300
        throw new NotImplementedException('Suggestions are not supported by legacy search engine.');
301
    }
302
303
    /**
304
     * Indexes a content object.
305
     *
306
     * @param \eZ\Publish\SPI\Persistence\Content $content
307
     */
308
    public function indexContent(Content $content)
309
    {
310
        // Not implemented in Legacy Storage Engine
311
    }
312
313
    /**
314
     * @param \eZ\Publish\SPI\Persistence\Content\Location $location
315
     */
316
    public function indexLocation(Location $location)
317
    {
318
        // Not implemented in Legacy Storage Engine
319
    }
320
321
    /**
322
     * Deletes a content object from the index.
323
     *
324
     * @param int $contentId
325
     * @param int|null $versionId
326
     */
327
    public function deleteContent($contentId, $versionId = null)
328
    {
329
        // Not implemented in Legacy Storage Engine
330
    }
331
332
    /**
333
     * Deletes a location from the index.
334
     *
335
     * @param mixed $locationId
336
     * @param mixed $contentId
337
     */
338
    public function deleteLocation($locationId, $contentId)
339
    {
340
        // Not implemented in Legacy Storage Engine
341
    }
342
}
343