Passed
Pull Request — master (#170)
by Wilmer
02:44
created

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