Test Setup Failed
Push — master ( 6592af...c06444 )
by Chauncey
08:19
created

AbstractProperty::clearVal()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace Charcoal\Property;
4
5
use PDO;
6
use Exception;
7
use LogicException;
8
use RuntimeException;
9
use InvalidArgumentException;
10
11
// From PSR-3
12
use Psr\Log\LoggerAwareInterface;
13
use Psr\Log\LoggerAwareTrait;
14
use Psr\Log\NullLogger;
15
16
// From Pimple
17
use Pimple\Container;
18
19
// From 'charcoal-config'
20
use Charcoal\Config\AbstractEntity;
21
22
// From 'charcoal-core'
23
use Charcoal\Model\DescribableInterface;
24
use Charcoal\Model\DescribableTrait;
25
use Charcoal\Validator\ValidatableInterface;
26
use Charcoal\Validator\ValidatableTrait;
27
use Charcoal\Validator\ValidatorInterface;
28
29
// From 'charcoal-translator'
30
use Charcoal\Translator\Translation;
31
use Charcoal\Translator\TranslatorAwareTrait;
32
33
// From 'charcoal-property'
34
use Charcoal\Property\DescribablePropertyInterface;
35
use Charcoal\Property\DescribablePropertyTrait;
36
use Charcoal\Property\PropertyInterface;
37
use Charcoal\Property\PropertyValidator;
38
use Charcoal\Property\StorablePropertyInterface;
39
use Charcoal\Property\StorablePropertyTrait;
40
41
/**
42
 * An abstract class that implements the full `PropertyInterface`.
43
 */
44
abstract class AbstractProperty extends AbstractEntity implements
45
    PropertyInterface,
46
    DescribableInterface,
47
    DescribablePropertyInterface,
48
    LoggerAwareInterface,
49
    StorablePropertyInterface,
50
    ValidatableInterface
51
{
52
    use LoggerAwareTrait;
53
    use DescribableTrait;
54
    use DescribablePropertyTrait;
55
    use StorablePropertyTrait;
56
    use TranslatorAwareTrait;
57
    use ValidatableTrait;
58
59
    const DEFAULT_L10N = false;
60
    const DEFAULT_MULTIPLE = false;
61
    const DEFAULT_HIDDEN = false;
62
    const DEFAULT_UNIQUE = false;
63
    const DEFAULT_REQUIRED = false;
64
    const DEFAULT_ALLOW_NULL = true;
65
    const DEFAULT_STORABLE = true;
66
    const DEFAULT_ACTIVE = true;
67
68
    /**
69
     * @var string
70
     */
71
    private $ident = '';
72
73
    /**
74
     * @var mixed
75
     */
76
    protected $val;
77
78
    /**
79
     * @var Translation|null
80
     */
81
    private $label;
82
83
    /**
84
     * @var boolean
85
     */
86
    private $l10n = self::DEFAULT_L10N;
87
88
    /**
89
     * @var boolean
90
     */
91
    private $multiple = self::DEFAULT_MULTIPLE;
92
93
    /**
94
     * Array of options for multiple properties
95
     * - `separator` (default=",") How the values will be separated in the storage (sql).
96
     * - `min` (default=null) The min number of values. If null, <0 or NaN, then this is not taken into consideration.
97
     * - `max` (default=null) The max number of values. If null, <0 or NaN, then there is not limit.
98
     * @var array|null
99
     */
100
    private $multipleOptions;
101
102
    /**
103
     * @var boolean
104
     */
105
    private $hidden = self::DEFAULT_HIDDEN;
106
107
    /**
108
     * If true, this property *must* have a value
109
     * @var boolean
110
     */
111
    private $required = self::DEFAULT_REQUIRED;
112
113
    /**
114
     * Unique properties should not share he same value across 2 objects
115
     * @var boolean
116
     */
117
    private $unique = self::DEFAULT_UNIQUE;
118
119
    /**
120
     * @var boolean $allowNull
121
     */
122
    private $allowNull = self::DEFAULT_ALLOW_NULL;
123
124
    /**
125
     * Only the storable properties should be saved in storage.
126
     * @var boolean
127
     */
128
    private $storable = self::DEFAULT_STORABLE;
129
130
    /**
131
     * Inactive properties should be hidden everywhere / unused
132
     * @var boolean
133
     */
134
    private $active = self::DEFAULT_ACTIVE;
135
136
    /**
137
     * @var Translation|null
138
     */
139
    private $description;
140
141
    /**
142
     * @var Translation|null
143
     */
144
    private $notes;
145
146
    /**
147
     * @var array|null
148
     */
149
    protected $viewOptions;
150
151
    /**
152
     * @var string
153
     */
154
    protected $displayType;
155
156
    /**
157
     * @var PDO
158
     */
159
    protected $pdo;
160
161
    /**
162
     * Required dependencies:
163
     * - `logger` a PSR3-compliant logger.
164
     * - `pdo` a PDO database.
165
     * - `translator` a Charcoal Translator (based on Symfony's).
166
     *
167
     * @param array $data Optional. Class Dependencies.
168
     */
169
    public function __construct(array $data = null)
170
    {
171
        $this->setLogger($data['logger']);
172
        $this->setPdo($data['database']);
173
        $this->setTranslator($data['translator']);
174
175
        // Optional DescribableInterface dependencies
176
        if (isset($data['property_factory'])) {
177
            $this->setPropertyFactory($data['property_factory']);
178
        }
179
180
        if (isset($data['metadata_loader'])) {
181
            $this->setMetadataLoader($data['metadata_loader']);
182
        }
183
184
        // DI Container can optionally be set in property constructor.
185
        if (isset($data['container'])) {
186
            $this->setDependencies($data['container']);
187
        }
188
    }
189
190
    /**
191
     * @return string
192
     * @deprecated
193
     */
194
    public function __toString()
195
    {
196
        $val = $this->val();
0 ignored issues
show
Deprecated Code introduced by
The method Charcoal\Property\AbstractProperty::val() has been deprecated.

This method has been deprecated.

Loading history...
197
        if (is_string($val)) {
198
            return $val;
199
        } else {
200
            if (is_object($val)) {
201
                return (string)$val;
202
            } else {
203
                return '';
204
            }
205
        }
206
    }
207
208
    /**
209
     * Get the "property type" string.
210
     *
211
     * ## Notes
212
     * - Type can not be set, so it must be explicitely provided by each implementing property classes.
213
     *
214
     * @return string
215
     */
216
    abstract public function type();
217
218
    /**
219
     * Set the property's identifier.
220
     *
221
     * @param  string $ident The property identifier.
222
     * @throws InvalidArgumentException  If the identifier is not a string.
223
     * @return self
224
     */
225
    public function setIdent($ident)
226
    {
227
        if (!is_string($ident)) {
228
            throw new InvalidArgumentException(
229
                'Ident needs to be string.'
230
            );
231
        }
232
        $this->ident = $ident;
233
234
        return $this;
235
    }
236
237
    /**
238
     * Retrieve the property's identifier.
239
     *
240
     * @return string
241
     */
242
    public function getIdent()
243
    {
244
        return $this->ident;
245
    }
246
247
    /**
248
     * Legacy support of ident() instead of getIdent().
249
     *
250
     * @return string
251
     */
252
    public function ident()
253
    {
254
        return $this->getIdent();
255
    }
256
257
    /**
258
     * Retrieve the property's localized identifier.
259
     *
260
     * @param  string|null $lang The language code to return the identifier with.
261
     * @throws LogicException If the property is not multilingual.
262
     * @throws RuntimeException If the property has no identifier.
263
     * @throws InvalidArgumentException If the language code is invalid.
264
     * @return string
265
     */
266
    public function l10nIdent($lang = null)
267
    {
268
        if ($this->ident === '') {
269
            throw new RuntimeException('Missing Property Identifier');
270
        }
271
272
        if (!$this['l10n']) {
273
            throw new LogicException(sprintf(
274
                'Property "%s" is not multilingual',
275
                $this->ident
276
            ));
277
        }
278
279 View Code Duplication
        if ($lang === null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
280
            $lang = $this->translator()->getLocale();
281
        } elseif (!is_string($lang)) {
282
            throw new InvalidArgumentException(sprintf(
283
                'Language must be a string for Property "%s"',
284
                $this->ident
285
            ));
286
        }
287
288
        return sprintf('%1$s_%2$s', $this->ident, $lang);
289
    }
290
291
    /**
292
     * Set the property's value.
293
     *
294
     * @deprecated
295
     *
296
     * @param  mixed $val The property (raw) value.
297
     * @return self
298
     */
299
    final public function setVal($val)
300
    {
301
        $this->val = $this->parseVal($val);
302
303
        return $this;
304
    }
305
306
    /**
307
     * Clear the property's value.
308
     *
309
     * @deprecated
310
     *
311
     * @return self
312
     */
313
    final public function clearVal()
314
    {
315
        $this->val = null;
316
317
        return $this;
318
    }
319
320
    /**
321
     * Retrieve the property's value.
322
     *
323
     * @deprecated
324
     *
325
     * @return mixed
326
     */
327
    final public function val()
328
    {
329
        return $this->val;
330
    }
331
332
    /**
333
     * Parse the given value.
334
     *
335
     * > Note: the base method (defined here) returns the current value intact.
336
     * > Other properties can reimplement this method to parse their values,
337
     * > such as {@see \Charcoal\Property\ObjectProperty::parseVal()} who could parse objects into object IDs.
338
     *
339
     * @param  mixed $val The value to be parsed (normalized).
340
     * @throws InvalidArgumentException If the value does not match property settings.
341
     * @return mixed Returns the parsed value.
342
     */
343
    final public function parseVal($val)
344
    {
345
        if ($this['allowNull']) {
346
            if ($val === null || $val === '') {
347
                return null;
348
            }
349
        } elseif ($val === null) {
350
            throw new InvalidArgumentException(sprintf(
351
                'Property "%s" value can not be NULL (not allowed)',
352
                $this->ident()
353
            ));
354
        }
355
356
        if ($this['multiple']) {
357
            $val = $this->parseValAsMultiple($val);
358
359
            if (empty($val)) {
360
                if ($this['allowNull'] === false) {
361
                    throw new InvalidArgumentException(sprintf(
362
                        'Property "%s" value can not be NULL or empty (not allowed)',
363
                        $this->ident()
364
                    ));
365
                }
366
367
                return $val;
368
            }
369
370
            $val = array_map([ $this, 'parseOne' ], $val);
371
        } else {
372
            if ($this['l10n']) {
373
                $val = $this->parseValAsL10n($val);
374
375
                if ($val) {
376
                    $val->sanitize([ $this, 'parseOne' ]);
377
                }
378
            } else {
379
                $val = $this->parseOne($val);
380
            }
381
        }
382
383
        return $val;
384
    }
385
386
    /**
387
     * @param mixed $val A single value to parse.
388
     * @return mixed The parsed value.
389
     */
390
    public function parseOne($val)
391
    {
392
        return $val;
393
    }
394
395
    /**
396
     * @param   mixed $val     Optional. The value to to convert for input.
397
     * @param   array $options Optional input options.
398
     * @return  string
399
     */
400
    public function inputVal($val, array $options = [])
401
    {
402
        if ($val === null) {
403
            return '';
404
        }
405
406
        if (is_string($val)) {
407
            return $val;
408
        }
409
410
        /** Parse multilingual values */
411 View Code Duplication
        if ($this['l10n']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
412
            $propertyValue = $this->l10nVal($val, $options);
413
            if ($propertyValue === null) {
414
                return '';
415
            }
416
        } elseif ($val instanceof Translation) {
417
            $propertyValue = (string)$val;
418
        } else {
419
            $propertyValue = $val;
420
        }
421
422
        /** Parse multiple values / ensure they are of array type. */
423
        if ($this['multiple']) {
424
            if (is_array($propertyValue)) {
425
                $propertyValue = implode($this->multipleSeparator(), $propertyValue);
426
            }
427
        }
428
429
        if (!is_scalar($propertyValue)) {
430
            $propertyValue = json_encode($propertyValue, JSON_PRETTY_PRINT);
431
        }
432
433
        return (string)$propertyValue;
434
    }
435
436
    /**
437
     * @param  mixed $val     The value to to convert for display.
438
     * @param  array $options Optional display options.
439
     * @return string
440
     */
441
    public function displayVal($val, array $options = [])
442
    {
443
        if ($val === null || $val === '') {
444
            return '';
445
        }
446
447
        /** Parse multilingual values */
448 View Code Duplication
        if ($this['l10n']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
449
            $propertyValue = $this->l10nVal($val, $options);
450
            if ($propertyValue === null) {
451
                return '';
452
            }
453
        } elseif ($val instanceof Translation) {
454
            $propertyValue = (string)$val;
455
        } else {
456
            $propertyValue = $val;
457
        }
458
459
        /** Parse multiple values / ensure they are of array type. */
460
        if ($this['multiple']) {
461
            if (!is_array($propertyValue)) {
462
                $propertyValue = $this->parseValAsMultiple($propertyValue);
463
            }
464
        }
465
466
        if (is_array($propertyValue)) {
467
            $separator = $this->multipleSeparator();
468
            if ($separator === ',') {
469
                $separator = ', ';
470
            }
471
472
            $propertyValue = implode($separator, $propertyValue);
473
        }
474
475
        return (string)$propertyValue;
476
    }
477
478
    /**
479
     * @param mixed $label The property label.
480
     * @return self
481
     */
482
    public function setLabel($label)
483
    {
484
        $this->label = $this->translator()->translation($label);
485
486
        return $this;
487
    }
488
489
    /**
490
     * @return Translation
491
     */
492
    public function getLabel()
493
    {
494
        if ($this->label === null) {
495
            return ucwords(str_replace([ '.', '_' ], ' ', $this->ident()));
0 ignored issues
show
Bug Best Practice introduced by
The return type of return ucwords(str_repla... ' ', $this->ident())); (string) is incompatible with the return type documented by Charcoal\Property\AbstractProperty::getLabel of type Charcoal\Translator\Translation.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
496
        }
497
498
        return $this->label;
499
    }
500
501
    /**
502
     * @param boolean $l10n The l10n, or "translatable" flag.
503
     * @return self
504
     */
505
    public function setL10n($l10n)
506
    {
507
        $this->l10n = !!$l10n;
508
509
        return $this;
510
    }
511
512
    /**
513
     * The l10n flag sets the property as being translatable, meaning the data is held for multple languages.
514
     *
515
     * @return boolean
516
     */
517
    public function getL10n()
518
    {
519
        return $this->l10n;
520
    }
521
522
    /**
523
     * @param  mixed $val A L10N variable.
524
     * @return Translation The translation value.
525
     */
526
    public function parseValAsL10n($val)
527
    {
528
        return $this->translator()->translation($val);
529
    }
530
531
    /**
532
     * @param boolean $hidden The hidden flag.
533
     * @return self
534
     */
535
    public function setHidden($hidden)
536
    {
537
        $this->hidden = !!$hidden;
538
539
        return $this;
540
    }
541
542
    /**
543
     * @return boolean
544
     */
545
    public function getHidden()
546
    {
547
        return $this->hidden;
548
    }
549
550
    /**
551
     * Set whether this property accepts multiple values or a single value.
552
     *
553
     * @param  boolean $multiple The multiple flag.
554
     * @return self
555
     */
556
    public function setMultiple($multiple)
557
    {
558
        if (!is_bool($multiple)) {
559
            if (is_array($multiple)) {
560
                $this->setMultipleOptions($multiple);
561
            } elseif (is_int($multiple)) {
562
                $this->setMultipleOptions([
563
                    'min' => $multiple,
564
                    'max' => $multiple
565
                ]);
566
            }
567
        }
568
569
        $this->multiple = !!$multiple;
570
571
        return $this;
572
    }
573
574
    /**
575
     * Determine if this property accepts multiple values or a single value.
576
     *
577
     * The multiple flag sets the property as being "repeatable", or allow to represent an array of multiple values.
578
     *
579
     * ## Notes
580
     * - The multiple flag can be forced to false (or true) in implementing property class.
581
     * - How a multiple behaves also depend on `multipleOptions`.
582
     *
583
     * @return boolean
584
     */
585
    public function getMultiple()
586
    {
587
        return $this->multiple;
588
    }
589
590
    /**
591
     * Set the multiple options / configuration, when property is `multiple`.
592
     *
593
     * ## Options structure
594
     * - `separator` (string) The separator charactor.
595
     * - `min` (integer) The minimum number of values. (0 = no limit).
596
     * - `max` (integer) The maximum number of values. (0 = no limit).
597
     *
598
     * @param array $multipleOptions The property multiple options.
599
     * @return self
600
     */
601
    public function setMultipleOptions(array $multipleOptions)
602
    {
603
        // The options are always merged with the defaults, to ensure minimum required array structure.
604
        $options = array_merge($this->defaultMultipleOptions(), $multipleOptions);
605
        $this->multipleOptions = $options;
606
607
        return $this;
608
    }
609
610
    /**
611
     * The options defining the property behavior when the multiple flag is set to true.
612
     *
613
     * @see    self::defaultMultipleOptions
614
     * @param  string|null $key Optional setting to retrieve from the options.
615
     * @return array|mixed|null
616
     */
617
    public function getMultipleOptions($key = null)
618
    {
619
        if ($this->multipleOptions === null) {
620
            $this->multipleOptions = $this->defaultMultipleOptions();
621
        }
622
623
        if (is_string($key)) {
624
            if (isset($this->multipleOptions[$key])) {
625
                return $this->multipleOptions[$key];
626
            } else {
627
                return null;
628
            }
629
        }
630
631
        return $this->multipleOptions;
632
    }
633
634
    /**
635
     * Output the property multiple options as json.
636
     *
637
     * @return string
638
     */
639
    public function multipleOptionsAsJson()
640
    {
641
        return json_encode($this->getMultipleOptions());
642
    }
643
644
    /**
645
     * Retrieve the default settings for a multi-value property.
646
     *
647
     * @return array
648
     */
649
    public function defaultMultipleOptions()
650
    {
651
        return [
652
            'separator' => ',',
653
            'min'       => 0,
654
            'max'       => 0
655
        ];
656
    }
657
658
    /**
659
     * Retrieve the value delimiter for a multi-value property.
660
     *
661
     * @return string
662
     */
663
    public function multipleSeparator()
664
    {
665
        return $this->getMultipleOptions('separator');
666
    }
667
668
    /**
669
     * @param  mixed $val A multi-value variable.
670
     * @return array The array of values.
671
     */
672
    public function parseValAsMultiple($val)
673
    {
674
        if (is_array($val)) {
675
            return $val;
676
        }
677
678
        if ($val === null || $val === '') {
679
            return [];
680
        }
681
682
        if (!is_string($val)) {
683
            return (array)$val;
684
        }
685
686
        return explode($this->multipleSeparator(), $val);
687
    }
688
689
    /**
690
     * @param boolean $allow The property allow null flag.
691
     * @return self
692
     */
693
    public function setAllowNull($allow)
694
    {
695
        $this->allowNull = !!$allow;
696
697
        return $this;
698
    }
699
700
    /**
701
     * The allow null flag sets the property as being able to be of a "null" value.
702
     *
703
     * ## Notes
704
     * - This flag typically modifies the storage database to also allow null values.
705
     *
706
     * @return boolean
707
     */
708
    public function getAllowNull()
709
    {
710
        return $this->allowNull;
711
    }
712
713
    /**
714
     * @param boolean $required The property required flag.
715
     * @return self
716
     */
717
    public function setRequired($required)
718
    {
719
        $this->required = !!$required;
720
721
        return $this;
722
    }
723
724
    /**
725
     * Required flag sets the property as being required, meaning not allowed to be null / empty.
726
     *
727
     * ## Notes
728
     * - The actual meaning of "required" might be different for implementing property class.
729
     *
730
     * @return boolean
731
     */
732
    public function getRequired()
733
    {
734
        return $this->required;
735
    }
736
737
    /**
738
     * @param boolean $unique The property unique flag.
739
     * @return self
740
     */
741
    public function setUnique($unique)
742
    {
743
        $this->unique = !!$unique;
744
745
        return $this;
746
    }
747
748
    /**
749
     * @return boolean
750
     */
751
    public function getUnique()
752
    {
753
        return $this->unique;
754
    }
755
756
    /**
757
     * @param boolean $active The property active flag. Inactive properties should have no effects.
758
     * @return self
759
     */
760
    public function setActive($active)
761
    {
762
        $this->active = !!$active;
763
764
        return $this;
765
    }
766
767
    /**
768
     * @return boolean
769
     */
770
    public function getActive()
771
    {
772
        return $this->active;
773
    }
774
775
    /**
776
     * Legacy support of active() instead of getActive().
777
     *
778
     * @return string
779
     */
780
    public function active()
781
    {
782
        return $this->getActive();
783
    }
784
785
    /**
786
     * @param boolean $storable The storable flag.
787
     * @return self
788
     */
789
    public function setStorable($storable)
790
    {
791
        $this->storable = !!$storable;
792
793
        return $this;
794
    }
795
796
    /**
797
     * @return boolean
798
     */
799
    public function getStorable()
800
    {
801
        return $this->storable;
802
    }
803
804
    /**
805
     * @param mixed $description The property description.
806
     * @return self
807
     */
808
    public function setDescription($description)
809
    {
810
        $this->description = $this->translator()->translation($description);
811
        return $this;
812
    }
813
814
    /**
815
     * @return Translation|null
816
     */
817
    public function getDescription()
818
    {
819
        return $this->description;
820
    }
821
822
    /**
823
     * @param mixed $notes The property notes.
824
     * @return self
825
     */
826
    public function setNotes($notes)
827
    {
828
        $this->notes = $this->translator()->translation($notes);
829
        return $this;
830
    }
831
832
    /**
833
     * @return Translation|null
834
     */
835
    public function getNotes()
836
    {
837
        return $this->notes;
838
    }
839
840
    /**
841
     * The property's default validation methods.
842
     *
843
     * - `required`
844
     * - `unique`
845
     * - `allowNull`
846
     *
847
     * ## Notes
848
     * - Those 3 base validation methods should always be merged, in implementing factory class.
849
     *
850
     * @return string[]
851
     */
852
    public function validationMethods()
853
    {
854
        return [
855
            'required',
856
            'unique',
857
            'allowNull',
858
        ];
859
    }
860
861
    /**
862
     * @return boolean
863
     */
864
    public function validateRequired()
865
    {
866
        if ($this['required'] && !$this->val()) {
0 ignored issues
show
Deprecated Code introduced by
The method Charcoal\Property\AbstractProperty::val() has been deprecated.

This method has been deprecated.

Loading history...
867
            $this->validator()->error('Value is required.', 'required');
0 ignored issues
show
Unused Code introduced by
The call to ValidatorInterface::error() has too many arguments starting with 'required'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
868
869
            return false;
870
        }
871
872
        return true;
873
    }
874
875
    /**
876
     * @return boolean
877
     */
878
    public function validateUnique()
879
    {
880
        if (!$this['unique']) {
881
            return true;
882
        }
883
884
        /** @todo Check in the model's storage if the value already exists. */
885
        return true;
886
    }
887
888
    /**
889
     * @return boolean
890
     */
891
    public function validateAllowNull()
892
    {
893
        if (!$this['allowNull'] && $this->val() === null) {
0 ignored issues
show
Deprecated Code introduced by
The method Charcoal\Property\AbstractProperty::val() has been deprecated.

This method has been deprecated.

Loading history...
894
            $this->validator()->error('Value can not be null.', 'allowNull');
0 ignored issues
show
Unused Code introduced by
The call to ValidatorInterface::error() has too many arguments starting with 'allowNull'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
895
896
            return false;
897
        }
898
899
        return true;
900
    }
901
902
    /**
903
     * @param mixed $val The value, at time of saving.
904
     * @return mixed
905
     */
906
    public function save($val)
907
    {
908
        // By default, nothing to do
909
        return $this->parseVal($val);
910
    }
911
912
    /**
913
     * @param string $type The display type.
914
     * @return self
915
     */
916
    public function setDisplayType($type)
917
    {
918
        $this->displayType = $type;
919
920
        return $this;
921
    }
922
923
    /**
924
     * @return string
925
     */
926
    public function getDisplayType()
927
    {
928
        if (!$this->displayType) {
929
            $meta = $this->metadata();
930
931
            // This default would be defined in type-property.json (@see charcoal-property/metadata)
932
            if (isset($meta['admin']) && isset($meta['admin']['display_type'])) {
933
                $default = $meta['admin']['display_type'];
934
            } else {
935
                $default = 'charcoal/admin/property/display/text';
936
            }
937
            $this->setDisplayType($default);
938
        }
939
940
        return $this->displayType;
941
    }
942
943
    /**
944
     * View options.
945
     * @param string $ident The display ident (ex: charcoal/admin/property/display/text).
946
     * @return array Should ALWAYS be an array.
947
     */
948
    final public function viewOptions($ident = null)
949
    {
950
        // No options defined
951
        if (!$this->viewOptions) {
952
            return [];
953
        }
954
955
        // No ident defined
956
        if (!$ident) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $ident of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
957
            return $this->viewOptions;
958
        }
959
960
        // Invalid ident
961
        if (!isset($this->viewOptions[$ident])) {
962
            return [];
963
        }
964
965
        // Success!
966
        return $this->viewOptions[$ident];
967
    }
968
969
    /**
970
     * Set view options for both display and input
971
     *
972
     * @param array $viewOpts View options.
973
     * @return self
974
     */
975
    final public function setViewOptions(array $viewOpts = [])
976
    {
977
        $this->viewOptions = $viewOpts;
978
979
        return $this;
980
    }
981
982
    /**
983
     * @param Container $container A Pimple DI container.
984
     * @return void
985
     */
986
    protected function setDependencies(Container $container)
987
    {
988
        $this->setPropertyFactory($container['property/factory']);
989
        $this->setMetadataLoader($container['metadata/loader']);
990
    }
991
992
    /**
993
     * Attempt to get the multilingual value in the requested language.
994
     *
995
     * @param  mixed $val  The multilingual value to lookup.
996
     * @param  mixed $lang The language to return the value in.
997
     * @return string|null
998
     */
999
    protected function l10nVal($val, $lang = null)
1000
    {
1001
        if (!is_string($lang)) {
1002
            if (is_array($lang) && isset($lang['lang'])) {
1003
                $lang = $lang['lang'];
1004
            } else {
1005
                $lang = $this->translator()->getLocale();
1006
            }
1007
        }
1008
1009
        if (isset($val[$lang])) {
1010
            return $val[$lang];
1011
        } else {
1012
            return null;
1013
        }
1014
    }
1015
1016
    /**
1017
     * Create a new metadata object.
1018
     *
1019
     * @param  array $data Optional metadata to merge on the object.
1020
     * @see DescribableTrait::createMetadata()
1021
     * @return PropertyMetadata
1022
     */
1023
    protected function createMetadata(array $data = null)
1024
    {
1025
        $class = $this->metadataClass();
1026
        return new $class($data);
1027
    }
1028
1029
    /**
1030
     * Retrieve the class name of the metadata object.
1031
     *
1032
     * @see DescribableTrait::metadataClass()
1033
     * @return string
1034
     */
1035
    protected function metadataClass()
1036
    {
1037
        return PropertyMetadata::class;
1038
    }
1039
1040
    /**
1041
     * Create a Validator object
1042
     *
1043
     * @see ValidatableTrait::createValidator()
1044
     * @return ValidatorInterface
1045
     */
1046
    protected function createValidator()
1047
    {
1048
        $validator = new PropertyValidator($this);
1049
1050
        return $validator;
1051
    }
1052
1053
    /**
1054
     * @param PDO $pdo The database connection (PDO) instance.
1055
     * @return void
1056
     */
1057
    private function setPdo(PDO $pdo)
1058
    {
1059
        $this->pdo = $pdo;
1060
    }
1061
}
1062