Completed
Push — master ( c04ad6...d6265d )
by Dion
06:50
created

ReadOptions::getJoin()   A

Complexity

Conditions 2
Paths 4

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

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