Passed
Pull Request — master (#155)
by Wilmer
11:04
created

ErrorSummary::run()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 25
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 4

Importance

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