Passed
Pull Request — master (#147)
by Wilmer
03:01 queued 19s
created

HtmlForm::getFirstError()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Form\Helper;
6
7
use InvalidArgumentException;
8
use Stringable;
9
use UnexpectedValueException;
10
use Yiisoft\Form\FormModelInterface;
11
12
/**
13
 * Form-related HTML tag generation
14
 */
15
final class HtmlForm
16
{
17
    /**
18
     * Return the attribute hint for the model.
19
     *
20
     * @param FormModelInterface $formModel the form object.
21
     * @param string $attribute the attribute name or expression.
22
     *
23
     * @return string
24
     */
25 147
    public static function getAttributeHint(FormModelInterface $formModel, string $attribute): string
26
    {
27 147
        return $formModel->getAttributeHint(self::getAttributeName($formModel, $attribute));
28
    }
29
30
    /**
31
     * Returns the label of the specified attribute name.
32
     *
33
     * @param FormModelInterface $formModel the form object.
34
     * @param string $attribute the attribute name or expression.
35
     *
36
     * @throws InvalidArgumentException if the attribute name contains non-word characters.
37
     *
38
     * @return string
39
     */
40 154
    public static function getAttributeLabel(FormModelInterface $formModel, string $attribute): string
41
    {
42 154
        return $formModel->getAttributeLabel(self::getAttributeName($formModel, $attribute));
43
    }
44
45
    /**
46
     * Returns the real attribute name from the given attribute expression.
47
     * If `$attribute` has neither prefix nor suffix, it will be returned without change.
48
     *
49
     * @param FormModelInterface $formModel the form object.
50
     * @param string $attribute the attribute name or expression
51
     *
52
     * @throws InvalidArgumentException if the attribute name contains non-word characters.
53
     *
54
     * @return string the attribute name without prefix and suffix.
55
     *
56
     * @see static::parseAttribute()
57
     */
58 311
    public static function getAttributeName(FormModelInterface $formModel, string $attribute): string
59
    {
60 311
        $attribute = self::parseAttribute($attribute)['name'];
61
62 310
        if (!$formModel->hasAttribute($attribute)) {
63 1
            throw new invalidArgumentException("Attribute '$attribute' does not exist.");
64
        }
65
66 309
        return $attribute;
67
    }
68
69
    /**
70
     * Returns the value of the specified attribute name or expression.
71
     *
72
     * For an attribute expression like `[0]dates[0]`, this method will return the value of `$form->dates[0]`.
73
     * See {@see getAttributeName()} for more details about attribute expression.
74
     *
75
     * If an attribute value an array of such instances, the primary value(s) of the AR instance(s) will be returned
76
     * instead.
77
     *
78
     * @param FormModelInterface $formModel the form object.
79
     * @param string $attribute the attribute name or expression.
80
     *
81
     * @throws InvalidArgumentException if the attribute name contains non-word characters.
82
     *
83
     * @return iterable|object|scalar|Stringable|null the corresponding attribute value.
84
     */
85 285
    public static function getAttributeValue(FormModelInterface $formModel, string $attribute)
86
    {
87 285
        return $formModel->getAttributeValue(self::getAttributeName($formModel, $attribute));
88
    }
89
90
    /**
91
     * Generates an appropriate input ID for the specified attribute name or expression.
92
     *
93
     * This method converts the result {@see getInputName()} into a valid input ID.
94
     *
95
     * For example, if {@see getInputName()} returns `Post[content]`, this method will return `post-content`.
96
     *
97
     * @param FormModelInterface $formModel the form object
98
     * @param string $attribute the attribute name or expression. See {@see getAttributeName()} for explanation of
99
     * attribute expression.
100
     * @param string $charset default `UTF-8`.
101
     *
102
     * @throws InvalidArgumentException if the attribute name contains non-word characters.
103
     * @throws UnexpectedValueException if charset is unknown
104
     *
105
     * @return string the generated input ID.
106
     */
107 266
    public static function getInputId(
108
        FormModelInterface $formModel,
109
        string $attribute,
110
        string $charset = 'UTF-8'
111
    ): string {
112 266
        $name = mb_strtolower(self::getInputName($formModel, $attribute), $charset);
113 266
        return str_replace(['[]', '][', '[', ']', ' ', '.'], ['', '-', '-', '', '-', '-'], $name);
114
    }
115
116
    /**
117
     * Generates an appropriate input name for the specified attribute name or expression.
118
     *
119
     * This method generates a name that can be used as the input name to collect user input for the specified
120
     * attribute. The name is generated according to the of the form and the given attribute name. For example, if the
121
     * form name of the `Post` form is `Post`, then the input name generated for the `content` attribute would be
122
     * `Post[content]`.
123
     *
124
     * See {@see getAttributeName()} for explanation of attribute expression.
125
     *
126
     * @param FormModelInterface $formModel the form object.
127
     * @param string $attribute the attribute name or expression.
128
     *
129
     * @throws InvalidArgumentException if the attribute name contains non-word characters
130
     * or empty form name for tabular inputs
131
     *
132
     * @return string the generated input name.
133
     */
134 281
    public static function getInputName(FormModelInterface $formModel, string $attribute): string
135
    {
136 281
        $data = self::parseAttribute($attribute);
137 281
        $formName = $formModel->getFormName();
138
139 281
        if ($formName === '' && $data['prefix'] === '') {
140 2
            return $attribute;
141
        }
142
143 279
        if ($formName !== '') {
144 278
            return "{$formName}{$data['prefix']}[{$data['name']}]{$data['suffix']}";
145
        }
146
147 1
        throw new InvalidArgumentException('formName() cannot be empty for tabular inputs.');
148
    }
149
150
    /**
151
     * This method parses an attribute expression and returns an associative array containing
152
     * real attribute name, prefix and suffix.
153
     * For example: `['name' => 'content', 'prefix' => '', 'suffix' => '[0]']`
154
     *
155
     * An attribute expression is an attribute name prefixed and/or suffixed with array indexes. It is mainly used in
156
     * tabular data input and/or input of array type. Below are some examples:
157
     *
158
     * - `[0]content` is used in tabular data input to represent the "content" attribute for the first model in tabular
159
     *    input;
160
     * - `dates[0]` represents the first array element of the "dates" attribute;
161
     * - `[0]dates[0]` represents the first array element of the "dates" attribute for the first model in tabular
162
     *    input.
163
     *
164
     * @param string $attribute the attribute name or expression
165
     *
166
     * @throws InvalidArgumentException if the attribute name contains non-word characters.
167
     *
168
     * @return string[] the attribute name, prefix and suffix.
169
     */
170 326
    private static function parseAttribute(string $attribute): array
171
    {
172 326
        if (!preg_match('/(^|.*\])([\w\.\+]+)(\[.*|$)/u', $attribute, $matches)) {
173 1
            throw new InvalidArgumentException('Attribute name must contain word characters only.');
174
        }
175
        return [
176 325
            'name' => $matches[2],
177 325
            'prefix' => $matches[1],
178 325
            'suffix' => $matches[3],
179
        ];
180
    }
181
}
182