Completed
Push — master ( d42871...b03616 )
by Philip
05:00 queued 02:20
created

EntityDefinition::setUnique()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 3
rs 10
c 0
b 0
f 0
nc 1
cc 1
eloc 2
nop 2
1
<?php
2
3
/*
4
 * This file is part of the CRUDlex package.
5
 *
6
 * (c) Philip Lehmann-Böhm <[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
namespace CRUDlex;
13
14
/**
15
 * The class for defining a single entity.
16
 */
17
class EntityDefinition {
18
19
    /**
20
     * The table where the data is stored.
21
     */
22
    protected $table;
23
24
    /**
25
     * Holds all fields in the same structure as in the CRUD YAML file.
26
     */
27
    protected $fields;
28
29
    /**
30
     * The label for the entity.
31
     */
32
    protected $label;
33
34
    /**
35
     * The labels  of the entity in the locales.
36
     */
37
    protected $localeLabels;
38
39
    /**
40
     * An array with the children referencing the entity. All entries are
41
     * arrays with three referencing elements: table, fieldName, entity
42
     */
43
    protected $children;
44
45
    /**
46
     * Labels for the fields "id", "created_at" and "updated_at".
47
     */
48
    protected $standardFieldLabels;
49
50
    /**
51
     * An array containing the fields which should appear in the list view
52
     * of the entity.
53
     */
54
    protected $listFields;
55
56
    /**
57
     * The fields used to display the children on the details page of an entity.
58
     * The keys are the entity names as in the CRUD YAML and the values are the
59
     * field names.
60
     */
61
    protected $childrenLabelFields;
62
63
    /**
64
     * Whether to delete its children when an instance is deleted.
65
     */
66
    protected $deleteCascade;
67
68
    /**
69
     * The amount of items to display per page on the listview.
70
     */
71
    protected $pageSize;
72
73
    /**
74
     * The fields offering to be filtered.
75
     */
76
    protected $filter;
77
78
    /**
79
     * Holds the {@see ServiceProvider}.
80
     */
81
    protected $serviceProvider;
82
83
    /**
84
     * Holds the locale.
85
     */
86
    protected $locale;
87
88
    /**
89
     * Holds the initial sort field.
90
     */
91
    protected $initialSortField;
92
93
    /**
94
     * Holds the initial sort order.
95
     */
96
    protected $initialSortAscending;
97
98
    /**
99
     * Gets the field names exluding the given ones.
100
     *
101
     * @param string[] $exclude
102
     * the field names to exclude
103
     *
104
     * @return array
105
     * all field names excluding the given ones
106
     */
107
    protected function getFilteredFieldNames(array $exclude) {
108
        $fieldNames = $this->getFieldNames(true);
109
        $result     = [];
110
        foreach ($fieldNames as $fieldName) {
111
            if (!in_array($fieldName, $exclude)) {
112
                $result[] = $fieldName;
113
            }
114
        }
115
        return $result;
116
    }
117
118
    /**
119
     * Checks if the given field has the given constraint.
120
     *
121
     * @param string $fieldName
122
     * the field name maybe having the constraint
123
     * @param string $constraint
124
     * the constraint to check, 'required' or 'unique'
125
     *
126
     * @return boolean
127
     * true if the given field has the given constraint
128
     */
129
    protected function isConstraint($fieldName, $constraint) {
130
        $result = $this->getField($fieldName, $constraint);
131
        if ($result === null) {
132
            $result = false;
133
        }
134
        return $result;
135
    }
136
137
    /**
138
     * Checks whether the given field names are declared and existing.
139
     *
140
     * @param string $reference
141
     * a hint towards the source of an invalid field name
142
     * @param array $fieldNames
143
     * the field names to check
144
     * @throws \InvalidArgumentException
145
     * thrown with all invalid field names
146
     */
147
    protected function checkFieldNames($reference, $fieldNames) {
148
        $validFieldNames   = $this->getPublicFieldNames();
149
        $invalidFieldNames = [];
150
        foreach ($fieldNames as $fieldName) {
151
            if (!in_array($fieldName, $validFieldNames)) {
152
                $invalidFieldNames[] = $fieldName;
153
            }
154
        }
155
        if (!empty($invalidFieldNames)) {
156
            throw new \InvalidArgumentException('Invalid fields ('.join(', ', $invalidFieldNames).') in '.$reference.', valid ones are: '.join(', ', $validFieldNames));
157
        }
158
    }
159
160
    /**
161
     * Constructor.
162
     *
163
     * @param string $table
164
     * the table of the entity
165
     * @param array $fields
166
     * the field structure just like the CRUD YAML
167
     * @param string $label
168
     * the label of the entity
169
     * @param array $localeLabels
170
     * the labels  of the entity in the locales
171
     * @param array $standardFieldLabels
172
     * labels for the fields "id", "created_at" and "updated_at"
173
     * @param ServiceProvider $serviceProvider
174
     * The current service provider
175
     */
176
    public function __construct($table, array $fields, $label, $localeLabels, array $standardFieldLabels, ServiceProvider $serviceProvider) {
177
        $this->table               = $table;
178
        $this->fields              = $fields;
179
        $this->label               = $label;
180
        $this->localeLabels        = $localeLabels;
181
        $this->standardFieldLabels = $standardFieldLabels;
182
        $this->serviceProvider     = $serviceProvider;
183
184
        $this->children             = [];
185
        $this->listFields           = [];
186
        $this->childrenLabelFields  = [];
187
        $this->filter               = [];
188
        $this->deleteCascade        = false;
189
        $this->pageSize             = 25;
190
        $this->locale               = null;
191
        $this->initialSortField     = 'created_at';
192
        $this->initialSortAscending = true;
193
    }
194
195
    /**
196
     * Gets all field names, including the implicit ones like "id" or
197
     * "created_at".
198
     *
199
     * @param boolean $includeMany
200
     * whether to include the many fields as well
201
     *
202
     * @return string[]
203
     * the field names
204
     */
205
    public function getFieldNames($includeMany = false) {
206
        $fieldNames = $this->getReadOnlyFields();
207
        foreach ($this->fields as $field => $value) {
208
            if ($includeMany || $this->getType($field) !== 'many') {
209
                $fieldNames[] = $field;
210
            }
211
        }
212
        return $fieldNames;
213
    }
214
215
    /**
216
     * Sets the field names to be used in the listview.
217
     *
218
     * @param array $listFields
219
     * the field names to be used in the listview
220
     */
221
    public function setListFields(array $listFields) {
222
        $this->checkFieldNames('listFields', $listFields);
223
        $this->listFields = $listFields;
224
    }
225
226
    /**
227
     * Gets the field names to be used in the listview. If they were not specified,
228
     * all public field names are returned.
229
     *
230
     * @return array
231
     * the field names to be used in the listview
232
     */
233
    public function getListFields() {
234
        if (!empty($this->listFields)) {
235
            return $this->listFields;
236
        }
237
        return $this->getPublicFieldNames();
238
    }
239
240
    /**
241
     * Gets the fields used to display the children on the details page of an
242
     * entity. The keys are the entity names as in the CRUD YAML and the values
243
     * are the field names.
244
     *
245
     * @return array
246
     * the fields used to display the children on the details page
247
     */
248
    public function getChildrenLabelFields() {
249
        return $this->childrenLabelFields;
250
    }
251
252
    /**
253
     * Sets the fields used to display the children on the details page of an
254
     * entity. The keys are the entity names as in the CRUD YAML and the values
255
     * are the field names.
256
     *
257
     * @param array $childrenLabelFields
258
     * the fields used to display the children on the details page
259
     */
260
    public function setChildrenLabelFields(array $childrenLabelFields) {
261
        $this->childrenLabelFields = $childrenLabelFields;
262
    }
263
264
    /**
265
     * Gets whether to delete its children when an instance is deleted.
266
     *
267
     * @return boolean
268
     * true if so
269
     */
270
    public function isDeleteCascade() {
271
        return $this->deleteCascade;
272
    }
273
274
    /**
275
     * Sets whether to delete its children when an instance is deleted.
276
     *
277
     * @param boolean $deleteCascade
278
     * whether to delete its children when an instance is deleted
279
     */
280
    public function setDeleteCascade($deleteCascade) {
281
        $this->deleteCascade = $deleteCascade;
282
    }
283
284
    /**
285
     * Gets the amount of items to display per page on the listview.
286
     *
287
     * @return integer
288
     * the amount of items to display per page on the listview
289
     */
290
    public function getPageSize() {
291
        return $this->pageSize;
292
    }
293
294
    /**
295
     * Sets the amount of items to display per page on the listview.
296
     *
297
     * @param integer $pageSize
298
     * the amount of items to display per page on the listview
299
     */
300
    public function setPageSize($pageSize) {
301
        $this->pageSize = $pageSize;
302
    }
303
304
    /**
305
     * Gets the fields offering a filter.
306
     *
307
     * @return array
308
     * the fields to filter
309
     */
310
    public function getFilter() {
311
        return $this->filter;
312
    }
313
314
    /**
315
     * Sets the fields offering a filter.
316
     *
317
     * @param array $filter
318
     * the fields to filter
319
     */
320
    public function setFilter(array $filter) {
321
        $this->checkFieldNames('filter', $filter);
322
        $this->filter = $filter;
323
    }
324
325
    /**
326
     * Gets the service provider.
327
     *
328
     * @return ServiceProvider
329
     * the service provider
330
     */
331
    public function getServiceProvider() {
332
        return $this->serviceProvider;
333
    }
334
335
    /**
336
     * Sets the service provider.
337
     *
338
     * @param ServiceProvider $serviceProvider
339
     * the new service provider
340
     */
341
    public function setServiceProvider(ServiceProvider $serviceProvider) {
342
        $this->serviceProvider = $serviceProvider;
343
    }
344
345
    /**
346
     * Gets the public field names. The internal fields "version" and
347
     * "deleted_at" are filtered.
348
     *
349
     * @return array
350
     * the public field names
351
     */
352
    public function getPublicFieldNames() {
353
        $exclude = ['version', 'deleted_at'];
354
        $result  = $this->getFilteredFieldNames($exclude);
355
        return $result;
356
    }
357
358
    /**
359
     * Gets the field names which are editable. Not editable are fields like the
360
     * id or the created_at.
361
     *
362
     * @return array
363
     * the editable field names
364
     */
365
    public function getEditableFieldNames() {
366
        $result = $this->getFilteredFieldNames($this->getReadOnlyFields());
367
        return $result;
368
    }
369
370
    /**
371
     * Gets the read only field names like the id or the created_at.
372
     *
373
     * @return string[]
374
     * the read only field names
375
     */
376
    public function getReadOnlyFields() {
377
        return ['id', 'created_at', 'updated_at', 'version', 'deleted_at'];
378
    }
379
380
    /**
381
     * Gets the type of a field.
382
     *
383
     * @param string $fieldName
384
     * the field name
385
     *
386
     * @return string
387
     * the type or null on invalid field name
388
     */
389
    public function getType($fieldName) {
390
        if ($fieldName === 'id') {
391
            return 'string';
392
        }
393
        if ($fieldName === 'version') {
394
            return 'integer';
395
        }
396
        if (in_array($fieldName, ['created_at', 'updated_at', 'deleted_at'])) {
397
            return 'datetime';
398
        }
399
        return $this->getField($fieldName, 'type');
400
    }
401
402
    /**
403
     * Sets the type of a field.
404
     *
405
     * @param string $fieldName
406
     * the field name
407
     * @param string $value
408
     * the new field type
409
     */
410
    public function setType($fieldName, $value) {
411
        $this->setField($fieldName, 'type', $value);
412
    }
413
414
    /**
415
     * Gets whether a field is required.
416
     *
417
     * @param string $fieldName
418
     * the field name
419
     *
420
     * @return boolean
421
     * true if so
422
     */
423
    public function isRequired($fieldName) {
424
        return $this->isConstraint($fieldName, 'required');
425
    }
426
427
    /**
428
     * Sets whether a field is required.
429
     *
430
     * @param string $fieldName
431
     * the field name
432
     * @param boolean $value
433
     * the new required state
434
     */
435
    public function setRequired($fieldName, $value) {
436
        $this->setField($fieldName, 'required', $value);
437
    }
438
439
    /**
440
     * Gets the label of a field.
441
     *
442
     * @param string $fieldName
443
     * the field name
444
     *
445
     * @return string
446
     * the label of the field or the field name if no label is set in the CRUD
447
     * YAML
448
     */
449
    public function getFieldLabel($fieldName) {
450
451
        $result = $this->getField($fieldName, 'label_'.$this->locale);
452
453
        if ($result === null) {
454
            $result = $this->getField($fieldName, 'label');
455
        }
456
457
        if ($result === null && array_key_exists($fieldName, $this->standardFieldLabels)) {
458
            $result = $this->standardFieldLabels[$fieldName];
459
        }
460
461
        if ($result === null) {
462
            $result = $fieldName;
463
        }
464
465
        return $result;
466
    }
467
468
    /**
469
     * Gets the label of a field.
470
     *
471
     * @param string $fieldName
472
     * the field name
473
     * @param string $value
474
     * the new label of the field
475
     */
476
    public function setFieldLabel($fieldName, $value) {
477
        $this->setField($fieldName, 'label', $value);
478
    }
479
480
    /**
481
     * Gets the table where the data is stored.
482
     *
483
     * @return string
484
     * the table where the data is stored
485
     */
486
    public function getTable() {
487
        return $this->table;
488
    }
489
490
    /**
491
     * Sets the table where the data is stored.
492
     *
493
     * @param string $table
494
     * the new table where the data is stored
495
     */
496
    public function setTable($table) {
497
        $this->table = $table;
498
    }
499
500
    /**
501
     * Gets the label for the entity.
502
     *
503
     * @return string
504
     * the label for the entity
505
     */
506
    public function getLabel() {
507
        if ($this->locale && array_key_exists($this->locale, $this->localeLabels)) {
508
            return $this->localeLabels[$this->locale];
509
        }
510
        return $this->label;
511
    }
512
513
    /**
514
     * Sets the label for the entity.
515
     *
516
     * @param string $label
517
     * the new label for the entity
518
     */
519
    public function setLabel($label) {
520
        $this->label = $label;
521
    }
522
523
    /**
524
     * Adds a child to this definition in case the other
525
     * definition has a reference to this one.
526
     *
527
     * @param string $table
528
     * the table of the referencing definition
529
     * @param string $fieldName
530
     * the field name of the referencing definition
531
     * @param string $entity
532
     * the entity of the referencing definition
533
     */
534
    public function addChild($table, $fieldName, $entity) {
535
        $this->children[] = [$table, $fieldName, $entity];
536
    }
537
538
    /**
539
     * Gets the referencing children to this definition.
540
     *
541
     * @return array
542
     * an array with the children referencing the entity. All entries are arrays
543
     * with three referencing elements: table, fieldName, entity
544
     */
545
    public function getChildren() {
546
        return $this->children;
547
    }
548
549
    /**
550
     * Sets the locale to be used.
551
     *
552
     * @param string $locale
553
     * the locale to be used.
554
     */
555
    public function setLocale($locale) {
556
        $this->locale = $locale;
557
    }
558
559
    /**
560
     * Gets the locale to be used.
561
     *
562
     * @return null|string
563
     * the locale to be used.
564
     */
565
    public function getLocale() {
566
        return $this->locale;
567
    }
568
569
    /**
570
     * Sets the initial sort field.
571
     *
572
     * @param string $initialSortField
573
     * the new initial sort field
574
     */
575
    public function setInitialSortField($initialSortField) {
576
        $this->initialSortField = $initialSortField;
577
    }
578
579
    /**
580
     * Gets the initial sort field.
581
     *
582
     * @return string
583
     * the initial sort field
584
     */
585
    public function getInitialSortField() {
586
        return $this->initialSortField;
587
    }
588
589
    /**
590
     * Sets the initial sort order.
591
     *
592
     * @param boolean $initialSortAscending
593
     * the initial sort order, true if ascending
594
     */
595
    public function setInitialSortAscending($initialSortAscending) {
596
        $this->initialSortAscending = $initialSortAscending;
597
    }
598
599
    /**
600
     * Gets the initial sort order.
601
     *
602
     * @return boolean
603
     * the initial sort order, true if ascending
604
     */
605
    public function isInitialSortAscending() {
606
        return $this->initialSortAscending;
607
    }
608
609
    /**
610
     * Gets a sub field of an field.
611
     *
612
     * @param string $fieldName
613
     * the field name of the sub type
614
     * @param string $subType
615
     * the sub type like "reference" or "many"
616
     * @param string $key
617
     * the key of the value
618
     *
619
     * @return string
620
     * the value of the sub field
621
     */
622
    public function getSubTypeField($fieldName, $subType, $key) {
623
624
        if (!isset($this->fields[$fieldName][$subType][$key])) {
625
            return null;
626
        }
627
628
        return $this->fields[$fieldName][$subType][$key];
629
    }
630
631
    /**
632
     * Gets the value of a field key.
633
     *
634
     * @param string $name
635
     * the name of the field
636
     * @param string $key
637
     * the value of the key
638
     * @param mixed $default
639
     * the default value to return if nothing is found
640
     *
641
     * @return mixed
642
     * the value of the field key or null if not existing
643
     */
644
    public function getField($name, $key, $default = null) {
645
        if (array_key_exists($name, $this->fields) && array_key_exists($key, $this->fields[$name])) {
646
            return $this->fields[$name][$key];
647
        }
648
        return $default;
649
    }
650
651
    /**
652
     * Sets the value of a field key. If the field or the key in the field
653
     * don't exist, they get created.
654
     *
655
     * @param string $name
656
     * the name of the field
657
     * @param string $key
658
     * the value of the key
659
     * @param mixed $value
660
     * the new value
661
     */
662
    public function setField($name, $key, $value) {
663
        if (!array_key_exists($name, $this->fields)) {
664
            $this->fields[$name] = [];
665
        }
666
        $this->fields[$name][$key] = $value;
667
    }
668
669
}
670