Passed
Push — master ( 34af4b...0f6000 )
by Alexander
02:23
created

ErrorSummary::getFormModel()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 7
ccs 4
cts 4
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Form\Widget;
6
7
use InvalidArgumentException;
8
use Yiisoft\Form\Exception\FormModelNotSetException;
9
use Yiisoft\Form\FormModelInterface;
10
use Yiisoft\Form\Helper\HtmlFormErrors;
11
use Yiisoft\Html\Html;
12
use Yiisoft\Html\Tag\CustomTag;
13
use Yiisoft\Html\Tag\P;
14
use Yiisoft\Html\Tag\Ul;
15
use Yiisoft\Widget\Widget;
16
17
use function array_flip;
18
use function array_intersect_key;
19
use function array_unique;
20
use function array_values;
21
22
/**
23
 * The error summary widget displays a summary of the errors in a form.
24
 */
25
final class ErrorSummary extends Widget
26
{
27
    private array $attributes = [];
28
    private bool $encode = true;
29
    private array $onlyAttributes = [];
30
    private ?FormModelInterface $formModel = null;
31
    private string $footer = '';
32
    private array $footerAttributes = [];
33
    private string $header = 'Please fix the following errors:';
34
    private array $headerAttributes = [];
35
    private bool $showAllErrors = false;
36
    /** @psalm-param non-empty-string */
37
    private string $tag = 'div';
38
39
    /**
40
     * The HTML attributes. The following special options are recognized.
41
     *
42
     * @param array $value
43
     *
44
     * @return self
45
     *
46
     * See {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
47
     */
48 5
    public function attributes(array $value): self
49
    {
50 5
        $new = clone $this;
51 5
        $new->attributes = $value;
52 5
        return $new;
53
    }
54
55
    /**
56
     * Whether content should be HTML-encoded.
57
     *
58
     * @param bool $value
59
     *
60
     * @return self
61
     */
62 1
    public function encode(bool $value): self
63
    {
64 1
        $new = clone $this;
65 1
        $new->encode = $value;
66 1
        return $new;
67
    }
68
69
    /**
70
     * Set the footer text for the error summary
71
     *
72
     * @param string $value
73
     *
74
     * @return self
75
     */
76 5
    public function footer(string $value): self
77
    {
78 5
        $new = clone $this;
79 5
        $new->footer = $value;
80 5
        return $new;
81
    }
82
83
    /**
84
     * Set footer attributes for the error summary.
85
     *
86
     * @param array $values Attribute values indexed by attribute names.
87
     *
88
     * @return self
89
     *
90
     * See {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
91
     */
92 4
    public function footerAttributes(array $values): self
93
    {
94 4
        $new = clone $this;
95 4
        $new->footerAttributes = $values;
96 4
        return $new;
97
    }
98
99
    /**
100
     * Set the header text for the error summary
101
     *
102
     * @param string $value
103
     *
104
     * @return self
105
     */
106 2
    public function header(string $value): self
107
    {
108 2
        $new = clone $this;
109 2
        $new->header = $value;
110 2
        return $new;
111
    }
112
113
    /**
114
     * Set header attributes for the error summary.
115
     *
116
     * @param array $values Attribute values indexed by attribute names.
117
     *
118
     * @return self
119
     *
120
     * See {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
121
     */
122 4
    public function headerAttributes(array $values): self
123
    {
124 4
        $new = clone $this;
125 4
        $new->headerAttributes = $values;
126 4
        return $new;
127
    }
128
129
    /**
130
     * Set the model for the error summary.
131
     *
132
     * @param FormModelInterface $formModel
133
     *
134
     * @return self
135
     */
136 7
    public function model(FormModelInterface $formModel): self
137
    {
138 7
        $new = clone $this;
139 7
        $new->formModel = $formModel;
140 7
        return $new;
141
    }
142
143
    /**
144
     * Whether to show all errors.
145
     *
146
     * @param bool $value
147
     *
148
     * @return self
149
     */
150 5
    public function showAllErrors(bool $value): self
151
    {
152 5
        $new = clone $this;
153 5
        $new->showAllErrors = $value;
154 5
        return $new;
155
    }
156
157
    /**
158
     * Specific attributes to be filtered out when rendering the error summary.
159
     *
160
     * @param array $values The attributes to be included in error summary.
161
     *
162
     * @return self
163
     */
164 5
    public function onlyAttributes(string ...$values): self
165
    {
166 5
        $new = clone $this;
167 5
        $new->onlyAttributes = $values;
168 5
        return $new;
169
    }
170
171
    /**
172
     * Set the container tag name for the error summary.
173
     *
174
     * Empty to render error messages without container {@see Html::tag()}.
175
     *
176
     * @param string $value
177
     *
178
     * @return self
179
     */
180 2
    public function tag(string $value): self
181
    {
182 2
        $new = clone $this;
183 2
        $new->tag = $value;
184 2
        return $new;
185
    }
186
187
    /**
188
     * Return array of the validation errors.
189
     *
190
     * @return array of the validation errors.
191
     */
192 6
    private function collectErrors(): array
193
    {
194 6
        $errors = HtmlFormErrors::getErrorSummaryFirstErrors($this->getFormModel());
195 6
        $errorMessages = [];
196
197 6
        if ($this->showAllErrors) {
198 3
            $errors = HtmlFormErrors::getErrorSummary($this->getFormModel(), $this->onlyAttributes);
199 3
        } elseif ($this->onlyAttributes !== []) {
200 1
            $errors = array_intersect_key($errors, array_flip($this->onlyAttributes));
201
        }
202
203
        /**
204
         * If there are the same error messages for different attributes, array_unique will leave gaps between
205
         * sequential keys. Applying array_values to reorder array keys.
206
         */
207 6
        $lines = array_values(array_unique($errors));
208
209 6
        if ($this->encode) {
210
            /** @var string $line */
211 6
            foreach ($lines as $line) {
212 5
                if (!empty($line)) {
213 5
                    $errorMessages[] = Html::encode($line);
214
                }
215
            }
216
        }
217
218 6
        return $errorMessages;
219
    }
220
221
    /**
222
     * Generates a summary of the validation errors.
223
     *
224
     * @return string the generated error summary
225
     */
226 7
    protected function run(): string
227
    {
228 7
        $attributes = $this->attributes;
229 7
        $content = '';
230
231 7
        if ($this->tag === '') {
232 1
            throw new InvalidArgumentException('Tag name cannot be empty.');
233
        }
234
235 6
        $content .=  P::tag()->attributes($this->headerAttributes)->content($this->header)->render() . PHP_EOL;
236
237
        /** @var array<string, string> */
238 6
        $lines = $this->collectErrors();
239 6
        $content .= Ul::tag()->strings($lines)->render();
240
241 6
        if ($this->footer !== '') {
242 1
            $content .= PHP_EOL . P::tag()->attributes($this->footerAttributes)->content($this->footer)->render();
243
        }
244
245 6
        return $lines !== []
246 5
            ? CustomTag::name($this->tag)
247 5
                ->attributes($attributes)
248 5
                ->encode(false)
249 5
                ->content(PHP_EOL . $content . PHP_EOL)
250 5
                ->render()
251 6
            : '';
252
    }
253
254
    /**
255
     * Return FormModelInterface object.
256
     *
257
     * @return FormModelInterface
258
     */
259 7
    private function getFormModel(): FormModelInterface
260
    {
261 7
        if ($this->formModel === null) {
262 1
            throw new FormModelNotSetException();
263
        }
264
265 6
        return $this->formModel;
266
    }
267
}
268