Passed
Push — master ( 5b91c4...ecf04c )
by Vincent
11:13
created

PhoneElementBuilder   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 195
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 12
eloc 27
c 1
b 0
f 0
dl 0
loc 195
ccs 31
cts 31
cp 1
rs 10

10 Methods

Rating   Name   Duplication   Size   Complexity  
A formatter() 0 5 1
A regionResolver() 0 5 1
A allowInvalidNumber() 0 5 1
A __construct() 0 5 1
A validateNumber() 0 10 2
A region() 0 3 1
A regionInput() 0 4 1
A errorMessage() 0 5 1
A providePhoneConstraint() 0 7 2
A createElement() 0 3 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 25
    public function __construct(RegistryInterface $registry = null)
67
    {
68 25
        parent::__construct($registry);
69
70 25
        $this->addConstraintsProvider([$this, 'providePhoneConstraint']);
71 25
    }
72
73
    /**
74
     * Define the region or country resolver
75
     *
76
     * <code>
77
     * $builder->regionResolver(function (PhoneElement $element) {
78
     *     return $this->user()->countryCode();
79
     * });
80
     * </code>
81
     *
82
     * @param callable(ElementInterface):string $regionResolver The resolver. Takes as parameter the PhoneElement, and must return the country code as string
83
     *
84
     * @return $this
85
     */
86 10
    public function regionResolver(callable $regionResolver): self
87
    {
88 10
        $this->regionResolver = $regionResolver;
89
90 10
        return $this;
91
    }
92
93
    /**
94
     * Define the default region code for parsing the phone number
95
     *
96
     * @param string $region The region code. See RegionCode constants
97
     *
98
     * @return $this
99
     *
100
     * @see RegionCode
101
     */
102
    public function region(string $region): self
103
    {
104
        return $this->regionResolver(function () use($region) { return $region; });
105
    }
106
107
    /**
108
     * Use a sibling input as region code value
109
     *
110
     * Note: Do not forget to declare the other input as dependency
111
     *
112
     * <code>
113
     * $builder->string('country')->choice();
114
     * $builder
115
     *      ->phone('phone')
116
     *      ->depends('country')
117
     *      ->regionInput('country')
118
     * ;
119
     * </code>
120
     *
121
     * @param string $inputPath The input path
122
     *
123
     * @return $this
124
     *
125
     * @see RegionCode
126
     * @see FieldPath::parse() For the path syntax
127
     * @see ChildBuilderInterface::depends() For declare the dependency to the other field
128
     */
129 1
    public function regionInput(string $inputPath): self
130
    {
131
        return $this->regionResolver(function (ElementInterface $element) use($inputPath) {
132 1
            return FieldPath::parse($inputPath)->value($element);
133 1
        });
134
    }
135
136
    /**
137
     * Define the PhoneNumberUtil instance
138
     *
139
     * @param PhoneNumberUtil $formatter
140
     *
141
     * @return $this
142
     */
143 1
    public function formatter(PhoneNumberUtil $formatter): self
144
    {
145 1
        $this->formatter = $formatter;
146
147 1
        return $this;
148
    }
149
150
    /**
151
     * Disable phone number validation check
152
     * If enabled, the element will not be marked as invalid if an invalid number is submitted
153
     *
154
     * @param bool $allowInvalidNumber
155
     *
156
     * @return $this
157
     */
158 1
    public function allowInvalidNumber(bool $allowInvalidNumber = true): self
159
    {
160 1
        $this->allowInvalidNumber = $allowInvalidNumber;
161
162 1
        return $this;
163
    }
164
165
    /**
166
     * Define phone validation options
167
     *
168
     * Note: This method can be called multiple times, the last defined options will overrides the previous ones
169
     *
170
     * Usage:
171
     * <code>
172
     * $builder->validateNumber('My error'); // Define the error message
173
     * $builder->validateNumber(['message' => 'My error']); // Also accept array of options
174
     * </code>
175
     *
176
     * @param array|string $options The option array, or string for provide the error message
177
     *
178
     * @return $this
179
     * @see ValidPhoneNumber
180
     */
181 1
    public function validateNumber($options = []): self
182
    {
183 1
        if (is_string($options)) {
184 1
            $options = ['message' => $options];
185
        }
186
187 1
        $this->allowInvalidNumber = false;
188 1
        $this->validPhoneNumberConstraintOptions = $options;
189
190 1
        return $this;
191
    }
192
193
    /**
194
     * Define the error message if the phone number is invalid
195
     *
196
     * @param string $message
197
     *
198
     * @return $this
199
     * @see ValidPhoneNumber::$message
200
     */
201 1
    public function errorMessage(string $message): self
202
    {
203 1
        $this->validPhoneNumberConstraintOptions['message'] = $message;
204
205 1
        return $this;
206
    }
207
208
    /**
209
     * {@inheritdoc}
210
     */
211 23
    protected function createElement(ValueValidatorInterface $validator, TransformerInterface $transformer): ElementInterface
212
    {
213 23
        return new PhoneElement($validator, $transformer, $this->regionResolver, $this->formatter);
214
    }
215
216
    /**
217
     * Provide validation constraint for the phone number
218
     *
219
     * @return Constraint[]
220
     */
221 23
    protected function providePhoneConstraint(): array
222
    {
223 23
        if ($this->allowInvalidNumber) {
224 1
            return [];
225
        }
226
227 22
        return [new ValidPhoneNumber($this->validPhoneNumberConstraintOptions)];
228
    }
229
}
230