These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
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_VALIDATABLE = true; |
||
67 | const DEFAULT_ACTIVE = true; |
||
68 | |||
69 | /** |
||
70 | * @var string |
||
71 | */ |
||
72 | private $ident = ''; |
||
73 | |||
74 | /** |
||
75 | * @var mixed |
||
76 | */ |
||
77 | protected $val; |
||
78 | |||
79 | /** |
||
80 | * @var Translation|null |
||
81 | */ |
||
82 | private $label; |
||
83 | |||
84 | /** |
||
85 | * @var boolean |
||
86 | */ |
||
87 | private $l10n = self::DEFAULT_L10N; |
||
88 | |||
89 | /** |
||
90 | * @var boolean |
||
91 | */ |
||
92 | private $multiple = self::DEFAULT_MULTIPLE; |
||
93 | |||
94 | /** |
||
95 | * Array of options for multiple properties |
||
96 | * - `separator` (default=",") How the values will be separated in the storage (sql). |
||
97 | * - `min` (default=null) The min number of values. If null, <0 or NaN, then this is not taken into consideration. |
||
98 | * - `max` (default=null) The max number of values. If null, <0 or NaN, then there is not limit. |
||
99 | * @var array|null |
||
100 | */ |
||
101 | private $multipleOptions; |
||
102 | |||
103 | /** |
||
104 | * @var boolean |
||
105 | */ |
||
106 | private $hidden = self::DEFAULT_HIDDEN; |
||
107 | |||
108 | /** |
||
109 | * If true, this property *must* have a value |
||
110 | * @var boolean |
||
111 | */ |
||
112 | private $required = self::DEFAULT_REQUIRED; |
||
113 | |||
114 | /** |
||
115 | * Unique properties should not share he same value across 2 objects |
||
116 | * @var boolean |
||
117 | */ |
||
118 | private $unique = self::DEFAULT_UNIQUE; |
||
119 | |||
120 | /** |
||
121 | * @var boolean $allowNull |
||
122 | */ |
||
123 | private $allowNull = self::DEFAULT_ALLOW_NULL; |
||
124 | |||
125 | /** |
||
126 | * Only the storable properties should be saved in storage. |
||
127 | * @var boolean |
||
128 | */ |
||
129 | private $storable = self::DEFAULT_STORABLE; |
||
130 | |||
131 | /** |
||
132 | * Whether to validate the property. |
||
133 | * @var boolean |
||
134 | */ |
||
135 | private $validatable = self::DEFAULT_VALIDATABLE; |
||
136 | |||
137 | /** |
||
138 | * Inactive properties should be hidden everywhere / unused |
||
139 | * @var boolean |
||
140 | */ |
||
141 | private $active = self::DEFAULT_ACTIVE; |
||
142 | |||
143 | /** |
||
144 | * @var Translation|null |
||
145 | */ |
||
146 | private $description; |
||
147 | |||
148 | /** |
||
149 | * @var Translation|null |
||
150 | */ |
||
151 | private $notes; |
||
152 | |||
153 | /** |
||
154 | * @var array|null |
||
155 | */ |
||
156 | protected $viewOptions; |
||
157 | |||
158 | /** |
||
159 | * @var string |
||
160 | */ |
||
161 | protected $displayType; |
||
162 | |||
163 | /** |
||
164 | * @var PDO |
||
165 | */ |
||
166 | protected $pdo; |
||
167 | |||
168 | /** |
||
169 | * Required dependencies: |
||
170 | * - `logger` a PSR3-compliant logger. |
||
171 | * - `pdo` a PDO database. |
||
172 | * - `translator` a Charcoal Translator (based on Symfony's). |
||
173 | * |
||
174 | * @param array $data Optional. Class Dependencies. |
||
175 | */ |
||
176 | public function __construct(array $data = null) |
||
177 | { |
||
178 | $this->setLogger($data['logger']); |
||
179 | $this->setPdo($data['database']); |
||
180 | $this->setTranslator($data['translator']); |
||
181 | |||
182 | // Optional DescribableInterface dependencies |
||
183 | if (isset($data['property_factory'])) { |
||
184 | $this->setPropertyFactory($data['property_factory']); |
||
185 | } |
||
186 | |||
187 | if (isset($data['metadata_loader'])) { |
||
188 | $this->setMetadataLoader($data['metadata_loader']); |
||
189 | } |
||
190 | |||
191 | // DI Container can optionally be set in property constructor. |
||
192 | if (isset($data['container'])) { |
||
193 | $this->setDependencies($data['container']); |
||
194 | } |
||
195 | } |
||
196 | |||
197 | /** |
||
198 | * @return string |
||
199 | * @deprecated |
||
200 | */ |
||
201 | public function __toString() |
||
202 | { |
||
203 | $val = $this->val(); |
||
204 | if (is_string($val)) { |
||
205 | return $val; |
||
206 | } else { |
||
207 | if (is_object($val)) { |
||
208 | return (string)$val; |
||
209 | } else { |
||
210 | return ''; |
||
211 | } |
||
212 | } |
||
213 | } |
||
214 | |||
215 | /** |
||
216 | * Get the "property type" string. |
||
217 | * |
||
218 | * ## Notes |
||
219 | * - Type can not be set, so it must be explicitely provided by each implementing property classes. |
||
220 | * |
||
221 | * @return string |
||
222 | */ |
||
223 | abstract public function type(); |
||
224 | |||
225 | /** |
||
226 | * Set the property's identifier. |
||
227 | * |
||
228 | * @param string $ident The property identifier. |
||
229 | * @throws InvalidArgumentException If the identifier is not a string. |
||
230 | * @return self |
||
231 | */ |
||
232 | public function setIdent($ident) |
||
233 | { |
||
234 | if (!is_string($ident)) { |
||
235 | throw new InvalidArgumentException( |
||
236 | 'Ident needs to be string.' |
||
237 | ); |
||
238 | } |
||
239 | $this->ident = $ident; |
||
240 | |||
241 | return $this; |
||
242 | } |
||
243 | |||
244 | /** |
||
245 | * Retrieve the property's identifier. |
||
246 | * |
||
247 | * @return string |
||
248 | */ |
||
249 | public function getIdent() |
||
250 | { |
||
251 | return $this->ident; |
||
252 | } |
||
253 | |||
254 | /** |
||
255 | * Legacy support of ident() instead of getIdent(). |
||
256 | * |
||
257 | * @return string |
||
258 | */ |
||
259 | public function ident() |
||
260 | { |
||
261 | return $this->getIdent(); |
||
262 | } |
||
263 | |||
264 | /** |
||
265 | * Retrieve the property's localized identifier. |
||
266 | * |
||
267 | * @param string|null $lang The language code to return the identifier with. |
||
268 | * @throws LogicException If the property is not multilingual. |
||
269 | * @throws RuntimeException If the property has no identifier. |
||
270 | * @throws InvalidArgumentException If the language code is invalid. |
||
271 | * @return string |
||
272 | */ |
||
273 | public function l10nIdent($lang = null) |
||
274 | { |
||
275 | if ($this->ident === '') { |
||
276 | throw new RuntimeException('Missing Property Identifier'); |
||
277 | } |
||
278 | |||
279 | if (!$this['l10n']) { |
||
280 | throw new LogicException(sprintf( |
||
281 | 'Property "%s" is not multilingual', |
||
282 | $this->ident |
||
283 | )); |
||
284 | } |
||
285 | |||
286 | View Code Duplication | if ($lang === null) { |
|
287 | $lang = $this->translator()->getLocale(); |
||
288 | } elseif (!is_string($lang)) { |
||
289 | throw new InvalidArgumentException(sprintf( |
||
290 | 'Language must be a string for Property "%s"', |
||
291 | $this->ident |
||
292 | )); |
||
293 | } |
||
294 | |||
295 | return sprintf('%1$s_%2$s', $this->ident, $lang); |
||
296 | } |
||
297 | |||
298 | /** |
||
299 | * Set the property's value. |
||
300 | * |
||
301 | * @deprecated |
||
302 | * |
||
303 | * @param mixed $val The property (raw) value. |
||
304 | * @return self |
||
305 | */ |
||
306 | final public function setVal($val) |
||
307 | { |
||
308 | $this->val = $this->parseVal($val); |
||
309 | |||
310 | return $this; |
||
311 | } |
||
312 | |||
313 | /** |
||
314 | * Clear the property's value. |
||
315 | * |
||
316 | * @deprecated |
||
317 | * |
||
318 | * @return self |
||
319 | */ |
||
320 | final public function clearVal() |
||
321 | { |
||
322 | $this->val = null; |
||
323 | |||
324 | return $this; |
||
325 | } |
||
326 | |||
327 | /** |
||
328 | * Retrieve the property's value. |
||
329 | * |
||
330 | * @deprecated |
||
331 | * |
||
332 | * @return mixed |
||
333 | */ |
||
334 | final public function val() |
||
335 | { |
||
336 | return $this->val; |
||
337 | } |
||
338 | |||
339 | /** |
||
340 | * Parse the given value. |
||
341 | * |
||
342 | * > Note: the base method (defined here) returns the current value intact. |
||
343 | * > Other properties can reimplement this method to parse their values, |
||
344 | * > such as {@see \Charcoal\Property\ObjectProperty::parseVal()} who could parse objects into object IDs. |
||
345 | * |
||
346 | * @param mixed $val The value to be parsed (normalized). |
||
347 | * @throws InvalidArgumentException If the value does not match property settings. |
||
348 | * @return mixed Returns the parsed value. |
||
349 | */ |
||
350 | final public function parseVal($val) |
||
351 | { |
||
352 | if ($this['allowNull']) { |
||
353 | if ($val === null || $val === '') { |
||
354 | return null; |
||
355 | } |
||
356 | } elseif ($val === null) { |
||
357 | throw new InvalidArgumentException(sprintf( |
||
358 | 'Property "%s" value can not be NULL (not allowed)', |
||
359 | $this->ident() |
||
360 | )); |
||
361 | } |
||
362 | |||
363 | if ($this['multiple']) { |
||
364 | $val = $this->parseValAsMultiple($val); |
||
365 | |||
366 | if (empty($val)) { |
||
367 | if ($this['allowNull'] === false) { |
||
368 | throw new InvalidArgumentException(sprintf( |
||
369 | 'Property "%s" value can not be NULL or empty (not allowed)', |
||
370 | $this->ident() |
||
371 | )); |
||
372 | } |
||
373 | |||
374 | return $val; |
||
375 | } |
||
376 | |||
377 | $val = array_map([ $this, 'parseOne' ], $val); |
||
378 | } else { |
||
379 | if ($this['l10n']) { |
||
380 | $val = $this->parseValAsL10n($val); |
||
381 | |||
382 | if ($val) { |
||
383 | $val->sanitize([ $this, 'parseOne' ]); |
||
384 | } |
||
385 | } else { |
||
386 | $val = $this->parseOne($val); |
||
387 | } |
||
388 | } |
||
389 | |||
390 | return $val; |
||
391 | } |
||
392 | |||
393 | /** |
||
394 | * @param mixed $val A single value to parse. |
||
395 | * @return mixed The parsed value. |
||
396 | */ |
||
397 | public function parseOne($val) |
||
398 | { |
||
399 | return $val; |
||
400 | } |
||
401 | |||
402 | /** |
||
403 | * @param mixed $val Optional. The value to to convert for input. |
||
404 | * @param array $options Optional input options. |
||
405 | * @return string |
||
406 | */ |
||
407 | public function inputVal($val, array $options = []) |
||
408 | { |
||
409 | if ($val === null) { |
||
410 | return ''; |
||
411 | } |
||
412 | |||
413 | if (is_string($val)) { |
||
414 | return $val; |
||
415 | } |
||
416 | |||
417 | /** Parse multilingual values */ |
||
418 | View Code Duplication | if ($this['l10n']) { |
|
419 | $propertyValue = $this->l10nVal($val, $options); |
||
420 | if ($propertyValue === null) { |
||
421 | return ''; |
||
422 | } |
||
423 | } elseif ($val instanceof Translation) { |
||
424 | $propertyValue = (string)$val; |
||
425 | } else { |
||
426 | $propertyValue = $val; |
||
427 | } |
||
428 | |||
429 | /** Parse multiple values / ensure they are of array type. */ |
||
430 | if ($this['multiple']) { |
||
431 | if (is_array($propertyValue)) { |
||
432 | $propertyValue = implode($this->multipleSeparator(), $propertyValue); |
||
433 | } |
||
434 | } |
||
435 | |||
436 | if (!is_scalar($propertyValue)) { |
||
437 | $propertyValue = json_encode($propertyValue, JSON_PRETTY_PRINT); |
||
438 | } |
||
439 | |||
440 | return (string)$propertyValue; |
||
441 | } |
||
442 | |||
443 | /** |
||
444 | * @param mixed $val The value to to convert for display. |
||
445 | * @param array $options Optional display options. |
||
446 | * @return string |
||
447 | */ |
||
448 | public function displayVal($val, array $options = []) |
||
449 | { |
||
450 | if ($val === null || $val === '') { |
||
451 | return ''; |
||
452 | } |
||
453 | |||
454 | /** Parse multilingual values */ |
||
455 | View Code Duplication | if ($this['l10n']) { |
|
456 | $propertyValue = $this->l10nVal($val, $options); |
||
457 | if ($propertyValue === null) { |
||
458 | return ''; |
||
459 | } |
||
460 | } elseif ($val instanceof Translation) { |
||
461 | $propertyValue = (string)$val; |
||
462 | } else { |
||
463 | $propertyValue = $val; |
||
464 | } |
||
465 | |||
466 | /** Parse multiple values / ensure they are of array type. */ |
||
467 | if ($this['multiple']) { |
||
468 | if (!is_array($propertyValue)) { |
||
469 | $propertyValue = $this->parseValAsMultiple($propertyValue); |
||
470 | } |
||
471 | } |
||
472 | |||
473 | if (is_array($propertyValue)) { |
||
474 | $separator = $this->multipleSeparator(); |
||
475 | if ($separator === ',') { |
||
476 | $separator = ', '; |
||
477 | } |
||
478 | |||
479 | $propertyValue = implode($separator, $propertyValue); |
||
480 | } |
||
481 | |||
482 | return (string)$propertyValue; |
||
483 | } |
||
484 | |||
485 | /** |
||
486 | * @param mixed $label The property label. |
||
487 | * @return self |
||
488 | */ |
||
489 | public function setLabel($label) |
||
490 | { |
||
491 | $this->label = $this->translator()->translation($label); |
||
492 | |||
493 | return $this; |
||
494 | } |
||
495 | |||
496 | /** |
||
497 | * @return Translation |
||
498 | */ |
||
499 | public function getLabel() |
||
500 | { |
||
501 | if ($this->label === null) { |
||
502 | return ucwords(str_replace([ '.', '_' ], ' ', $this->ident())); |
||
503 | } |
||
504 | |||
505 | return $this->label; |
||
506 | } |
||
507 | |||
508 | /** |
||
509 | * @param boolean $l10n The l10n, or "translatable" flag. |
||
510 | * @return self |
||
511 | */ |
||
512 | public function setL10n($l10n) |
||
513 | { |
||
514 | $this->l10n = !!$l10n; |
||
515 | |||
516 | return $this; |
||
517 | } |
||
518 | |||
519 | /** |
||
520 | * The l10n flag sets the property as being translatable, meaning the data is held for multple languages. |
||
521 | * |
||
522 | * @return boolean |
||
523 | */ |
||
524 | public function getL10n() |
||
525 | { |
||
526 | return $this->l10n; |
||
527 | } |
||
528 | |||
529 | /** |
||
530 | * @param mixed $val A L10N variable. |
||
531 | * @return Translation The translation value. |
||
532 | */ |
||
533 | public function parseValAsL10n($val) |
||
534 | { |
||
535 | return $this->translator()->translation($val); |
||
536 | } |
||
537 | |||
538 | /** |
||
539 | * @param boolean $hidden The hidden flag. |
||
540 | * @return self |
||
541 | */ |
||
542 | public function setHidden($hidden) |
||
543 | { |
||
544 | $this->hidden = !!$hidden; |
||
545 | |||
546 | return $this; |
||
547 | } |
||
548 | |||
549 | /** |
||
550 | * @return boolean |
||
551 | */ |
||
552 | public function getHidden() |
||
553 | { |
||
554 | return $this->hidden; |
||
555 | } |
||
556 | |||
557 | /** |
||
558 | * Set whether this property accepts multiple values or a single value. |
||
559 | * |
||
560 | * @param boolean $multiple The multiple flag. |
||
561 | * @return self |
||
562 | */ |
||
563 | public function setMultiple($multiple) |
||
564 | { |
||
565 | if (!is_bool($multiple)) { |
||
566 | if (is_array($multiple)) { |
||
567 | $this->setMultipleOptions($multiple); |
||
568 | } elseif (is_int($multiple)) { |
||
569 | $this->setMultipleOptions([ |
||
570 | 'min' => $multiple, |
||
571 | 'max' => $multiple |
||
572 | ]); |
||
573 | } |
||
574 | } |
||
575 | |||
576 | $this->multiple = !!$multiple; |
||
577 | |||
578 | return $this; |
||
579 | } |
||
580 | |||
581 | /** |
||
582 | * Determine if this property accepts multiple values or a single value. |
||
583 | * |
||
584 | * The multiple flag sets the property as being "repeatable", or allow to represent an array of multiple values. |
||
585 | * |
||
586 | * ## Notes |
||
587 | * - The multiple flag can be forced to false (or true) in implementing property class. |
||
588 | * - How a multiple behaves also depend on `multipleOptions`. |
||
589 | * |
||
590 | * @return boolean |
||
591 | */ |
||
592 | public function getMultiple() |
||
593 | { |
||
594 | return $this->multiple; |
||
595 | } |
||
596 | |||
597 | /** |
||
598 | * Set the multiple options / configuration, when property is `multiple`. |
||
599 | * |
||
600 | * ## Options structure |
||
601 | * - `separator` (string) The separator charactor. |
||
602 | * - `min` (integer) The minimum number of values. (0 = no limit). |
||
603 | * - `max` (integer) The maximum number of values. (0 = no limit). |
||
604 | * |
||
605 | * @param array $multipleOptions The property multiple options. |
||
606 | * @return self |
||
607 | */ |
||
608 | public function setMultipleOptions(array $multipleOptions) |
||
609 | { |
||
610 | // The options are always merged with the defaults, to ensure minimum required array structure. |
||
611 | $options = array_merge($this->defaultMultipleOptions(), $multipleOptions); |
||
612 | $this->multipleOptions = $options; |
||
613 | |||
614 | return $this; |
||
615 | } |
||
616 | |||
617 | /** |
||
618 | * The options defining the property behavior when the multiple flag is set to true. |
||
619 | * |
||
620 | * @see self::defaultMultipleOptions |
||
621 | * @param string|null $key Optional setting to retrieve from the options. |
||
622 | * @return array|mixed|null |
||
623 | */ |
||
624 | public function getMultipleOptions($key = null) |
||
625 | { |
||
626 | if ($this->multipleOptions === null) { |
||
627 | $this->multipleOptions = $this->defaultMultipleOptions(); |
||
628 | } |
||
629 | |||
630 | if (is_string($key)) { |
||
631 | if (isset($this->multipleOptions[$key])) { |
||
632 | return $this->multipleOptions[$key]; |
||
633 | } else { |
||
634 | return null; |
||
635 | } |
||
636 | } |
||
637 | |||
638 | return $this->multipleOptions; |
||
639 | } |
||
640 | |||
641 | /** |
||
642 | * Output the property multiple options as json. |
||
643 | * |
||
644 | * @return string |
||
645 | */ |
||
646 | public function multipleOptionsAsJson() |
||
647 | { |
||
648 | return json_encode($this->getMultipleOptions()); |
||
649 | } |
||
650 | |||
651 | /** |
||
652 | * Retrieve the default settings for a multi-value property. |
||
653 | * |
||
654 | * @return array |
||
655 | */ |
||
656 | public function defaultMultipleOptions() |
||
657 | { |
||
658 | return [ |
||
659 | 'separator' => ',', |
||
660 | 'min' => 0, |
||
661 | 'max' => 0 |
||
662 | ]; |
||
663 | } |
||
664 | |||
665 | /** |
||
666 | * Retrieve the value delimiter for a multi-value property. |
||
667 | * |
||
668 | * @return string |
||
669 | */ |
||
670 | public function multipleSeparator() |
||
671 | { |
||
672 | return $this->getMultipleOptions('separator'); |
||
673 | } |
||
674 | |||
675 | /** |
||
676 | * @param mixed $val A multi-value variable. |
||
677 | * @return array The array of values. |
||
678 | */ |
||
679 | public function parseValAsMultiple($val) |
||
680 | { |
||
681 | if (is_array($val)) { |
||
682 | return $val; |
||
683 | } |
||
684 | |||
685 | if ($val === null || $val === '') { |
||
686 | return []; |
||
687 | } |
||
688 | |||
689 | if (!is_string($val)) { |
||
690 | return (array)$val; |
||
691 | } |
||
692 | |||
693 | return explode($this->multipleSeparator(), $val); |
||
694 | } |
||
695 | |||
696 | /** |
||
697 | * @param boolean $allow The property allow null flag. |
||
698 | * @return self |
||
699 | */ |
||
700 | public function setAllowNull($allow) |
||
701 | { |
||
702 | $this->allowNull = !!$allow; |
||
703 | |||
704 | return $this; |
||
705 | } |
||
706 | |||
707 | /** |
||
708 | * The allow null flag sets the property as being able to be of a "null" value. |
||
709 | * |
||
710 | * ## Notes |
||
711 | * - This flag typically modifies the storage database to also allow null values. |
||
712 | * |
||
713 | * @return boolean |
||
714 | */ |
||
715 | public function getAllowNull() |
||
716 | { |
||
717 | return $this->allowNull; |
||
718 | } |
||
719 | |||
720 | /** |
||
721 | * @param boolean $required The property required flag. |
||
722 | * @return self |
||
723 | */ |
||
724 | public function setRequired($required) |
||
725 | { |
||
726 | $this->required = !!$required; |
||
727 | |||
728 | return $this; |
||
729 | } |
||
730 | |||
731 | /** |
||
732 | * Required flag sets the property as being required, meaning not allowed to be null / empty. |
||
733 | * |
||
734 | * ## Notes |
||
735 | * - The actual meaning of "required" might be different for implementing property class. |
||
736 | * |
||
737 | * @return boolean |
||
738 | */ |
||
739 | public function getRequired() |
||
740 | { |
||
741 | return $this->required; |
||
742 | } |
||
743 | |||
744 | /** |
||
745 | * @param boolean $unique The property unique flag. |
||
746 | * @return self |
||
747 | */ |
||
748 | public function setUnique($unique) |
||
749 | { |
||
750 | $this->unique = !!$unique; |
||
751 | |||
752 | return $this; |
||
753 | } |
||
754 | |||
755 | /** |
||
756 | * @return boolean |
||
757 | */ |
||
758 | public function getUnique() |
||
759 | { |
||
760 | return $this->unique; |
||
761 | } |
||
762 | |||
763 | /** |
||
764 | * @param boolean $active The property active flag. Inactive properties should have no effects. |
||
765 | * @return self |
||
766 | */ |
||
767 | public function setActive($active) |
||
768 | { |
||
769 | $this->active = !!$active; |
||
770 | |||
771 | return $this; |
||
772 | } |
||
773 | |||
774 | /** |
||
775 | * @return boolean |
||
776 | */ |
||
777 | public function getActive() |
||
778 | { |
||
779 | return $this->active; |
||
780 | } |
||
781 | |||
782 | /** |
||
783 | * Legacy support of active() instead of getActive(). |
||
784 | * |
||
785 | * @return string |
||
786 | */ |
||
787 | public function active() |
||
788 | { |
||
789 | return $this->getActive(); |
||
790 | } |
||
791 | |||
792 | /** |
||
793 | * @param boolean $validatable The validatable flag. |
||
794 | * @return self |
||
795 | */ |
||
796 | public function setValidatable($validatable) |
||
797 | { |
||
798 | $this->validatable = !!$validatable; |
||
799 | |||
800 | return $this; |
||
801 | } |
||
802 | |||
803 | /** |
||
804 | * @return boolean |
||
805 | */ |
||
806 | public function getValidatable() |
||
807 | { |
||
808 | return $this->validatable; |
||
809 | } |
||
810 | |||
811 | /** |
||
812 | * @param boolean $storable The storable flag. |
||
813 | * @return self |
||
814 | */ |
||
815 | public function setStorable($storable) |
||
816 | { |
||
817 | $this->storable = !!$storable; |
||
818 | |||
819 | return $this; |
||
820 | } |
||
821 | |||
822 | /** |
||
823 | * @return boolean |
||
824 | */ |
||
825 | public function getStorable() |
||
826 | { |
||
827 | return $this->storable; |
||
828 | } |
||
829 | |||
830 | /** |
||
831 | * @param mixed $description The property description. |
||
832 | * @return self |
||
833 | */ |
||
834 | public function setDescription($description) |
||
835 | { |
||
836 | $this->description = $this->translator()->translation($description); |
||
837 | return $this; |
||
838 | } |
||
839 | |||
840 | /** |
||
841 | * @return Translation|null |
||
842 | */ |
||
843 | public function getDescription() |
||
844 | { |
||
845 | return $this->description; |
||
846 | } |
||
847 | |||
848 | /** |
||
849 | * @param mixed $notes The property notes. |
||
850 | * @return self |
||
851 | */ |
||
852 | public function setNotes($notes) |
||
853 | { |
||
854 | $this->notes = $this->translator()->translation($notes); |
||
855 | return $this; |
||
856 | } |
||
857 | |||
858 | /** |
||
859 | * @return Translation|null |
||
860 | */ |
||
861 | public function getNotes() |
||
862 | { |
||
863 | return $this->notes; |
||
864 | } |
||
865 | |||
866 | /** |
||
867 | * The property's default validation methods. |
||
868 | * |
||
869 | * - `required` |
||
870 | * - `unique` |
||
871 | * - `allowNull` |
||
872 | * |
||
873 | * ## Notes |
||
874 | * - Those 3 base validation methods should always be merged, in implementing factory class. |
||
875 | * |
||
876 | * @return string[] |
||
877 | */ |
||
878 | public function validationMethods() |
||
879 | { |
||
880 | return [ |
||
881 | 'required', |
||
882 | 'unique', |
||
883 | 'allowNull', |
||
884 | ]; |
||
885 | } |
||
886 | |||
887 | /** |
||
888 | * @return boolean |
||
889 | */ |
||
890 | View Code Duplication | public function validateRequired() |
|
0 ignored issues
–
show
|
|||
891 | { |
||
892 | $val = $this->val(); |
||
0 ignored issues
–
show
|
|||
893 | if ($this['required'] && empty($val) && !is_numeric($val)) { |
||
894 | $this->validator()->error('Value is required.', 'required'); |
||
895 | |||
896 | return false; |
||
897 | } |
||
898 | |||
899 | return true; |
||
900 | } |
||
901 | |||
902 | /** |
||
903 | * @return boolean |
||
904 | */ |
||
905 | public function validateUnique() |
||
906 | { |
||
907 | if (!$this['unique']) { |
||
908 | return true; |
||
909 | } |
||
910 | |||
911 | /** @todo Check in the model's storage if the value already exists. */ |
||
912 | return true; |
||
913 | } |
||
914 | |||
915 | /** |
||
916 | * @return boolean |
||
917 | */ |
||
918 | public function validateAllowNull() |
||
919 | { |
||
920 | $val = $this->val(); |
||
0 ignored issues
–
show
|
|||
921 | if (!$this['allowNull'] && $val === null) { |
||
922 | $this->validator()->error('Value can not be null.', 'allowNull'); |
||
923 | |||
924 | return false; |
||
925 | } |
||
926 | |||
927 | return true; |
||
928 | } |
||
929 | |||
930 | /** |
||
931 | * @param mixed $val The value, at time of saving. |
||
932 | * @return mixed |
||
933 | */ |
||
934 | public function save($val) |
||
935 | { |
||
936 | // By default, nothing to do |
||
937 | return $this->parseVal($val); |
||
938 | } |
||
939 | |||
940 | /** |
||
941 | * @param string $type The display type. |
||
942 | * @return self |
||
943 | */ |
||
944 | public function setDisplayType($type) |
||
945 | { |
||
946 | $this->displayType = $type; |
||
947 | |||
948 | return $this; |
||
949 | } |
||
950 | |||
951 | /** |
||
952 | * @return string |
||
953 | */ |
||
954 | public function getDisplayType() |
||
955 | { |
||
956 | if (!$this->displayType) { |
||
957 | $meta = $this->metadata(); |
||
958 | |||
959 | // This default would be defined in type-property.json (@see charcoal-property/metadata) |
||
960 | if (isset($meta['admin']) && isset($meta['admin']['display_type'])) { |
||
961 | $default = $meta['admin']['display_type']; |
||
962 | } else { |
||
963 | $default = 'charcoal/admin/property/display/text'; |
||
964 | } |
||
965 | $this->setDisplayType($default); |
||
966 | } |
||
967 | |||
968 | return $this->displayType; |
||
969 | } |
||
970 | |||
971 | /** |
||
972 | * View options. |
||
973 | * @param string $ident The display ident (ex: charcoal/admin/property/display/text). |
||
974 | * @return array Should ALWAYS be an array. |
||
975 | */ |
||
976 | final public function viewOptions($ident = null) |
||
977 | { |
||
978 | // No options defined |
||
979 | if (!$this->viewOptions) { |
||
980 | return []; |
||
981 | } |
||
982 | |||
983 | // No ident defined |
||
984 | if (!$ident) { |
||
985 | return $this->viewOptions; |
||
986 | } |
||
987 | |||
988 | // Invalid ident |
||
989 | if (!isset($this->viewOptions[$ident])) { |
||
990 | return []; |
||
991 | } |
||
992 | |||
993 | // Success! |
||
994 | return $this->viewOptions[$ident]; |
||
995 | } |
||
996 | |||
997 | /** |
||
998 | * Set view options for both display and input |
||
999 | * |
||
1000 | * @param array $viewOpts View options. |
||
1001 | * @return self |
||
1002 | */ |
||
1003 | final public function setViewOptions(array $viewOpts = []) |
||
1004 | { |
||
1005 | $this->viewOptions = $viewOpts; |
||
1006 | |||
1007 | return $this; |
||
1008 | } |
||
1009 | |||
1010 | /** |
||
1011 | * @param Container $container A Pimple DI container. |
||
1012 | * @return void |
||
1013 | */ |
||
1014 | protected function setDependencies(Container $container) |
||
1015 | { |
||
1016 | $this->setPropertyFactory($container['property/factory']); |
||
1017 | $this->setMetadataLoader($container['metadata/loader']); |
||
1018 | } |
||
1019 | |||
1020 | /** |
||
1021 | * Attempt to get the multilingual value in the requested language. |
||
1022 | * |
||
1023 | * @param mixed $val The multilingual value to lookup. |
||
1024 | * @param mixed $lang The language to return the value in. |
||
1025 | * @return string|null |
||
1026 | */ |
||
1027 | protected function l10nVal($val, $lang = null) |
||
1028 | { |
||
1029 | if (!is_string($lang)) { |
||
1030 | if (is_array($lang) && isset($lang['lang'])) { |
||
1031 | $lang = $lang['lang']; |
||
1032 | } else { |
||
1033 | $lang = $this->translator()->getLocale(); |
||
1034 | } |
||
1035 | } |
||
1036 | |||
1037 | if (isset($val[$lang])) { |
||
1038 | return $val[$lang]; |
||
1039 | } else { |
||
1040 | return null; |
||
1041 | } |
||
1042 | } |
||
1043 | |||
1044 | /** |
||
1045 | * Create a new metadata object. |
||
1046 | * |
||
1047 | * @param array $data Optional metadata to merge on the object. |
||
1048 | * @see DescribableTrait::createMetadata() |
||
1049 | * @return PropertyMetadata |
||
1050 | */ |
||
1051 | protected function createMetadata(array $data = null) |
||
1052 | { |
||
1053 | $class = $this->metadataClass(); |
||
1054 | return new $class($data); |
||
1055 | } |
||
1056 | |||
1057 | /** |
||
1058 | * Retrieve the class name of the metadata object. |
||
1059 | * |
||
1060 | * @see DescribableTrait::metadataClass() |
||
1061 | * @return string |
||
1062 | */ |
||
1063 | protected function metadataClass() |
||
1064 | { |
||
1065 | return PropertyMetadata::class; |
||
1066 | } |
||
1067 | |||
1068 | /** |
||
1069 | * Create a Validator object |
||
1070 | * |
||
1071 | * @see ValidatableTrait::createValidator() |
||
1072 | * @return ValidatorInterface |
||
1073 | */ |
||
1074 | protected function createValidator() |
||
1075 | { |
||
1076 | $validator = new PropertyValidator($this); |
||
1077 | |||
1078 | return $validator; |
||
1079 | } |
||
1080 | |||
1081 | /** |
||
1082 | * @param PDO $pdo The database connection (PDO) instance. |
||
1083 | * @return void |
||
1084 | */ |
||
1085 | private function setPdo(PDO $pdo) |
||
1086 | { |
||
1087 | $this->pdo = $pdo; |
||
1088 | } |
||
1089 | } |
||
1090 |
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.