Passed
Push — master ( a265ce...b92c2f )
by Sergei
04:01 queued 01:03
created

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