Completed
Push — master ( 2e4b99...3b687b )
by
unknown
24s queued 10s
created

RecordQueryBuilder::setFields()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 6
cts 6
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
namespace Alchemy\Phraseanet\Query;
4
5
use Alchemy\Phraseanet\Predicate\PredicateBuilder;
6
use PhraseanetSDK\Entity\DataboxCollection;
7
8
class RecordQueryBuilder
9
{
10
11
    const RECORD_TYPE_AUDIO = 'audio';
12
13
    const RECORD_TYPE_VIDEO = 'video';
14
15
    const RECORD_TYPE_IMAGE = 'image';
16
17
    const RECORD_TYPE_DOCUMENT = 'document';
18
19
    const RECORD_TYPE_FLASH = 'flash';
20
21
    const SEARCH_RECORDS = 0;
22
23
    const SEARCH_STORIES = 1;
24
    
25
    const SEARCH_STORIES_LIGHT = 2;
26
27
    const SORT_RELEVANCE = 'relevance';
28
29
    const SORT_CREATED_ON = 'created_on';
30
31
    const SORT_RANDOM = 'random';
32
33
    /**
34
     * @var string
35
     */
36
    private $query = '';
37
38
    /**
39
     * @var PredicateBuilder
40
     */
41
    private $conditionBuilder;
42
43
    /**
44
     * @var int[] An array of collection ID's to search
45
     */
46
    private $collections = array();
47
48
    /**
49
     * @var int Offset of the first record to return
50
     */
51
    private $offsetStart = 0;
52
53
    /**
54
     * @var int Number of records to return
55
     */
56
    private $recordsPerPage = 10;
57
58
    /**
59
     * @var string|null One of the RECORD_TYPE_* constant values to restrict the search to a specific document type
60
     */
61
    private $recordType = null;
62
63
    /**
64
     * @var int One of the SEARCH_TYPE_* constant values to select the type of record to fetch
65
     */
66
    private $searchType = self::SEARCH_RECORDS;
67
68
    /**
69
     * @var string|null Name of a date field to use as a date criterion
70
     */
71
    private $dateCriterionField = null;
72
73
    /**
74
     * @var \DateTimeInterface|null
75
     */
76
    private $dateCriterionMin = null;
77
78
    /**
79
     * @var \DateTimeInterface|null
80
     */
81
    private $dateCriterionMax = null;
82
83
    /**
84
     * @var int[] An array of statuses to restrict the search to documents matching the given statuses
85
     */
86
    private $statuses = array();
87
88
    /**
89
     * @var string[] An array of fields names to return in the search results
90
     */
91
    private $fields = array();
92
93
    /**
94
     * @var bool Whether to sort in descending order
95
     */
96
    private $sortDescending = false;
97
98
    /**
99
     * @var string Type of sort to use
100
     */
101
    private $sortType = null;
102
103
	/**
104
	 * @var bool Type of truncature
105
	 */
106
    private $truncature = true;
107
108
    /**
109
     * Sets the record search query string. Format follows the same specification as the Phraseanet search
110 1
     * engine.
111
     *
112 1
     * @param $query
113
     * @return $this
114 1
     */
115
    public function setQuery($query)
116
    {
117
        $this->query = $query;
118
119
        return $this;
120
    }
121
122
    /**
123
     * @return PredicateBuilder
124
     */
125
    public function getConditionBuilder()
126
    {
127
        if ($this->conditionBuilder === null) {
128
            $this->conditionBuilder = new PredicateBuilder();
129
        }
130
131
        return $this->conditionBuilder;
132
    }
133
134
    /**
135 5
     * Add a collection to the search criteria.
136
     *
137 5
     * @param DataboxCollection|int $collection A collection or collection ID.
138 1
     * @return $this
139
     */
140
    public function addCollection($collection)
141 5
    {
142
        if ($collection instanceof DataboxCollection) {
143 5
            $collection = $collection->getBaseId();
144
        }
145
146
        $this->collections[] = $collection;
147
148
        return $this;
149
    }
150
151
    /**
152 3
     * Adds a list of collections to the search criteria.
153
     *
154 3
     * @param array $collections An array of DataboxCollection instances or collection ID's.
155 3
     * @return $this
156
     */
157
    public function addCollections(array $collections)
158 3
    {
159
        foreach ($collections as $collection) {
160
            $this->addCollection($collection);
161
        }
162
163
        return $this;
164
    }
165
166
    /**
167
     * Sets the list of collections in which to search for records.
168
     *
169 1
     * @param array $collections An array of DataboxCollection instances or collection ID's. An empty array will clear
170
     * the collection restriction.
171 1
     *
172
     * @return $this
173 1
     */
174
    public function setCollections(array $collections)
175 1
    {
176
        $this->collections = array();
177
178
        $this->addCollections($collections);
179
180
        return $this;
181
    }
182
183 1
    /**
184
     * Intersects the current list of collections with the given collections.
185 1
     *
186 1
     * @param array $collections
187
     */
188
    public function intersectCollections(array $collections)
189
    {
190
        $this->collections = array_values(array_intersect($this->collections, $collections));
191
    }
192
193
    /**
194
     * Sets the offset of the first record to return.
195 1
     *
196
     * @param int $offset Index of the first record to return. Defaults to 0 if an invalid value is provided.
197 1
     *
198
     * @return $this
199 1
     */
200
    public function setOffset($offset)
201
    {
202
        $this->offsetStart = max(0, intval($offset));
203
204
        return $this;
205
    }
206
207
    /**
208
     * @param int $pageIndex The 0-based page index
209
     */
210
    public function setPage($pageIndex)
211
    {
212
        $this->setOffset($pageIndex * $this->recordsPerPage);
213
    }
214
215
    /**
216
     * Sets the sort type for the query
217
     *
218 3
     * @param string $sort One of the SORT_* constant values.
219
     * @param bool $descending True to sort in descending order, false otherwise.
220 3
     * @return $this
221 3
     * @throws \InvalidArgumentException when the sort type is not of the SORT_* constant values..
222
     */
223 3
    public function sortBy($sort, $descending = false)
224
    {
225
        $this->sortType = (string) $sort;
226
        $this->sortDescending = (bool) $descending;
227
228
        return $this;
229
    }
230
231
    /**
232
     * Sets the maximum number of records to return.
233 1
     *
234
     * @param int $limit Maximum number of records to return. Defaults to 1 if an invalid value is provided.
235 1
     *
236
     * @return $this
237 1
     */
238
    public function setLimit($limit)
239
    {
240
        $this->recordsPerPage = max(1, intval($limit));
241
242
        return $this;
243
    }
244
245
    /**
246
     * Filters the media type of records to return.
247 6
     *
248
     * @param string $recordType One of the QueryBuilder::RECORD_TYPE_* constant values.
249
     * @return $this
250 6
     * @throws \InvalidArgumentException when the record media type is not valid.
251 6
     */
252 6
    public function setRecordType($recordType)
253 6
    {
254 6
        $allowedTypes = array(
255
            self::RECORD_TYPE_AUDIO,
256
            self::RECORD_TYPE_DOCUMENT,
257 6
            self::RECORD_TYPE_FLASH,
258 1
            self::RECORD_TYPE_IMAGE,
259 1
            self::RECORD_TYPE_VIDEO
260
        );
261
262
        if (! in_array($recordType, $allowedTypes, true)) {
263 5
            throw new \InvalidArgumentException(
264
                sprintf('Record type must be one of the RECORD_TYPE_* values, %s given.', $recordType)
265 5
            );
266
        }
267
268
        $this->recordType = $recordType;
269
270
        return $this;
271
    }
272
273
    /**
274
     * Sets the type of record to search for.
275 2
     *
276
     * @param int $searchType One of the QueryBuilder::SEARCH_* constant values.
277
     * @return $this
278 2
     * @throws \InvalidArgumentException when the the search type if not valid.
279 2
     */
280 2
    public function setSearchType($searchType)
281
    {
282
        $allowedTypes = array(
283 2
            self::SEARCH_RECORDS,
284 1
            self::SEARCH_STORIES,
285
            self::SEARCH_STORIES_LIGHT
286
        );
287 1
288
        if (! in_array($searchType, $allowedTypes, true)) {
289 1
            throw new \InvalidArgumentException('Search type must be one of the SEARCH_* values');
290
        }
291
292
        $this->searchType = $searchType;
293
294
        return $this;
295
    }
296
297
    /**
298
     * Sets a date filter on the records to return. At least one of $minDate or $maxDate arguments
299
     * must be specified.
300
     *
301
     * @param string $fieldName The name of the field on which to filter by date.
302
     * @param \DateTimeInterface $minDate The lower date boundary.
303 10
     * @param \DateTimeInterface $maxDate The upper date boundary.
304
     * @return $this
305 10
     * @throws \InvalidArgumentException when the field name is an invalid or empty string
306
     * @throws \InvalidArgumentException when both min and max date values are null.
307 4
     */
308 1
    public function setDateCriterion($fieldName, \DateTimeInterface $minDate = null, \DateTimeInterface $maxDate = null)
309
    {
310
        $this->validateFieldName($fieldName, 'Field name is required and must be a non-empty string.');
311 3
312 3
        if ($minDate == null && $maxDate == null) {
313 3
            throw new \InvalidArgumentException('At least one of min or max date must be provided');
314
        }
315 3
316
        $this->dateCriterionField = $fieldName;
317
        $this->dateCriterionMin = $minDate;
318
        $this->dateCriterionMax = $maxDate;
319
320
        return $this;
321
    }
322
323
    /**
324
     * Adds a status filter to the search
325 3
     *
326
     * @param mixed $status
327 3
     * @param $value
328
     * @return $this
329 3
     */
330
    public function addStatus($status, $value)
331
    {
332
        $this->statuses[$status] = $value;
333
334
        return $this;
335
    }
336
337
    /**
338 2
     * Adds a list of status filters to the search.
339
     *
340 2
     * @param array $statuses
341 2
     * @return $this
342
     */
343
    public function addStatuses(array $statuses)
344 2
    {
345
        foreach ($statuses as $status => $value) {
346
            $this->addStatus($status, $value);
347
        }
348
349
        return $this;
350
    }
351
352
    /**
353 1
     * Sets the status filter to the given list. An empty list clears the filter.
354
     *
355 1
     * @param array $statuses
356
     * @return $this
357 1
     */
358
    public function setStatuses(array $statuses)
359 1
    {
360
        $this->statuses = array();
361
362
        $this->addStatuses($statuses);
363
364
        return $this;
365
    }
366
367
	/**
368
	 * Set truncature search type
369 21
	 *
370
	 * @param bool $truncature
371 21
	 * @return $this
372 15
	 */
373
	public function setTruncature(bool $truncature)
374 15
	{
375
		$this->truncature = $truncature;
376
		return $this;
377 31
	}
378
379 31
    /**
380 24
     * Adds a field to the list of requested fields.
381
     *
382 19
     * @param string $fieldName
383
     * @return $this
384
     * @throws \InvalidArgumentException when the field name is an invalid or empty string
385
     */
386
    public function addField($fieldName)
387
    {
388
        $this->validateFieldName($fieldName, 'Field name is required and must be a non-empty string.');
389
        $this->fields[] = $fieldName;
390
391 14
        return $this;
392
    }
393 14
394 14
    private function validateFieldName($fieldName, $errorMessage)
395
    {
396
        if (! is_string($fieldName) || trim($fieldName) === '') {
397 2
            throw new \InvalidArgumentException($errorMessage);
398
        }
399
    }
400
401
    /**
402
     * Adds a list of fields to the list of requested fields.
403
     *
404
     * @param array $fields
405
     * @return $this
406
     * @throws \InvalidArgumentException when one of the field names is an invalid or empty string
407 7
     */
408
    public function addFields(array $fields)
409 7
    {
410
        foreach ($fields as $field) {
411 7
            $this->addField($field);
412
        }
413 1
414
        return $this;
415
    }
416
417
    /**
418
     * Sets the list of requested fields. An empty clears the filter and all fields will be returned.
419
     *
420
     * @param array $fields
421 27
     * @return $this
422
     * @throws \InvalidArgumentException when one of the field names is an invalid or empty string
423
     */
424 27
    public function setFields(array $fields)
425 27
    {
426 27
        $this->fields = array();
427 27
428 27
        $this->addFields($fields);
429
430
        return $this;
431 27
    }
432 27
433 27
    /**
434 27
     * Returns the built query.
435 27
     *
436
     * @return RecordQuery
437 27
     */
438
    public function getQuery()
439
    {
440 27
        $query = array(
441
            'query' => $this->buildQueryTerm(),
442 27
            'bases' => array_unique($this->collections),
443 27
            'offset_start' => $this->offsetStart,
444
            'per_page' => $this->recordsPerPage,
445
            'search_type' => $this->searchType
446
        );
447
448
        $query = $this->appendRecordType($query);
449
        $query = $this->appendDates($query);
450
        $query = $this->appendStatuses($query);
451
        $query = $this->appendFields($query);
452
        $query = $this->appendSort($query);
453
        $query = $this->appendTruncature($query);
454
455
        return new RecordQuery($query, $this->searchType);
456
    }
457
458 27
    private function buildQueryTerm()
459
    {
460 27
        if (! $this->conditionBuilder) {
461 5
            return $this->query;
462
        }
463 5
464
        $compiler = new QueryPredicateVisitor();
465
466 22
        $this->conditionBuilder->endAllGroups();
467
        $this->conditionBuilder->andWhere($this->query);
468
469
        return $compiler->compile($this->conditionBuilder->getPredicate());
470
    }
471
472
    /**
473 27
     * @param $query
474
     * @return mixed
475 27
     */
476 3
    private function appendRecordType($query)
477
    {
478 3
        if ($this->recordType !== null) {
479 2
            $query['record_type'] = $this->recordType;
480
481
            return $query;
482 3
        }
483 2
484
        return $query;
485 2
    }
486
487
    /**
488 1
     * @param $query
489
     * @return mixed
490
     */
491 24
    private function appendDates($query)
492
    {
493
        if ($this->dateCriterionField !== null) {
494
            $query['date_field'] = $this->dateCriterionField;
495
496
            if ($this->dateCriterionMin) {
497
                $query['date_min'] = $this->dateCriterionMin->format('Y/m/d');
498 27
            }
499
500 27
            if ($this->dateCriterionMax) {
501 3
                $query['date_max'] = $this->dateCriterionMax->format('Y/m/d');
502
503 3
                return $query;
504
            }
505
506 24
            return $query;
507
        }
508
509
        return $query;
510
    }
511
512
    /**
513 27
     * @param $query
514
     * @return mixed
515 27
     */
516 3
    private function appendStatuses($query)
517
    {
518 3
        if (!empty($this->statuses)) {
519
            $query['status'] = $this->statuses;
520
521 24
            return $query;
522
        }
523
524
        return $query;
525
    }
526
527
528 27
	/**
529
	 * @param $query
530 27
	 * @return mixed
531 3
	 */
532 3
    private function appendTruncature($query)
533
	{
534 3
		$query['truncation'] = $this->truncature;
535
536
		return $query;
537 24
	}
538
539
    /**
540
     * @param $query
541
     * @return mixed
542
     */
543
    private function appendFields($query)
544
    {
545
        if (!empty($this->fields)) {
546
            $query['fields'] = $this->fields;
547
548
            return $query;
549
        }
550
551
        return $query;
552
    }
553
554
    /**
555
     * @param $query
556
     * @return mixed
557
     */
558
    private function appendSort($query)
559
    {
560
        if ($this->sortType !== null) {
561
            $query['sort'] = $this->sortType;
562
            $query['ord'] = $this->sortDescending ? 'desc' : 'asc';
563
564
            return $query;
565
        }
566
567
        return $query;
568
    }
569
}
570