Passed
Pull Request — master (#160)
by Wilmer
02:47
created

ErrorSummary::run()   A

Complexity

Conditions 5
Paths 9

Size

Total Lines 30
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 5

Importance

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