Passed
Push — master ( 7a99c7...b0c988 )
by Vincent
04:45
created

PhoneElementBuilder::createElement()   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 2
crap 1
1
<?php
2
3
namespace Bdf\Form\Phone;
4
5
use Bdf\Form\AbstractElementBuilder;
6
use Bdf\Form\Aggregate\FormBuilderInterface;
7
use Bdf\Form\Child\ChildBuilderInterface;
8
use Bdf\Form\ElementInterface;
9
use Bdf\Form\Registry\RegistryInterface;
10
use Bdf\Form\Transformer\TransformerInterface;
11
use Bdf\Form\Util\FieldPath;
12
use Bdf\Form\Validator\ValueValidatorInterface;
13
use libphonenumber\PhoneNumberUtil;
14
use libphonenumber\RegionCode;
0 ignored issues
show
Bug introduced by
The type libphonenumber\RegionCode was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
15
use Symfony\Component\Validator\Constraint;
16
17
/**
18
 * Builder for a phone element
19
 *
20
 * <code>
21
 * $builder->phone('contact')
22
 *     ->depends('country')
23
 *     ->regionInput('country')
24
 *     ->allowInvalidNumber()
25
 * ;
26
 * </code>
27
 *
28
 * @see PhoneElement
29
 * @see FormBuilderInterface::phone()
30
 *
31
 * @extends AbstractElementBuilder<PhoneElement>
32
 */
33
class PhoneElementBuilder extends AbstractElementBuilder
34
{
35
    /**
36
     * @var callable(ElementInterface):string|null
37
     */
38
    private $regionResolver;
39
40
    /**
41
     * @var PhoneNumberUtil|null
42
     */
43
    private $formatter;
44
45
    /**
46
     * Invalid phone number are allowed ?
47
     * (i.e. number value is not validated)
48
     *
49
     * @var bool
50
     */
51
    private $allowInvalidNumber = false;
52
53
    /**
54
     * Option for phone number validation
55
     *
56
     * @var array
57
     */
58
    private $validPhoneNumberConstraintOptions = [];
59
60
61
    /**
62
     * PhoneElementBuilder constructor.
63
     *
64
     * @param RegistryInterface|null $registry
65
     */
66 29
    public function __construct(RegistryInterface $registry = null)
67
    {
68 29
        parent::__construct($registry);
69
70 29
        $this->addConstraintsProvider([$this, 'providePhoneConstraint']);
71 29
    }
72
73
    /**
74
     * {@inheritdoc}
75
     *
76
     * @return $this
77
     */
78 4
    public function required($options = null)
79
    {
80 4
        if (!$options instanceof Constraint) {
81 4
            if (is_string($options)) {
82 1
                $options = ['message' => $options];
83
            }
84
85 4
            $options = new NotEmptyPhoneNumber($options);
86
        }
87
88 4
        return $this->satisfy($options);
89
    }
90
91
    /**
92
     * Define the region or country resolver
93
     *
94
     * <code>
95
     * $builder->regionResolver(function (PhoneElement $element) {
96
     *     return $this->user()->countryCode();
97
     * });
98
     * </code>
99
     *
100
     * @param callable(ElementInterface):string $regionResolver The resolver. Takes as parameter the PhoneElement, and must return the country code as string
101
     *
102
     * @return $this
103
     */
104 10
    public function regionResolver(callable $regionResolver): self
105
    {
106 10
        $this->regionResolver = $regionResolver;
107
108 10
        return $this;
109
    }
110
111
    /**
112
     * Define the default region code for parsing the phone number
113
     *
114
     * @param string $region The region code. See RegionCode constants
115
     *
116
     * @return $this
117
     *
118
     * @see RegionCode
119
     */
120
    public function region(string $region): self
121
    {
122
        return $this->regionResolver(function () use($region) { return $region; });
123
    }
124
125
    /**
126
     * Use a sibling input as region code value
127
     *
128
     * Note: Do not forget to declare the other input as dependency
129
     *
130
     * <code>
131
     * $builder->string('country')->choice();
132
     * $builder
133
     *      ->phone('phone')
134
     *      ->depends('country')
135
     *      ->regionInput('country')
136
     * ;
137
     * </code>
138
     *
139
     * @param string $inputPath The input path
140
     *
141
     * @return $this
142
     *
143
     * @see RegionCode
144
     * @see FieldPath::parse() For the path syntax
145
     * @see ChildBuilderInterface::depends() For declare the dependency to the other field
146
     */
147 1
    public function regionInput(string $inputPath): self
148
    {
149
        return $this->regionResolver(function (ElementInterface $element) use($inputPath) {
150 1
            return FieldPath::parse($inputPath)->value($element);
151 1
        });
152
    }
153
154
    /**
155
     * Define the PhoneNumberUtil instance
156
     *
157
     * @param PhoneNumberUtil $formatter
158
     *
159
     * @return $this
160
     */
161 1
    public function formatter(PhoneNumberUtil $formatter): self
162
    {
163 1
        $this->formatter = $formatter;
164
165 1
        return $this;
166
    }
167
168
    /**
169
     * Disable phone number validation check
170
     * If enabled, the element will not be marked as invalid if an invalid number is submitted
171
     *
172
     * @param bool $allowInvalidNumber
173
     *
174
     * @return $this
175
     */
176 4
    public function allowInvalidNumber(bool $allowInvalidNumber = true): self
177
    {
178 4
        $this->allowInvalidNumber = $allowInvalidNumber;
179
180 4
        return $this;
181
    }
182
183
    /**
184
     * Define phone validation options
185
     *
186
     * Note: This method can be called multiple times, the last defined options will overrides the previous ones
187
     *
188
     * Usage:
189
     * <code>
190
     * $builder->validateNumber('My error'); // Define the error message
191
     * $builder->validateNumber(['message' => 'My error']); // Also accept array of options
192
     * </code>
193
     *
194
     * @param array|string $options The option array, or string for provide the error message
195
     *
196
     * @return $this
197
     * @see ValidPhoneNumber
198
     */
199 1
    public function validateNumber($options = []): self
200
    {
201 1
        if (is_string($options)) {
202 1
            $options = ['message' => $options];
203
        }
204
205 1
        $this->allowInvalidNumber = false;
206 1
        $this->validPhoneNumberConstraintOptions = $options;
207
208 1
        return $this;
209
    }
210
211
    /**
212
     * Define the error message if the phone number is invalid
213
     *
214
     * @param string $message
215
     *
216
     * @return $this
217
     * @see ValidPhoneNumber::$message
218
     */
219 1
    public function errorMessage(string $message): self
220
    {
221 1
        $this->validPhoneNumberConstraintOptions['message'] = $message;
222
223 1
        return $this;
224
    }
225
226
    /**
227
     * {@inheritdoc}
228
     */
229 27
    protected function createElement(ValueValidatorInterface $validator, TransformerInterface $transformer): ElementInterface
230
    {
231 27
        return new PhoneElement($validator, $transformer, $this->regionResolver, $this->formatter);
232
    }
233
234
    /**
235
     * Provide validation constraint for the phone number
236
     *
237
     * @return Constraint[]
238
     */
239 27
    protected function providePhoneConstraint(): array
240
    {
241 27
        if ($this->allowInvalidNumber) {
242 4
            return [];
243
        }
244
245 23
        return [new ValidPhoneNumber($this->validPhoneNumberConstraintOptions)];
246
    }
247
}
248