Passed
Push — master ( c15889...08ecf6 )
by Sergei
03:03
created

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