Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
42 / 42
100.00% covered (success)
100.00%
5 / 5
CRAP
100.00% covered (success)
100.00%
1 / 1
Youtube
100.00% covered (success)
100.00%
42 / 42
100.00% covered (success)
100.00%
5 / 5
11
100.00% covered (success)
100.00%
1 / 1
 control
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 metaControl
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
1
 value
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 structure
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 shape
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
1 / 1
7
1<?php
2
3declare(strict_types=1);
4
5namespace Cosray\Field;
6
7use Celema\Sire\Shape;
8use Cosray\Validation\Shapes;
9use Cosray\Value\Youtube as YoutubeValue;
10use Override;
11
12class Youtube extends Field implements Capability\Translatable, Capability\Limitable, Capability\Placeholdable
13{
14    use Capability\IsTranslatable;
15    use Capability\IsLimitable;
16    use Capability\IsPlaceholdable;
17
18    /** A video id, not a URL: the value is spliced into the embed `src`. */
19    private const string ID_RULE = 'regex:/^[A-Za-z0-9_-]{1,64}$/';
20
21    #[Override]
22    public function control(): Control
23    {
24        return Control::youtube();
25    }
26
27    #[Override]
28    public function metaControl(): ?Control
29    {
30        return Control::group([
31            [
32                'key' => 'aspectRatioX',
33                'label' => __('youtube:aspect-ratio-x'),
34                'control' => Control::number(step: 1, min: 1),
35                'width' => 50,
36            ],
37            [
38                'key' => 'aspectRatioY',
39                'label' => __('youtube:aspect-ratio-y'),
40                'control' => Control::number(step: 1, min: 1),
41                'width' => 50,
42            ],
43        ]);
44    }
45
46    #[Override]
47    public function value(): YoutubeValue
48    {
49        return new YoutubeValue($this->owner, $this, $this->valueContext);
50    }
51
52    #[Override]
53    public function structure(mixed $value = null): array
54    {
55        $result = $this->getTranslatableStructure('youtube', $value);
56        $result['meta']['aspectRatioX'] = [self::NEUTRAL_LOCALE => 16];
57        $result['meta']['aspectRatioY'] = [self::NEUTRAL_LOCALE => 9];
58
59        return $result;
60    }
61
62    #[Override]
63    public function shape(): Shape
64    {
65        $shape = Shapes::create();
66        $this->addType($shape);
67
68        if ($this->isTranslatable()) {
69            $locales = $this->owner->locales();
70            $defaultLocale = $locales->getDefault()->id;
71            $i18nShape = Shapes::create();
72
73            foreach ($locales as $locale) {
74                $localeValidators = [];
75
76                if ($this->isRequired() && $locale->id === $defaultLocale) {
77                    $localeValidators[] = 'required';
78                }
79
80                $localeField = $i18nShape
81                    ->add($locale->id, 'string')
82                    ->label($this->valueLabel($locale))
83                    ->rules(self::ID_RULE, ...$localeValidators);
84
85                if (!in_array('required', $localeValidators, true)) {
86                    $localeField->optional()->nullable();
87                }
88            }
89
90            $value = $shape->add('value', $i18nShape)->rules(...$this->validators);
91        } else {
92            $value = $shape->add('value', $this->zxxShape('string', [self::ID_RULE, ...$this->validators]));
93        }
94
95        if (!$this->isRequired()) {
96            $value->optional()->nullable();
97        }
98
99        $this->addMeta($shape);
100
101        return $shape;
102    }
103}