Completed
Push — master ( d2ae90...960360 )
by Dion
02:48
created

ReadOptions::getQueryParameters()   A

Complexity

Conditions 2
Paths 4

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 9
dl 0
loc 14
rs 9.9666
c 0
b 0
f 0
cc 2
nc 4
nop 0
1
<?php
2
3
/*
4
 * This file is part of the SexyField package.
5
 *
6
 * (c) Dion Snoeijen <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare (strict_types = 1);
13
14
namespace Tardigrades\SectionField\Service;
15
16
use Assert\Assertion;
17
use Assert\AssertionFailedException;
18
use Assert\InvalidArgumentException;
19
use Tardigrades\SectionField\ValueObject\Slug;
20
use Tardigrades\SectionField\ValueObject\After;
21
use Tardigrades\SectionField\ValueObject\Before;
22
use Tardigrades\SectionField\ValueObject\FullyQualifiedClassName;
23
use Tardigrades\SectionField\ValueObject\Handle;
24
use Tardigrades\SectionField\ValueObject\Id;
25
use Tardigrades\SectionField\ValueObject\Limit;
26
use Tardigrades\SectionField\ValueObject\Offset;
27
use Tardigrades\SectionField\ValueObject\OrderBy;
28
use Tardigrades\SectionField\ValueObject\Search;
29
use Tardigrades\SectionField\ValueObject\Sort;
30
31
class ReadOptions implements ReadOptionsInterface
32
{
33
    /** Read options that are relevant for all readers */
34
    const ID = 'id';
35
    const SLUG = 'slug';
36
    const SECTION = 'section';
37
    const SECTION_ID = 'sectionId';
38
    const LIMIT = 'limit';
39
    const RELATE = 'relate';
40
    const OFFSET = 'offset';
41
    const ORDER_BY = 'orderBy';
42
    const SORT = 'sort';
43
    const BEFORE = 'before';
44
    const AFTER = 'after';
45
    const LOCALE_ENABLED = 'localeEnabled';
46
    const LOCALE = 'locale';
47
    const SEARCH = 'search';
48
    const FIELD = 'field';
49
    const JOIN = 'join';
50
    const QUERY = 'query';
51
    const QUERY_PARAMETERS = 'queryParameters';
52
53
    /**
54
     * @var string If you know in advance what fields you are going to need
55
     * use the fetchFields option. That way a custom DQL query will be built
56
     * and greatly improve performance because we are bypassing the default
57
     * LAZY_LOADING setup of doctrine.
58
     *
59
     * A string like that should be comma separated. If you need fields that are
60
     * in a relationship, add the entity property name that would return the relationship.
61
     * title,subTitle,someRelation,someRelationTitle
62
     */
63
    const FETCH_FIELDS = 'fetchFields';
64
65
    /** @var array */
66
    protected $options;
67
68
    private function __construct(
69
        array $options
70
    ) {
71
        $valid = false;
72
73
        if (key_exists(ReadOptions::QUERY, $options)) {
74
            $valid = true;
75
        } else {
76
77
            if (is_array($options[ReadOptions::SECTION])) {
78
                $valid = true;
79
            }
80
81
            if (is_string($options[ReadOptions::SECTION])) {
82
                $valid = true;
83
            }
84
85
            if ($options[ReadOptions::SECTION] instanceof FullyQualifiedClassName) {
86
                $valid = true;
87
            }
88
        }
89
90
        if (!$valid) {
91
            throw new InvalidArgumentException(
92
                'The section is not of a valid type',
93
                400,
94
                null,
95
                $options[ReadOptions::SECTION]
96
            );
97
        }
98
99
        $this->options = $options;
100
    }
101
102
    public function getSection(): array
103
    {
104
        $sectionEntities = [];
105
106
        if (!key_exists(ReadOptions::SECTION, $this->options)) {
107
            return $sectionEntities;
108
        }
109
110
        if ($this->options[ReadOptions::SECTION] instanceof FullyQualifiedClassName) {
111
            $sectionEntities = [$this->options[ReadOptions::SECTION]];
112
        }
113
114
        if (is_string($this->options[ReadOptions::SECTION])) {
115
            $sectionEntities = [FullyQualifiedClassName::fromString($this->options[ReadOptions::SECTION])];
116
        }
117
118
        if (is_array($this->options[ReadOptions::SECTION])) {
119
            foreach ($this->options[ReadOptions::SECTION] as $section) {
120
                $sectionEntities[] = FullyQualifiedClassName::fromString((string) $section);
121
            }
122
        }
123
124
        return $sectionEntities;
125
    }
126
127
    public function getSectionId(): ?Id
128
    {
129
        try {
130
            Assertion::keyIsset(
131
                $this->options,
132
                ReadOptions::SECTION_ID,
133
                'The sectionId is not set'
134
            );
135
            Assertion::integerish(
136
                $this->options[ReadOptions::SECTION_ID],
137
                'The sectionId needs to be an integer'
138
            );
139
        } catch (AssertionFailedException $exception) {
140
            return null;
141
        }
142
143
        return Id::fromInt($this->options[ReadOptions::SECTION_ID]);
144
    }
145
146
    public function getOffset(): ?Offset
147
    {
148
        try {
149
            Assertion::keyIsset(
150
                $this->options,
151
                ReadOptions::OFFSET,
152
                'The offset is not set'
153
            );
154
            Assertion::integerish(
155
                $this->options[ReadOptions::OFFSET],
156
                'The offset needs to be an integer.'
157
            );
158
        } catch (AssertionFailedException $exception) {
159
            return null;
160
        }
161
162
        return Offset::fromInt($this->options[ReadOptions::OFFSET]);
163
    }
164
165
    public function getLimit(): ?Limit
166
    {
167
        try {
168
            Assertion::keyIsset(
169
                $this->options,
170
                ReadOptions::LIMIT,
171
                'The limit is not set'
172
            );
173
            Assertion::integerish(
174
                $this->options[ReadOptions::LIMIT],
175
                'The limit needs to be an integer.'
176
            );
177
        } catch (AssertionFailedException $exception) {
178
            return null;
179
        }
180
181
        return Limit::fromInt($this->options[ReadOptions::LIMIT]);
182
    }
183
184
    public function getOrderBy(): ?OrderBy
185
    {
186
        try {
187
            Assertion::keyIsset(
188
                $this->options,
189
                ReadOptions::ORDER_BY,
190
                'orderBy is not set'
191
            );
192
            Assertion::isArray(
193
                $this->options[ReadOptions::ORDER_BY],
194
                'Order by needs to be an array. Example: (["some" => "ASC"])'
195
            );
196
            $handle = Handle::fromString(key($this->options[ReadOptions::ORDER_BY]));
197
            $sort = Sort::fromString(array_values($this->options[ReadOptions::ORDER_BY])[0]);
198
            $orderBy = OrderBy::fromHandleAndSort($handle, $sort);
199
        } catch (AssertionFailedException $exception) {
200
            return null;
201
        }
202
203
        return $orderBy;
204
    }
205
206
    public function getBefore(): ?Before
207
    {
208
        try {
209
            Assertion::keyIsset($this->options, ReadOptions::BEFORE, 'Before is not defined');
210
            Assertion::string($this->options[ReadOptions::BEFORE]);
211
        } catch (AssertionFailedException $exception) {
212
            return null;
213
        }
214
215
        return Before::fromString($this->options[ReadOptions::BEFORE]);
216
    }
217
218
    public function getAfter(): ?After
219
    {
220
        try {
221
            Assertion::keyIsset($this->options, ReadOptions::AFTER, 'After is not defined');
222
            Assertion::string($this->options[ReadOptions::AFTER]);
223
        } catch (AssertionFailedException $exception) {
224
            return null;
225
        }
226
227
        return After::fromString($this->options[ReadOptions::AFTER]);
228
    }
229
230
    public function getLocaleEnabled(): ?bool
231
    {
232
        try {
233
            Assertion::keyIsset($this->options, ReadOptions::LOCALE_ENABLED, 'localeEnabled is not set');
234
            Assertion::boolean($this->options[ReadOptions::LOCALE_ENABLED]);
235
        } catch (AssertionFailedException $exception) {
236
            return null;
237
        }
238
239
        return (bool) $this->options[ReadOptions::LOCALE_ENABLED];
240
    }
241
242
    public function getLocale(): ?string
243
    {
244
        try {
245
            Assertion::keyIsset($this->options, ReadOptions::LOCALE, 'No locale defined');
246
            Assertion::string($this->options[ReadOptions::LOCALE], 'Locale is supposed to be a string like en_EN');
247
        } catch (AssertionFailedException $exception) {
248
            return null;
249
        }
250
251
        return (string) $this->options[ReadOptions::LOCALE];
252
    }
253
254
    public function getSearch(): ?Search
255
    {
256
        try {
257
            Assertion::keyIsset($this->options, ReadOptions::SEARCH, 'No search defined');
258
            Assertion::string($this->options[ReadOptions::SEARCH], 'The search term must be a string');
259
        } catch (AssertionFailedException $exception) {
260
            return null;
261
        }
262
263
        return Search::fromString($this->options[ReadOptions::SEARCH]);
264
    }
265
266
    public function getField(): ?array
267
    {
268
        try {
269
            Assertion::keyExists(
270
                $this->options,
271
                ReadOptions::FIELD,
272
                'The key field should exist'
273
            );
274
            Assertion::notEmpty(
275
                $this->options[ReadOptions::FIELD],
276
                'The field option must contain something.'
277
            );
278
            Assertion::isArray(
279
                $this->options[ReadOptions::FIELD],
280
                'The field option must be an array. "fieldHandle" => "value"'
281
            );
282
        } catch (AssertionFailedException $exception) {
283
            return null;
284
        }
285
286
        return $this->options[ReadOptions::FIELD];
287
    }
288
289
    public function getJoin(): ?array
290
    {
291
        try {
292
            Assertion::keyExists($this->options, ReadOptions::JOIN, 'The key join should exist');
293
            Assertion::notEmpty(
294
                $this->options[ReadOptions::JOIN],
295
                'The join option must contain something.'
296
            );
297
            Assertion::isArray(
298
                $this->options[ReadOptions::JOIN],
299
                'The join option must be an array. "fieldHandle" => "value"'
300
            );
301
        } catch (AssertionFailedException $exception) {
302
            return null;
303
        }
304
305
        return $this->options[ReadOptions::JOIN];
306
    }
307
308
    public function getRelate(): ?array
309
    {
310
        try {
311
            Assertion::keyExists($this->options, ReadOptions::RELATE, 'The key relate should exist');
312
            Assertion::notEmpty(
313
                $this->options[ReadOptions::RELATE],
314
                'The relate option must contain something.'
315
            );
316
            Assertion::isArray(
317
                $this->options[ReadOptions::RELATE],
318
                'The relate option must be an array. ["relateProperty", "relateProperty"]'
319
            );
320
        } catch (AssertionFailedException $exception) {
321
            return null;
322
        }
323
324
        return $this->options[ReadOptions::RELATE];
325
    }
326
327
    public function getId(): ?Id
328
    {
329
        try {
330
            Assertion::keyIsset($this->options, ReadOptions::ID, 'This id is not set');
331
            Assertion::digit($this->options[ReadOptions::ID], 'The id is not numeric');
332
        } catch (AssertionFailedException $exception) {
333
            return null;
334
        }
335
336
        return Id::fromInt($this->options[ReadOptions::ID]);
337
    }
338
339
    public function getSlug(): ?Slug
340
    {
341
        try {
342
            Assertion::keyIsset($this->options, ReadOptions::SLUG, 'The slug is not set');
343
344
            // There is a possibility the read options are built with a value object,
345
            // added flexibility by converting value to slug first.
346
            if ($this->options[ReadOptions::SLUG] instanceof Slug) {
347
                Assertion::string((string) $this->options[ReadOptions::SLUG], 'The slug is supposed to be a string');
348
            } else {
349
                Assertion::string($this->options[ReadOptions::SLUG], 'The slug is supposed to be a string');
350
            }
351
352
            return Slug::fromString((string) $this->options[ReadOptions::SLUG]);
353
        } catch (AssertionFailedException $exception) {
354
            return null;
355
        }
356
    }
357
358
    public function getFetchFields(): ?array
359
    {
360
        try {
361
            Assertion::keyIsset($this->options, ReadOptions::FETCH_FIELDS, 'No fetch fields');
362
            Assertion::notEmpty(
363
                $this->options[ReadOptions::FETCH_FIELDS],
364
                'The fetch fields are empty.'
365
            );
366
            Assertion::string($this->options[ReadOptions::FETCH_FIELDS], 'Fetch fields must be a string');
367
        } catch (AssertionFailedException $exception) {
368
            return null;
369
        }
370
371
        return explode(',', $this->options[ReadOptions::FETCH_FIELDS]);
372
    }
373
374
    public function getQuery(): ?string
375
    {
376
        try {
377
            Assertion::keyIsset($this->options, ReadOptions::QUERY, 'No query');
378
            Assertion::notEmpty($this->options[ReadOptions::QUERY], 'The query is empty');
379
            Assertion::string($this->options[ReadOptions::QUERY], 'Query must be a string');
380
        } catch (AssertionFailedException $exception) {
381
            return null;
382
        }
383
384
        return $this->options[ReadOptions::QUERY];
385
    }
386
387
    public function getQueryParameters(): ?array
388
    {
389
        try {
390
            Assertion::keyIsset($this->options, ReadOptions::QUERY_PARAMETERS, 'No query parameters');
391
            Assertion::notEmpty(
392
                $this->options[ReadOptions::QUERY_PARAMETERS],
393
                'The query parameters empty.'
394
            );
395
            Assertion::isArray($this->options[ReadOptions::QUERY_PARAMETERS]);
396
        } catch (AssertionFailedException $exception) {
397
            return null;
398
        }
399
400
        return $this->options[ReadOptions::QUERY_PARAMETERS];
401
    }
402
403
    public static function fromArray(array $options): ReadOptionsInterface
404
    {
405
        return new static($options);
406
    }
407
408
    public function toArray(): array
409
    {
410
        return $this->options;
411
    }
412
}
413