Completed
Push — master ( cc63a2...da4c0c )
by Philip
02:48
created

EntityDefinition::getReadOnlyFields()   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 0
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
     * Gets the value of a field key.
120
     *
121
     * @param string $name
122
     * the name of the field
123
     * @param string $key
124
     * the value of the key
125
     * @param mixed $default
126
     * the default value to return if nothing is found
127
     *
128
     * @return mixed
129
     * the value of the field key or null if not existing
130
     */
131
    protected function getFieldValue($name, $key, $default = null) {
132
        if (array_key_exists($name, $this->fields) && array_key_exists($key, $this->fields[$name])) {
133
            return $this->fields[$name][$key];
134
        }
135
        return $default;
136
    }
137
138
    /**
139
     * Sets the value of a field key. If the field or the key in the field
140
     * don't exist, they get created.
141
     *
142
     * @param string $name
143
     * the name of the field
144
     * @param string $key
145
     * the value of the key
146
     * @param mixed $value
147
     * the new value
148
     */
149
    protected function setFieldValue($name, $key, $value) {
150
        if (!array_key_exists($name, $this->fields)) {
151
            $this->fields[$name] = [];
152
        }
153
        $this->fields[$name][$key] = $value;
154
    }
155
156
    /**
157
     * Gets the value of a reference field.
158
     *
159
     * @param string $type
160
     * the reference type like "reference" or "many"
161
     * @param string $fieldName
162
     * the field name of the reference
163
     * @param string $key
164
     * the key of the reference value
165
     *
166
     * @return string
167
     * the value of the reference field
168
     */
169
    protected function getReferenceValue($type, $fieldName, $key) {
170
        if ($this->getType($fieldName) != $type) {
171
            return null;
172
        }
173
        if (!array_key_exists($type, $this->fields[$fieldName])) {
174
            return null;
175
        }
176
        if (!array_key_exists($key, $this->fields[$fieldName][$type])) {
177
            return null;
178
        }
179
        return $this->fields[$fieldName][$type][$key];
180
    }
181
182
    /**
183
     * Checks if the given field has the given constraint.
184
     *
185
     * @param string $fieldName
186
     * the field name maybe having the constraint
187
     * @param string $constraint
188
     * the constraint to check, 'required' or 'unique'
189
     *
190
     * @return boolean
191
     * true if the given field has the given constraint
192
     */
193
    protected function isConstraint($fieldName, $constraint) {
194
        $result = $this->getFieldValue($fieldName, $constraint);
195
        if ($result === null) {
196
            $result = false;
197
        }
198
        return $result;
199
    }
200
201
    /**
202
     * Checks whether the given field names are declared and existing.
203
     *
204
     * @param string $reference
205
     * a hint towards the source of an invalid field name
206
     * @param array $fieldNames
207
     * the field names to check
208
     * @throws \InvalidArgumentException
209
     * thrown with all invalid field names
210
     */
211
    protected function checkFieldNames($reference, $fieldNames) {
212
        $validFieldNames   = $this->getPublicFieldNames();
213
        $invalidFieldNames = [];
214
        foreach ($fieldNames as $fieldName) {
215
            if (!in_array($fieldName, $validFieldNames)) {
216
                $invalidFieldNames[] = $fieldName;
217
            }
218
        }
219
        if (!empty($invalidFieldNames)) {
220
            throw new \InvalidArgumentException('Invalid fields ('.join(', ', $invalidFieldNames).') in '.$reference.', valid ones are: '.join(', ', $validFieldNames));
221
        }
222
    }
223
224
    /**
225
     * Constructor.
226
     *
227
     * @param string $table
228
     * the table of the entity
229
     * @param array $fields
230
     * the field structure just like the CRUD YAML
231
     * @param string $label
232
     * the label of the entity
233
     * @param array $localeLabels
234
     * the labels  of the entity in the locales
235
     * @param array $standardFieldLabels
236
     * labels for the fields "id", "created_at" and "updated_at"
237
     * @param ServiceProvider $serviceProvider
238
     * The current service provider
239
     */
240
    public function __construct($table, array $fields, $label, $localeLabels, array $standardFieldLabels, ServiceProvider $serviceProvider) {
241
        $this->table               = $table;
242
        $this->fields              = $fields;
243
        $this->label               = $label;
244
        $this->localeLabels        = $localeLabels;
245
        $this->standardFieldLabels = $standardFieldLabels;
246
        $this->serviceProvider     = $serviceProvider;
247
248
        $this->children             = [];
249
        $this->listFields           = [];
250
        $this->childrenLabelFields  = [];
251
        $this->filter               = [];
252
        $this->deleteCascade        = false;
253
        $this->pageSize             = 25;
254
        $this->locale               = null;
255
        $this->initialSortField     = 'created_at';
256
        $this->initialSortAscending = true;
257
    }
258
259
    /**
260
     * Gets all field names, including the implicit ones like "id" or
261
     * "created_at".
262
     *
263
     * @param boolean $includeMany
264
     * whether to include the many fields as well
265
     *
266
     * @return string[]
267
     * the field names
268
     */
269
    public function getFieldNames($includeMany = false) {
270
        $fieldNames = $this->getReadOnlyFields();
271
        foreach ($this->fields as $field => $value) {
272
            if ($includeMany || $this->getType($field) !== 'many') {
273
                $fieldNames[] = $field;
274
            }
275
        }
276
        return $fieldNames;
277
    }
278
279
    /**
280
     * Sets the field names to be used in the listview.
281
     *
282
     * @param array $listFields
283
     * the field names to be used in the listview
284
     */
285
    public function setListFields(array $listFields) {
286
        $this->checkFieldNames('listFields', $listFields);
287
        $this->listFields = $listFields;
288
    }
289
290
    /**
291
     * Gets the field names to be used in the listview. If they were not specified,
292
     * all public field names are returned.
293
     *
294
     * @return array
295
     * the field names to be used in the listview
296
     */
297
    public function getListFields() {
298
        if (!empty($this->listFields)) {
299
            return $this->listFields;
300
        }
301
        return $this->getPublicFieldNames();
302
    }
303
304
    /**
305
     * Gets the fields used to display the children on the details page of an
306
     * entity. The keys are the entity names as in the CRUD YAML and the values
307
     * are the field names.
308
     *
309
     * @return array
310
     * the fields used to display the children on the details page
311
     */
312
    public function getChildrenLabelFields() {
313
        return $this->childrenLabelFields;
314
    }
315
316
    /**
317
     * Sets the fields used to display the children on the details page of an
318
     * entity. The keys are the entity names as in the CRUD YAML and the values
319
     * are the field names.
320
     *
321
     * @param array $childrenLabelFields
322
     * the fields used to display the children on the details page
323
     */
324
    public function setChildrenLabelFields(array $childrenLabelFields) {
325
        $this->childrenLabelFields = $childrenLabelFields;
326
    }
327
328
    /**
329
     * Gets whether to delete its children when an instance is deleted.
330
     *
331
     * @return boolean
332
     * true if so
333
     */
334
    public function isDeleteCascade() {
335
        return $this->deleteCascade;
336
    }
337
338
    /**
339
     * Sets whether to delete its children when an instance is deleted.
340
     *
341
     * @param boolean $deleteCascade
342
     * whether to delete its children when an instance is deleted
343
     */
344
    public function setDeleteCascade($deleteCascade) {
345
        $this->deleteCascade = $deleteCascade;
346
    }
347
348
    /**
349
     * Gets the amount of items to display per page on the listview.
350
     *
351
     * @return integer
352
     * the amount of items to display per page on the listview
353
     */
354
    public function getPageSize() {
355
        return $this->pageSize;
356
    }
357
358
    /**
359
     * Sets the amount of items to display per page on the listview.
360
     *
361
     * @param integer $pageSize
362
     * the amount of items to display per page on the listview
363
     */
364
    public function setPageSize($pageSize) {
365
        $this->pageSize = $pageSize;
366
    }
367
368
    /**
369
     * Gets the fields offering a filter.
370
     *
371
     * @return array
372
     * the fields to filter
373
     */
374
    public function getFilter() {
375
        return $this->filter;
376
    }
377
378
    /**
379
     * Sets the fields offering a filter.
380
     *
381
     * @param array $filter
382
     * the fields to filter
383
     */
384
    public function setFilter(array $filter) {
385
        $this->checkFieldNames('filter', $filter);
386
        $this->filter = $filter;
387
    }
388
389
    /**
390
     * Gets the service provider.
391
     *
392
     * @return ServiceProvider
393
     * the service provider
394
     */
395
    public function getServiceProvider() {
396
        return $this->serviceProvider;
397
    }
398
399
    /**
400
     * Sets the service provider.
401
     *
402
     * @param ServiceProvider $serviceProvider
403
     * the new service provider
404
     */
405
    public function setServiceProvider(ServiceProvider $serviceProvider) {
406
        $this->serviceProvider = $serviceProvider;
407
    }
408
409
    /**
410
     * Gets the public field names. The internal fields "version" and
411
     * "deleted_at" are filtered.
412
     *
413
     * @return array
414
     * the public field names
415
     */
416
    public function getPublicFieldNames() {
417
        $exclude = ['version', 'deleted_at'];
418
        $result  = $this->getFilteredFieldNames($exclude);
419
        return $result;
420
    }
421
422
    /**
423
     * Gets the field names which are editable. Not editable are fields like the
424
     * id or the created_at.
425
     *
426
     * @return array
427
     * the editable field names
428
     */
429
    public function getEditableFieldNames() {
430
        $result = $this->getFilteredFieldNames($this->getReadOnlyFields());
431
        return $result;
432
    }
433
434
    /**
435
     * Gets the read only field names like the id or the created_at.
436
     *
437
     * @return string[]
438
     * the read only field names
439
     */
440
    public function getReadOnlyFields() {
441
        return ['id', 'created_at', 'updated_at', 'version', 'deleted_at'];
442
    }
443
444
    /**
445
     * Gets the type of a field.
446
     *
447
     * @param string $fieldName
448
     * the field name
449
     *
450
     * @return string
451
     * the type or null on invalid field name
452
     */
453
    public function getType($fieldName) {
454
        if ($fieldName === 'id') {
455
            return 'string';
456
        }
457
        if ($fieldName === 'version') {
458
            return 'integer';
459
        }
460
        if (in_array($fieldName, ['created_at', 'updated_at', 'deleted_at'])) {
461
            return 'datetime';
462
        }
463
        return $this->getFieldValue($fieldName, 'type');
464
    }
465
466
    /**
467
     * Sets the type of a field.
468
     *
469
     * @param string $fieldName
470
     * the field name
471
     * @param string $value
472
     * the new field type
473
     */
474
    public function setType($fieldName, $value) {
475
        $this->setFieldValue($fieldName, 'type', $value);
476
    }
477
478
    /**
479
     * Gets whether a field is required.
480
     *
481
     * @param string $fieldName
482
     * the field name
483
     *
484
     * @return boolean
485
     * true if so
486
     */
487
    public function isRequired($fieldName) {
488
        return $this->isConstraint($fieldName, 'required');
489
    }
490
491
    /**
492
     * Sets whether a field is required.
493
     *
494
     * @param string $fieldName
495
     * the field name
496
     * @param boolean $value
497
     * the new required state
498
     */
499
    public function setRequired($fieldName, $value) {
500
        $this->setFieldValue($fieldName, 'required', $value);
501
    }
502
503
    /**
504
     * Gets whether a field is unique.
505
     *
506
     * @param string $fieldName
507
     * the field name
508
     *
509
     * @return boolean
510
     * true if so
511
     */
512
    public function isUnique($fieldName) {
513
        return $this->isConstraint($fieldName, 'unique');
514
    }
515
516
    /**
517
     * Sets whether a field is unique.
518
     *
519
     * @param string $fieldName
520
     * the field name
521
     * @param boolean $value
522
     * true if so
523
     */
524
    public function setUnique($fieldName, $value) {
525
        $this->setFieldValue($fieldName, 'unique', $value);
526
    }
527
528
    /**
529
     * Gets the entity field of a reference.
530
     *
531
     * @param string $fieldName
532
     * the field name of the reference
533
     *
534
     * @return string
535
     * the entity field of a reference or null on invalid field name
536
     */
537
    public function getReferenceEntity($fieldName) {
538
        return $this->getReferenceValue('reference', $fieldName, 'entity');
539
    }
540
541
    /**
542
     * Gets the file path of a field.
543
     *
544
     * @param string $fieldName
545
     * the field name
546
     *
547
     * @return string
548
     * the file path of a field or null on invalid field name
549
     */
550
    public function getPath($fieldName) {
551
        return $this->getFieldValue($fieldName, 'path');
552
    }
553
554
    /**
555
     * Sets the file path of a field.
556
     *
557
     * @param string $fieldName
558
     * the field name
559
     * @param string $value
560
     * the file path of a field or null on invalid field name
561
     */
562
    public function setPath($fieldName, $value) {
563
        $this->setFieldValue($fieldName, 'path', $value);
564
    }
565
566
    /**
567
     * Gets the value of a fixed field.
568
     *
569
     * @param string $fieldName
570
     * the field name
571
     *
572
     * @return string
573
     * the value of a fixed field or null on invalid field name
574
     */
575
    public function getValue($fieldName) {
576
        return $this->getFieldValue($fieldName, 'value');
577
    }
578
579
    /**
580
     * Sets the value of a fixed field.
581
     *
582
     * @param string $fieldName
583
     * the field name
584
     * @param string $value
585
     * the new value for the fixed field
586
     */
587
    public function setValue($fieldName, $value) {
588
        $this->setFieldValue($fieldName, 'value', $value);
589
    }
590
591
    /**
592
     * Gets the items of a set field.
593
     *
594
     * @param string $fieldName
595
     * the field name
596
     *
597
     * @return array
598
     * the items of the set field or empty array on invalid field name
599
     */
600
    public function getItems($fieldName) {
601
        $result = $this->getFieldValue($fieldName, 'items', []);
602
        return $result;
603
    }
604
605
    /**
606
     * Sets the items of a set field.
607
     *
608
     * @param string $fieldName
609
     * the field name
610
     * @param string $value
611
     * the new items of the set field
612
     */
613
    public function setItems($fieldName, $value) {
614
        $this->setFieldValue($fieldName, 'items', $value);
615
    }
616
617
    /**
618
     * Gets the step size of a float field.
619
     *
620
     * @param string $fieldName
621
     * the field name
622
     *
623
     * @return array
624
     * the step size of a float field or null on invalid field name
625
     */
626
    public function getFloatStep($fieldName) {
627
        return $this->getFieldValue($fieldName, 'floatStep');
628
    }
629
630
    /**
631
     * Sets the step size of a float field.
632
     *
633
     * @param string $fieldName
634
     * the field name
635
     * @param string $value
636
     * the new step size of the float field
637
     */
638
    public function setFloatStep($fieldName, $value) {
639
        $this->setFieldValue($fieldName, 'floatStep', $value);
640
    }
641
642
    /**
643
     * Gets the label of a field.
644
     *
645
     * @param string $fieldName
646
     * the field name
647
     *
648
     * @return string
649
     * the label of the field or the field name if no label is set in the CRUD
650
     * YAML
651
     */
652
    public function getFieldLabel($fieldName) {
653
654
        $result = $this->getFieldValue($fieldName, 'label_'.$this->locale);
655
656
        if ($result === null) {
657
            $result = $this->getFieldValue($fieldName, 'label');
658
        }
659
660
        if ($result === null && array_key_exists($fieldName, $this->standardFieldLabels)) {
661
            $result = $this->standardFieldLabels[$fieldName];
662
        }
663
664
        if ($result === null) {
665
            $result = $fieldName;
666
        }
667
668
        return $result;
669
    }
670
671
    /**
672
     * Gets the label of a field.
673
     *
674
     * @param string $fieldName
675
     * the field name
676
     * @param string $value
677
     * the new label of the field
678
     */
679
    public function setFieldLabel($fieldName, $value) {
680
        $this->setFieldValue($fieldName, 'label', $value);
681
    }
682
683
    /**
684
     * Gets the table where the data is stored.
685
     *
686
     * @return string
687
     * the table where the data is stored
688
     */
689
    public function getTable() {
690
        return $this->table;
691
    }
692
693
    /**
694
     * Sets the table where the data is stored.
695
     *
696
     * @param string $table
697
     * the new table where the data is stored
698
     */
699
    public function setTable($table) {
700
        $this->table = $table;
701
    }
702
703
    /**
704
     * Gets the label for the entity.
705
     *
706
     * @return string
707
     * the label for the entity
708
     */
709
    public function getLabel() {
710
        if ($this->locale && array_key_exists($this->locale, $this->localeLabels)) {
711
            return $this->localeLabels[$this->locale];
712
        }
713
        return $this->label;
714
    }
715
716
    /**
717
     * Sets the label for the entity.
718
     *
719
     * @param string $label
720
     * the new label for the entity
721
     */
722
    public function setLabel($label) {
723
        $this->label = $label;
724
    }
725
726
    /**
727
     * Gets the description of a field.
728
     *
729
     * @param string $fieldName
730
     * the field name
731
     *
732
     * @return string
733
     * the description of the field
734
     */
735
    public function getDescription($fieldName) {
736
        return $this->getFieldValue($fieldName, 'description');
737
    }
738
739
    /**
740
     * Sets the description of a field.
741
     *
742
     * @param string $fieldName
743
     * the field name
744
     * @param string $value
745
     * the new description of the field
746
     */
747
    public function setDescription($fieldName, $value) {
748
        $this->setFieldValue($fieldName, 'description', $value);
749
    }
750
751
    /**
752
     * Adds a child to this definition in case the other
753
     * definition has a reference to this one.
754
     *
755
     * @param string $table
756
     * the table of the referencing definition
757
     * @param string $fieldName
758
     * the field name of the referencing definition
759
     * @param string $entity
760
     * the entity of the referencing definition
761
     */
762
    public function addChild($table, $fieldName, $entity) {
763
        $this->children[] = [$table, $fieldName, $entity];
764
    }
765
766
    /**
767
     * Gets the referencing children to this definition.
768
     *
769
     * @return array
770
     * an array with the children referencing the entity. All entries are arrays
771
     * with three referencing elements: table, fieldName, entity
772
     */
773
    public function getChildren() {
774
        return $this->children;
775
    }
776
777
    /**
778
     * Sets the locale to be used.
779
     *
780
     * @param string $locale
781
     * the locale to be used.
782
     */
783
    public function setLocale($locale) {
784
        $this->locale = $locale;
785
    }
786
787
    /**
788
     * Gets the locale to be used.
789
     *
790
     * @return null|string
791
     * the locale to be used.
792
     */
793
    public function getLocale() {
794
        return $this->locale;
795
    }
796
797
    /**
798
     * Sets the initial sort field.
799
     *
800
     * @param string $initialSortField
801
     * the new initial sort field
802
     */
803
    public function setInitialSortField($initialSortField) {
804
        $this->initialSortField = $initialSortField;
805
    }
806
807
    /**
808
     * Gets the initial sort field.
809
     *
810
     * @return string
811
     * the initial sort field
812
     */
813
    public function getInitialSortField() {
814
        return $this->initialSortField;
815
    }
816
817
    /**
818
     * Sets the initial sort order.
819
     *
820
     * @param boolean $initialSortAscending
821
     * the initial sort order, true if ascending
822
     */
823
    public function setInitialSortAscending($initialSortAscending) {
824
        $this->initialSortAscending = $initialSortAscending;
825
    }
826
827
    /**
828
     * Gets the initial sort order.
829
     *
830
     * @return boolean
831
     * the initial sort order, true if ascending
832
     */
833
    public function isInitialSortAscending() {
834
        return $this->initialSortAscending;
835
    }
836
837
    /**
838
     * Gets a sub field of an field.
839
     *
840
     * @param string $fieldName
841
     * the field name of the sub type
842
     * @param string $subType
843
     * the sub type like "reference" or "many"
844
     * @param string $key
845
     * the key of the value
846
     *
847
     * @return string
848
     * the value of the sub field
849
     */
850
    public function getSubTypeField($fieldName, $subType, $key) {
851
852
        if (!isset($this->fields[$fieldName][$subType][$key])) {
853
            return null;
854
        }
855
856
        return $this->fields[$fieldName][$subType][$key];
857
    }
858
}
859