Passed
Pull Request — master (#275)
by Sergei
03:07
created

FormModelInputData::getValidationRules()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 7
ccs 5
cts 5
cp 1
crap 2
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Form\InputData\FormModel;
6
7
use InvalidArgumentException;
8
use Yiisoft\Form\Exception\PropertyNotSupportNestedValuesException;
9
use Yiisoft\Form\Exception\StaticObjectPropertyException;
10
use Yiisoft\Form\Exception\UndefinedObjectPropertyException;
11
use Yiisoft\Form\Exception\ValueNotFoundException;
12
use Yiisoft\Form\Field\Base\InputDataInterface;
13
use Yiisoft\Form\FormModelInterface;
14
use Yiisoft\Validator\Helper\RulesNormalizer;
15
16
/**
17
 * @psalm-import-type NormalizedRulesList from RulesNormalizer
18
 */
19
final class FormModelInputData implements InputDataInterface
20
{
21
    /**
22
     * @psalm-var NormalizedRulesList|null
23
     */
24
    private ?iterable $validationRules = null;
25
26 495
    public function __construct(
27
        private FormModelInterface $model,
28
        private string $property,
29
    ) {
30 495
    }
31
32 44
    public function getValidationRules(): iterable
33
    {
34 44
        if ($this->validationRules === null) {
35 44
            $rules = RulesNormalizer::normalize(null, $this->model);
36 44
            $this->validationRules = $rules[$this->property] ?? [];
37
        }
38 44
        return $this->validationRules;
39
    }
40
41
    /**
42
     * Generates an appropriate input name.
43
     *
44
     * This method generates a name that can be used as the input name to collect user input. The name is generated
45
     * according to the of the form and the property name. For example, if the form name of the `Post` form is `Post`,
46
     * then the input name generated for the `content` property would be `Post[content]`.
47
     *
48
     * See {@see getPropertyName()} for explanation of attribute expression.
49
     *
50
     * @throws InvalidArgumentException If the attribute name contains non-word characters or empty form name for
51
     * tabular inputs.
52
     * @return string The generated input name.
53
     */
54 411
    public function getName(): string
55
    {
56 411
        $data = $this->parseProperty($this->property);
57 411
        $formName = $this->model->getFormName();
58
59 411
        if ($formName === '' && $data['prefix'] === '') {
60
            return $this->property;
61
        }
62
63 411
        if ($formName !== '') {
64 411
            return "$formName{$data['prefix']}[{$data['name']}]{$data['suffix']}";
65
        }
66
67
        throw new InvalidArgumentException('formName() cannot be empty for tabular inputs.');
68
    }
69
70
    /**
71
     * @throws UndefinedObjectPropertyException
72
     * @throws StaticObjectPropertyException
73
     * @throws PropertyNotSupportNestedValuesException
74
     * @throws ValueNotFoundException
75
     */
76 379
    public function getValue(): mixed
77
    {
78 379
        $parsedName = $this->parseProperty($this->property);
79 379
        return $this->model->getAttributeValue($parsedName['name'] . $parsedName['suffix']);
80
    }
81
82 210
    public function getLabel(): ?string
83
    {
84 210
        return $this->model->getAttributeLabel($this->getPropertyName());
85
    }
86
87 407
    public function getHint(): ?string
88
    {
89 407
        return $this->model->getAttributeHint($this->getPropertyName());
90
    }
91
92 180
    public function getPlaceholder(): ?string
93
    {
94 180
        $placeholder = $this->model->getAttributePlaceholder($this->getPropertyName());
95 180
        return $placeholder === '' ? null : $placeholder;
96
    }
97
98
    /**
99
     * Generates an appropriate input ID.
100
     *
101
     * This method converts the result {@see getName()} into a valid input ID.
102
     *
103
     * For example, if {@see getInputName()} returns `Post[content]`, this method will return `post-content`.
104
     *
105
     * @throws InvalidArgumentException If the attribute name contains non-word characters.
106
     * @return string The generated input ID.
107
     */
108 365
    public function getId(): string
109
    {
110 365
        $name = mb_strtolower($this->getName(), 'UTF-8');
111 365
        return str_replace(['[]', '][', '[', ']', ' ', '.'], ['', '-', '-', '', '-', '-'], $name);
112
    }
113
114 335
    public function isValidated(): bool
115
    {
116 335
        return $this->model->getValidationResult() !== null;
117
    }
118
119 409
    public function getValidationErrors(): array
120
    {
121
        /** @psalm-var list<string> */
122 409
        return $this->model
123 409
            ->getValidationResult()
124 409
            ?->getAttributeErrorMessages($this->getPropertyName())
125 409
            ?? [];
126
    }
127
128 467
    private function getPropertyName(): string
129
    {
130 467
        $property = $this->parseProperty($this->property)['name'];
131
132 467
        if (!$this->model->hasAttribute($property)) {
133
            throw new InvalidArgumentException('Property "' . $property . '" does not exist.');
134
        }
135
136 467
        return $property;
137
    }
138
139
    /**
140
     * This method parses a property expression and returns an associative array containing
141
     * real property name, prefix and suffix.
142
     * For example: `['name' => 'content', 'prefix' => '', 'suffix' => '[0]']`
143
     *
144
     * A property expression is a property name prefixed and/or suffixed with array indexes. It is mainly used in
145
     * tabular data input and/or input of array type. Below are some examples:
146
     *
147
     * - `[0]content` is used in tabular data input to represent the "content" property for the first model in tabular
148
     *    input;
149
     * - `dates[0]` represents the first array element of the "dates" property;
150
     * - `[0]dates[0]` represents the first array element of the "dates" property for the first model in tabular
151
     *    input.
152
     *
153
     * @param string $property The property name or expression
154
     *
155
     * @throws InvalidArgumentException If the property name contains non-word characters.
156
     * @return string[] The property name, prefix and suffix.
157
     */
158 486
    private function parseProperty(string $property): array
159
    {
160 486
        if (!preg_match('/(^|.*\])([\w\.\+\-_]+)(\[.*|$)/u', $property, $matches)) {
161
            throw new InvalidArgumentException('Property name must contain word characters only.');
162
        }
163 486
        return [
164 486
            'name' => $matches[2],
165 486
            'prefix' => $matches[1],
166 486
            'suffix' => $matches[3],
167 486
        ];
168
    }
169
}
170