Passed
Push — master ( 60c4e3...4cf43a )
by Sergei
02:58
created

ErrorSummary::headerEncode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 5
ccs 4
cts 4
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Form\Field;
6
7
use InvalidArgumentException;
8
use Yiisoft\Form\Field\Base\BaseField;
9
use Yiisoft\Html\Html;
10
use Yiisoft\Html\Tag\CustomTag;
11
12
use function in_array;
13
14
/**
15
 * @psalm-type Errors = array<string,list<string>>
16
 */
17
final class ErrorSummary extends BaseField
18
{
19
    /**
20
     * @psalm-var Errors
21
     */
22
    private array $errors = [];
23
    private bool $encode = true;
24
    private bool $onlyFirst = false;
25
    private array $onlyProperties = [];
26
27
    private string $footer = '';
28
    private array $footerAttributes = [];
29
30
    /**
31
     * @var non-empty-string|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string|null at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string|null.
Loading history...
32
     */
33
    private ?string $headerTag = 'div';
34
    private string $header = '';
35
    private bool $headerEncode = true;
36
    private array $headerAttributes = [];
37
38
    private array $listAttributes = [];
39
40
    /**
41
     * @psalm-param Errors $errors
42
     */
43 19
    public function errors(array $errors): self
44
    {
45 19
        $new = clone $this;
46 19
        $new->errors = $errors;
47 19
        return $new;
48
    }
49
50
    /**
51
     * Whether error content should be HTML-encoded.
52
     */
53 1
    public function encode(bool $value): self
54
    {
55 1
        $new = clone $this;
56 1
        $new->encode = $value;
57 1
        return $new;
58
    }
59
60 2
    public function onlyFirst(bool $value = true): self
61
    {
62 2
        $new = clone $this;
63 2
        $new->onlyFirst = $value;
64 2
        return $new;
65
    }
66
67
    /**
68
     * Specific properties to be filtered out when rendering the error summary.
69
     *
70
     * @param array $names The property names to be included in error summary.
71
     */
72 2
    public function onlyProperties(string ...$names): self
73
    {
74 2
        $new = clone $this;
75 2
        $new->onlyProperties = $names;
76 2
        return $new;
77
    }
78
79
    /**
80
     * Use only common errors when rendering the error summary.
81
     */
82 2
    public function onlyCommonErrors(): self
83
    {
84 2
        $new = clone $this;
85 2
        $new->onlyProperties = [''];
86 2
        return $new;
87
    }
88
89
    /**
90
     * Set the footer text for the error summary
91
     */
92 2
    public function footer(string $value): self
93
    {
94 2
        $new = clone $this;
95 2
        $new->footer = $value;
96 2
        return $new;
97
    }
98
99
    /**
100
     * Set footer attributes for the error summary.
101
     *
102
     * @param array $values Attribute values indexed by attribute names.
103
     *
104
     * See {@see Html::renderTagAttributes} for details on how attributes are being rendered.
105
     */
106 2
    public function footerAttributes(array $values): self
107
    {
108 2
        $new = clone $this;
109 2
        $new->footerAttributes = $values;
110 2
        return $new;
111
    }
112
113
    /**
114
     * Set the header text for the error summary
115
     */
116 7
    public function header(string $value): self
117
    {
118 7
        $new = clone $this;
119 7
        $new->header = $value;
120 7
        return $new;
121
    }
122
123
    /**
124
     * Set the header tag name.
125
     *
126
     * @param string|null $tag Header tag name.
127
     */
128 5
    public function headerTag(?string $tag): self
129
    {
130 5
        if ($tag === '') {
131 1
            throw new InvalidArgumentException('Tag name cannot be empty.');
132
        }
133
134 4
        $new = clone $this;
135 4
        $new->headerTag = $tag;
136 4
        return $new;
137
    }
138
139
    /**
140
     * Whether header content should be HTML-encoded.
141
     */
142 2
    public function headerEncode(bool $encode): self
143
    {
144 2
        $new = clone $this;
145 2
        $new->headerEncode = $encode;
146 2
        return $new;
147
    }
148
149
    /**
150
     * Set header attributes for the error summary.
151
     *
152
     * @param array $values Attribute values indexed by attribute names.
153
     *
154
     * See {@see Html::renderTagAttributes} for details on how attributes are being rendered.
155
     */
156 2
    public function headerAttributes(array $values): self
157
    {
158 2
        $new = clone $this;
159 2
        $new->headerAttributes = $values;
160 2
        return $new;
161
    }
162
163
    /**
164
     * Set errors list container attributes.
165
     *
166
     * @param array $attributes Attribute values indexed by attribute names.
167
     *
168
     * See {@see Html::renderTagAttributes} for details on how attributes are being rendered.
169
     */
170 2
    public function listAttributes(array $attributes): self
171
    {
172 2
        $new = clone $this;
173 2
        $new->listAttributes = $attributes;
174 2
        return $new;
175
    }
176
177
    /**
178
     * Add one or more CSS classes to the list container tag.
179
     *
180
     * @param string|null ...$class One or many CSS classes.
181
     */
182 2
    public function addListClass(?string ...$class): self
183
    {
184 2
        $new = clone $this;
185 2
        Html::addCssClass($new->listAttributes, $class);
186 2
        return $new;
187
    }
188
189
    /**
190
     * Replace current list container tag CSS classes with a new set of classes.
191
     *
192
     * @param string|null ...$class One or many CSS classes.
193
     */
194 3
    public function listClass(?string ...$class): self
195
    {
196 3
        $new = clone $this;
197 3
        $new->listAttributes['class'] = $class;
198 3
        return $new;
199
    }
200
201 18
    protected function generateContent(): ?string
202
    {
203 18
        $errors = $this->filterErrors();
204 18
        if (empty($errors)) {
205 1
            return null;
206
        }
207
208 17
        $content = [];
209
210 17
        if ($this->header !== '') {
211 6
            $content[] = $this->headerTag === null
212 2
                ? ($this->headerEncode ? Html::encode($this->header) : $this->header)
213 4
                : CustomTag::name($this->headerTag)
214 4
                    ->attributes($this->headerAttributes)
215 4
                    ->content($this->header)
216 4
                    ->encode($this->headerEncode)
217 4
                    ->render();
218
        }
219
220 17
        $content[] = Html::ul()
221 17
            ->attributes($this->listAttributes)
222 17
            ->strings($errors, [], $this->encode)
223 17
            ->render();
224
225 17
        if ($this->footer !== '') {
226 1
            $content[] = Html::div($this->footer, $this->footerAttributes)->render();
227
        }
228
229 17
        return implode("\n", $content);
230
    }
231
232
    /**
233
     * Return array of the validation errors.
234
     *
235
     * @return string[] Array of the validation errors.
236
     */
237 18
    private function filterErrors(): array
238
    {
239 18
        if (empty($this->errors)) {
240 1
            return [];
241
        }
242
243 17
        $errors = $this->errors;
244
245 17
        if (!empty($this->onlyProperties)) {
246 2
            $errors = array_filter(
247 2
                $errors,
248 2
                fn(string $property) => in_array($property, $this->onlyProperties, true),
249 2
                ARRAY_FILTER_USE_KEY,
250 2
            );
251
        }
252
253 17
        if ($this->onlyFirst) {
254 1
            $errors = array_map(
255 1
                static fn(array $propertyErrors) => array_slice($propertyErrors, 0, 1),
256 1
                $errors,
257 1
            );
258
        }
259
260 17
        $result = [];
261 17
        foreach ($errors as $propertyErrors) {
262 17
            $result = [...$result, ...$propertyErrors];
263
        }
264
265
        /**
266
         * If there are the same error messages for different properties, `array_unique` will leave gaps between
267
         * sequential keys.
268
         */
269 17
        return array_unique($result);
270
    }
271
}
272