Passed
Pull Request — master (#148)
by Wilmer
12:08 queued 01:58
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\Html\Html;
10
use Yiisoft\Html\Tag\CustomTag;
11
use Yiisoft\Widget\Widget;
12
13
use function array_merge;
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 4
    public function model(FormModelInterface $formModel): self
93
    {
94 4
        $new = clone $this;
95 4
        $new->formModel = $formModel;
96 4
        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
        if ($value === '') {
125 1
            throw new InvalidArgumentException('Tag name cannot be empty.');
126
        }
127
128 1
        $new = clone $this;
129 1
        $new->tag = $value;
130 1
        return $new;
131
    }
132
133
    /**
134
     * Return array of the validation errors.
135
     *
136
     * @param bool $encode if set to false then the error messages won't be encoded.
137
     * @param bool $showAllErrors if set to true every error message for each attribute will be shown otherwise only
138
     * the first error message for each attribute will be shown.
139
     *
140
     * @return array of the validation errors.
141
     */
142 3
    private function collectErrors(bool $encode, bool $showAllErrors): array
143
    {
144 3
        $new = clone $this;
145
146 3
        $lines = [];
147
148 3
        foreach ([$new->formModel] as $form) {
149 3
            $lines = array_unique(array_merge($lines, $form->getErrorSummary($showAllErrors)));
150
        }
151
152
        /**
153
         * If there are the same error messages for different attributes, array_unique will leave gaps between
154
         * sequential keys. Applying array_values to reorder array keys.
155
         */
156 3
        $lines = array_values($lines);
157
158 3
        if ($encode) {
159
            /** @var string $line */
160 3
            foreach ($lines as &$line) {
161 1
                $line = Html::encode($line);
162
            }
163
        }
164
165 3
        return $lines;
166
    }
167
168
    /**
169
     * Generates a summary of the validation errors.
170
     *
171
     * @return string the generated error summary
172
     */
173 3
    protected function run(): string
174
    {
175 3
        $new = clone $this;
176
177
        /** @var array<string, string> */
178 3
        $lines = $new->collectErrors($new->encode, $new->showAllErrors);
179
180 3
        if (empty($lines)) {
181
            /** still render the placeholder for client-side validation use */
182 2
            $content = '<ul></ul>';
183 2
            $new->attributes['style'] = isset($new->attributes['style'])
184 2
                ? rtrim((string)$new->attributes['style'], ';') . '; display:none' : 'display:none';
185
        } else {
186 1
            $content = '<ul><li>' . implode("</li>\n<li>", $lines) . '</li></ul>';
187
        }
188
189 3
        if ($new->tag === '') {
190
            throw new InvalidArgumentException('Tag name cannot be empty.');
191
        }
192
193 3
        return CustomTag::name($new->tag)
194 3
            ->attributes($new->attributes)
195 3
            ->encode(false)
196 3
            ->content($new->header . $content . $new->footer)
197 3
            ->render();
198
    }
199
}
200