Passed
Push — master ( 355586...94527c )
by Vincent
04:50
created

ValidatorBuilderTrait   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 227
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 17
eloc 36
c 1
b 0
f 0
dl 0
loc 227
ccs 42
cts 42
cp 1
rs 10

10 Methods

Rating   Name   Duplication   Size   Complexity  
A ignoreTransformerException() 0 5 1
A getTransformerExceptionConstraint() 0 7 2
A transformerErrorMessage() 0 5 1
A transformerErrorCode() 0 5 1
A required() 0 11 3
A defaultTransformerExceptionConstraintOptions() 0 3 1
A addConstraintsProvider() 0 3 1
A satisfy() 0 13 3
A buildValidator() 0 14 3
A transformerExceptionValidation() 0 5 1
1
<?php
2
3
namespace Bdf\Form\Util;
4
5
use Bdf\Form\ElementBuilderInterface;
6
use Bdf\Form\Registry\RegistryInterface;
7
use Bdf\Form\Validator\ConstraintValueValidator;
8
use Bdf\Form\Validator\TransformerExceptionConstraint;
9
use Bdf\Form\Validator\ValueValidatorInterface;
10
use Symfony\Component\Validator\Constraint;
11
use Symfony\Component\Validator\Constraints\NotBlank;
12
13
/**
14
 * Trait for implements build of constraint validator
15
 *
16
 * @psalm-require-implements \Bdf\Form\ElementBuilderInterface
17
 */
18
trait ValidatorBuilderTrait
19
{
20
    /**
21
     * @var array<Constraint|string|array>
22
     */
23
    private $constraints = [];
24
25
    /**
26
     * @var TransformerExceptionConstraint|null
27
     */
28
    private $transformerExceptionConstraint;
29
30
    /**
31
     * @var callable[]
32
     */
33
    private $constraintsProviders = [];
34
35
    /**
36
     * Mark this input as required
37
     * Calling this method is equivalent as calling `satisfy(new NotBlank($options))`
38
     *
39
     * Note: The constraint is not prepend, but simply added at the end of constraints.
40
     *       To stop validation process if the value is empty, this method must be called before all other `satisfy()`.
41
     *
42
     * Usage:
43
     * <code>
44
     * $builder->required(); // Mark as required, using default message
45
     * $builder->required('This field is required'); // With custom message
46
     * $builder->required(['allowNull' => true]); // With custom options
47
     * </code>
48
     *
49
     * @param array|string|null $options The constraint option. Is a string is given, it will be used as error message
50
     *
51
     * @return $this
52
     *
53
     * @see NotBlank The used constraint
54
     */
55 65
    final public function required($options = null)
56
    {
57 65
        if (!$options instanceof Constraint) {
58 59
            if (is_string($options)) {
59 9
                $options = ['message' => $options];
60
            }
61
62 59
            $options = new NotBlank($options);
0 ignored issues
show
Bug introduced by
It seems like $options can also be of type string; however, parameter $options of Symfony\Component\Valida...NotBlank::__construct() does only seem to accept array|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

62
            $options = new NotBlank(/** @scrutinizer ignore-type */ $options);
Loading history...
63
        }
64
65 65
        return $this->satisfy($options);
66
    }
67
68
    /**
69
     * {@inheritdoc}
70
     *
71
     * @see ElementBuilderInterface::satisfy()
72
     */
73 179
    final public function satisfy($constraint, $options = null, bool $append = true)
74
    {
75 179
        if ($options !== null) {
76 2
            $constraint = [$constraint, $options];
77
        }
78
79 179
        if ($append === true) {
80 179
            $this->constraints[] = $constraint;
81
        } else {
82 7
            array_unshift($this->constraints, $constraint);
83
        }
84
85 179
        return $this;
86
    }
87
88
    /**
89
     * Ignore the transformer exception
90
     * If true, when a transformation fails, the error will be ignored, and the standard validation process will be performed
91
     *
92
     * @param bool $flag true to ignore
93
     *
94
     * @return $this
95
     *
96
     * @see TransformerExceptionConstraint::$ignoreException
97
     */
98 1
    final public function ignoreTransformerException(bool $flag = true)
99
    {
100 1
        $this->getTransformerExceptionConstraint()->ignoreException = $flag;
101
102 1
        return $this;
103
    }
104
105
    /**
106
     * Define the error message to show when the transformer raise an exception
107
     *
108
     * @param string $message The error message
109
     * @return $this
110
     *
111
     * @see TransformerExceptionConstraint::$message
112
     */
113 1
    final public function transformerErrorMessage(string $message)
114
    {
115 1
        $this->getTransformerExceptionConstraint()->message = $message;
116
117 1
        return $this;
118
    }
119
120
    /**
121
     * Define the error code of the transformer error
122
     *
123
     * @param string $code The error code
124
     * @return $this
125
     *
126
     * @see TransformerExceptionConstraint::$code
127
     */
128 1
    final public function transformerErrorCode(string $code)
129
    {
130 1
        $this->getTransformerExceptionConstraint()->code = $code;
131
132 1
        return $this;
133
    }
134
135
    /**
136
     * Define custom transformer exception validation callback
137
     * Allow to define an error message and code corresponding to the exception, or ignore the exception
138
     *
139
     * The callback takes as parameters :
140
     * 1. The raw HTTP value
141
     * 2. The transformer exception constraint, as in-out parameter for get the exception and set message and code
142
     * 3. The form element
143
     *
144
     * The return value should return false to ignore the error, or true to add the error
145
     *
146
     * <code>
147
     * $builder->integer('value')->transformerExceptionValidation(function ($value, TransformerExceptionConstraint $constraint, ElementInterface $element) {
148
     *     if ($constraint->exception instanceof MyException) {
149
     *         // Define the error message and code
150
     *         $constraint->message = 'My error';
151
     *         $constraint->code = 'MY_ERROR';
152
     *
153
     *         return true;
154
     *     }
155
     *
156
     *     // Ignore the exception
157
     *     return false;
158
     * });
159
     * </code>
160
     *
161
     * @param callable(mixed,\Bdf\Form\Validator\TransformerExceptionConstraint,\Bdf\Form\ElementInterface):bool $validationCallback
162
     * @return $this
163
     *
164
     * @see TransformerExceptionConstraint::$code
165
     */
166 1
    final public function transformerExceptionValidation(callable $validationCallback)
167
    {
168 1
        $this->getTransformerExceptionConstraint()->validationCallback = $validationCallback;
169
170 1
        return $this;
171
    }
172
173
    /**
174
     * Register a new constraints provider
175
     * Constrains providers are call when building validator
176
     * It should return an array of constraints
177
     *
178
     * <code>
179
     * $builder->addConstraintsProvider(function(RegistryInterface $registry) {
180
     *     return [
181
     *         new FooConstraint($this->fooValue),
182
     *         new BarConstraint($this->barValue),
183
     *     ];
184
     * });
185
     * </code>
186
     *
187
     * @param callable(RegistryInterface):Constraint[] $constraintsProvider
188
     */
189 41
    final protected function addConstraintsProvider(callable $constraintsProvider): void
190
    {
191 41
        $this->constraintsProviders[] = $constraintsProvider;
192 41
    }
193
194
    /**
195
     * Get or create the transformer exception constraint
196
     *
197
     * @return TransformerExceptionConstraint
198
     */
199 343
    final private function getTransformerExceptionConstraint(): TransformerExceptionConstraint
200
    {
201 343
        if ($this->transformerExceptionConstraint) {
202 15
            return $this->transformerExceptionConstraint;
203
        }
204
205 343
        return $this->transformerExceptionConstraint = new TransformerExceptionConstraint($this->defaultTransformerExceptionConstraintOptions());
206
    }
207
208
    /**
209
     * Define the default constraints options for the TransformerExceptionConstraint
210
     * This method should be overridden for define options
211
     *
212
     * @return array
213
     */
214 256
    protected function defaultTransformerExceptionConstraintOptions(): array
215
    {
216 256
        return [];
217
    }
218
219
    /**
220
     * Get the registry instance
221
     *
222
     * @return RegistryInterface
223
     */
224
    abstract protected function registry(): RegistryInterface;
225
226
    /**
227
     * Create the value validator for the element
228
     *
229
     * @return ValueValidatorInterface
230
     */
231 343
    private function buildValidator(): ValueValidatorInterface
232
    {
233 343
        $registry = $this->registry();
234 343
        $constraints = [];
235
236 343
        foreach ($this->constraintsProviders as $provider) {
237 39
            $constraints = array_merge($constraints, $provider($registry));
238
        }
239
240 343
        foreach ($this->constraints as $constraint) {
241 179
            $constraints[] = $registry->constraint($constraint);
242
        }
243
244 343
        return new ConstraintValueValidator($constraints, $this->getTransformerExceptionConstraint());
245
    }
246
}
247