Passed
Branch master (3daac1)
by Vincent
07:53
created

FormError::__toString()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
namespace Bdf\Form\Error;
4
5
use Bdf\Form\Child\ChildInterface;
6
use Bdf\Form\ElementInterface;
7
use InvalidArgumentException;
8
use Symfony\Component\Validator\ConstraintViolation;
9
use Symfony\Component\Validator\ConstraintViolationInterface;
10
11
/**
12
 * Store errors of a form element
13
 *
14
 * @see ElementInterface::error()
15
 * @see ChildInterface::error()
16
 */
17
final class FormError
18
{
19
    /**
20
     * @var FormError|null
21
     */
22
    private static $null;
23
24
    /**
25
     * @var string|null
26
     */
27
    private $global;
28
29
    /**
30
     * @var string|null
31
     */
32
    private $code;
33
34
    /**
35
     * @var FormError[]
36
     */
37
    private $children;
38
39
40
    /**
41
     * FormError constructor.
42
     * Prefer use static methods to instantiate the FormError
43
     *
44
     * @param string|null $global
45
     * @param string|null $code
46
     * @param FormError[] $children
47
     */
48 163
    public function __construct(?string $global, ?string $code, array $children)
49
    {
50 163
        $this->global = $global;
51 163
        $this->code = $code;
52 163
        $this->children = $children;
53 163
    }
54
55
    /**
56
     * Get the error of the current element, or the global error on an aggregate element
57
     *
58
     * @return string|null The error message, or null if there is no errors
59
     */
60 140
    public function global(): ?string
61
    {
62 140
        return $this->global;
63
    }
64
65
    /**
66
     * The error code of the current element
67
     *
68
     * @return string|null The error code, or null if not provided
69
     */
70 21
    public function code(): ?string
71
    {
72 21
        return $this->code;
73
    }
74
75
    /**
76
     * Get the children's errors
77
     * The errors are indexed by the child's name
78
     * Contains only non-empty errors
79
     *
80
     * @return FormError[]
81
     */
82 4
    public function children(): array
83
    {
84 4
        return $this->children;
85
    }
86
87
    /**
88
     * Check if the error object is an empty one
89
     *
90
     * If true, there is no errors, neither on current elements, nor on children
91
     * An element returning an empty error is a valid one
92
     *
93
     * @return bool
94
     */
95 270
    public function empty(): bool
96
    {
97 270
        return empty($this->global) && empty($this->code) && empty($this->children);
98
    }
99
100
    /**
101
     * Export the errors into an array
102
     *
103
     * - The errors are indexed by the children's name
104
     * - If a child contains a "global" error, the error value will be the global error
105
     * - Else, call toArray() on the child's error
106
     * - If the current element contains a global error, return it at the int(0) index
107
     *
108
     * @return array
109
     */
110 11
    public function toArray(): array
111
    {
112 11
        $errors = [];
113
114 11
        if ($this->global) {
115 3
            $errors[0] = $this->global;
116
        }
117
118 11
        foreach ($this->children as $name => $child) {
119 9
            if ($child->global) {
120 9
                $errors[$name] = $child->global;
121
            } else {
122 1
                $errors[$name] = $child->toArray();
123
            }
124
        }
125
126 11
        return $errors;
127
    }
128
129
    /**
130
     * Format the error using the given printer
131
     *
132
     * @param FormErrorPrinterInterface $printer
133
     *
134
     * @return mixed The printer result
135
     */
136 18
    public function print(FormErrorPrinterInterface $printer)
137
    {
138 18
        if ($this->global) {
139 16
            $printer->global($this->global);
140
        }
141
142 18
        if ($this->code) {
143 14
            $printer->code($this->code);
144
        }
145
146 18
        foreach ($this->children as $name => $child) {
147 13
            $printer->child($name, $child);
148
        }
149
150 18
        return $printer->print();
151
    }
152
153
    /**
154
     * Format errors as string
155
     *
156
     * @return string
157
     */
158 4
    public function __toString(): string
159
    {
160 4
        return $this->print(new StringErrorPrinter());
161
    }
162
163
    /**
164
     * Get an empty error instance
165
     *
166
     * @return FormError
167
     */
168 517
    public static function null(): FormError
169
    {
170 517
        if (self::$null) {
171 517
            return self::$null;
172
        }
173
174 1
        return self::$null = new FormError(null, null, []);
175
    }
176
177
    /**
178
     * Creates an error containing only the global message
179
     *
180
     * @param string $message The error message
181
     * @param string|null $code The error code
182
     *
183
     * @return FormError
184
     */
185 161
    public static function message(string $message, ?string $code = null): FormError
186
    {
187 161
        return new FormError($message, $code, []);
188
    }
189
190
    /**
191
     * Creates an aggregation of children errors
192
     *
193
     * @param FormError[] $errors The children errors, indexed by the child name
194
     *
195
     * @return FormError
196
     */
197 33
    public static function aggregate(array $errors): FormError
198
    {
199 33
        return new FormError(null, null, $errors);
200
    }
201
202
    /**
203
     * Create a form error from a symfony violation
204
     *
205
     * @param ConstraintViolationInterface $violation The violation instance
206
     *
207
     * @return FormError
208
     */
209 138
    public static function violation(ConstraintViolationInterface $violation): FormError
210
    {
211 138
        $message = (string) $violation->getMessage();
212 138
        $code = $violation->getCode();
213
214 138
        if ($code !== null && $violation instanceof ConstraintViolation && ($constraint = $violation->getConstraint()) !== null) {
215
            try {
216 138
                $code = $constraint->getErrorName($code);
217 15
            } catch (InvalidArgumentException $e) {
218
                // Ignore error
219
            }
220
        }
221
222 138
        return self::message($message, $code);
223
    }
224
}
225