Passed
Pull Request — master (#147)
by Wilmer
03:09 queued 51s
created

ErrorSummary   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 178
Duplicated Lines 0 %

Test Coverage

Coverage 98.21%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 55
c 1
b 0
f 0
dl 0
loc 178
ccs 55
cts 56
cp 0.9821
rs 10
wmc 16

9 Methods

Rating   Name   Duplication   Size   Complexity  
A tag() 0 9 2
A showAllErrors() 0 5 1
A header() 0 5 1
A model() 0 5 1
A attributes() 0 5 1
A run() 0 25 4
A footer() 0 5 1
A collectErrors() 0 26 4
A encode() 0 5 1
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\Widget\Widget;
13
14
use function array_merge;
15
use function array_unique;
16
use function array_values;
17
18
/**
19
 * The error summary widget displays a summary of the errors in a form.
20
 *
21
 * @psalm-suppress MissingConstructor
22
 */
23
final class ErrorSummary extends Widget
24
{
25
    private array $attributes = [];
26
    private bool $encode = true;
27
    private FormModelInterface $formModel;
28
    private string $footer = '';
29
    private string $header = '<p>' . 'Please fix the following errors:' . '</p>';
30
    private bool $showAllErrors = false;
31
    /** @psalm-param non-empty-string */
32
    private string $tag = 'div';
33
34
    /**
35
     * The HTML attributes. The following special options are recognized.
36
     *
37
     * @param array $value
38
     *
39
     * @return static
40
     *
41
     * See {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
42
     */
43 4
    public function attributes(array $value): self
44
    {
45 4
        $new = clone $this;
46 4
        $new->attributes = $value;
47 4
        return $new;
48
    }
49
50
    /**
51
     * Whether content should be HTML-encoded.
52
     *
53
     * @param bool $value
54
     */
55 1
    public function encode(bool $value): self
56
    {
57 1
        $new = clone $this;
58 1
        $new->encode = $value;
59 1
        return $new;
60
    }
61
62
    /**
63
     * Set the footer text for the error summary
64
     *
65
     * @param string $value
66
     */
67 4
    public function footer(string $value): self
68
    {
69 4
        $new = clone $this;
70 4
        $new->footer = $value;
71 4
        return $new;
72
    }
73
74
    /**
75
     * Set the header text for the error summary
76
     *
77
     * @param string $value
78
     */
79 2
    public function header(string $value): self
80
    {
81 2
        $new = clone $this;
82 2
        $new->header = $value;
83 2
        return $new;
84
    }
85
86
    /**
87
     * Set the model for the error summary.
88
     *
89
     * @param FormModelInterface $formModel
90
     *
91
     * @return static
92
     */
93 4
    public function model(FormModelInterface $formModel): self
94
    {
95 4
        $new = clone $this;
96 4
        $new->formModel = $formModel;
97 4
        return $new;
98
    }
99
100
    /**
101
     * Whether to show all errors.
102
     *
103
     * @param bool $value
104
     *
105
     * @return static
106
     */
107 4
    public function showAllErrors(bool $value): self
108
    {
109 4
        $new = clone $this;
110 4
        $new->showAllErrors = $value;
111 4
        return $new;
112
    }
113
114
    /**
115
     * Set the container tag name for the error summary.
116
     *
117
     * Empty to render error messages without container {@see Html::tag()}.
118
     *
119
     * @param string $value
120
     *
121
     * @return static
122
     */
123 2
    public function tag(string $value): self
124
    {
125 2
        if ($value === '') {
126 1
            throw new InvalidArgumentException('Tag name cannot be empty.');
127
        }
128
129 1
        $new = clone $this;
130 1
        $new->tag = $value;
131 1
        return $new;
132
    }
133
134
    /**
135
     * Return array of the validation errors.
136
     *
137
     * @param bool $encode if set to false then the error messages won't be encoded.
138
     * @param bool $showAllErrors if set to true every error message for each attribute will be shown otherwise only
139
     * the first error message for each attribute will be shown.
140
     *
141
     * @return array of the validation errors.
142
     */
143 3
    private function collectErrors(bool $encode, bool $showAllErrors): array
144
    {
145 3
        $new = clone $this;
146
147 3
        $lines = [];
148
149 3
        foreach ([$new->formModel] as $form) {
150 3
            $lines = array_unique(
151 3
                array_merge($lines, HtmlFormErrors::getErrorSummary($form, $showAllErrors))
152
            );
153
        }
154
155
        /**
156
         * If there are the same error messages for different attributes, array_unique will leave gaps between
157
         * sequential keys. Applying array_values to reorder array keys.
158
         */
159 3
        $lines = array_values($lines);
160
161 3
        if ($encode) {
162
            /** @var string $line */
163 3
            foreach ($lines as &$line) {
164 1
                $line = Html::encode($line);
165
            }
166
        }
167
168 3
        return $lines;
169
    }
170
171
    /**
172
     * Generates a summary of the validation errors.
173
     *
174
     * @return string the generated error summary
175
     */
176 3
    protected function run(): string
177
    {
178 3
        $new = clone $this;
179
180
        /** @var array<string, string> */
181 3
        $lines = $new->collectErrors($new->encode, $new->showAllErrors);
182
183 3
        if (empty($lines)) {
184
            /** still render the placeholder for client-side validation use */
185 2
            $content = '<ul></ul>';
186 2
            $new->attributes['style'] = isset($new->attributes['style'])
187 2
                ? rtrim((string)$new->attributes['style'], ';') . '; display:none' : 'display:none';
188
        } else {
189 1
            $content = '<ul><li>' . implode("</li>\n<li>", $lines) . '</li></ul>';
190
        }
191
192 3
        if ($new->tag === '') {
193
            throw new InvalidArgumentException('Tag name cannot be empty.');
194
        }
195
196 3
        return CustomTag::name($new->tag)
197 3
            ->attributes($new->attributes)
198 3
            ->encode(false)
199 3
            ->content($new->header . $content . $new->footer)
200 3
            ->render();
201
    }
202
}
203