Passed
Pull Request — master (#160)
by Wilmer
10:36
created

ErrorSummary::run()   A

Complexity

Conditions 6
Paths 13

Size

Total Lines 29
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 6

Importance

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