VatNumberValidator::validateVatNumberAddress()   B
last analyzed

Complexity

Conditions 7
Paths 6

Size

Total Lines 23
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 11
nc 6
nop 2
dl 0
loc 23
rs 8.8333
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Gewebe\SyliusVATPlugin\Validator\Constraints;
6
7
use Gewebe\SyliusVATPlugin\Entity\VatNumberAddressInterface;
8
use Gewebe\SyliusVATPlugin\Vat\Number\ClientException;
9
use Gewebe\SyliusVATPlugin\Vat\Number\VatNumberValidatorInterface;
10
use Gewebe\SyliusVATPlugin\Vat\Number\VatNumberValidatorProviderInterface;
11
use Symfony\Component\Validator\Constraint;
12
use Symfony\Component\Validator\ConstraintValidator;
13
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
14
use Symfony\Component\Validator\Exception\UnexpectedValueException;
15
16
class VatNumberValidator extends ConstraintValidator
17
{
18
    private ?VatNumberValidatorInterface $validator = null;
19
20
    public function __construct(
21
        private VatNumberValidatorProviderInterface $validatorProvider,
22
        private bool $isActive = true,
23
        private bool $validateCountry = true,
24
        private bool $validateExistence = true,
25
        private bool $isCompanyVatRequired = true,
26
        private array $requiredCountries = [],
27
    ) {
28
    }
29
30
    public function validate(mixed $value, Constraint $constraint): void
31
    {
32
        if (!$constraint instanceof VatNumber) {
33
            throw new UnexpectedTypeException($constraint, VatNumber::class);
34
        }
35
36
        if (!$value instanceof VatNumberAddressInterface) {
37
            throw new UnexpectedValueException($value, VatNumberAddressInterface::class);
38
        }
39
40
        if (!$this->hasVatNumberForCompany($value, $constraint)) {
41
            return;
42
        }
43
44
        if (!$this->hasVatNumberForCountry($value, $constraint)) {
45
            return;
46
        }
47
48
        if (!$value->hasVatNumber()) {
49
            return;
50
        }
51
52
        $this->validateVatNumberAddress($value, $constraint);
53
    }
54
55
    private function hasVatNumberForCompany(VatNumberAddressInterface $address, VatNumber $constraint): bool
56
    {
57
        if ($this->isCompanyVatRequired &&
58
            null !== $address->getCompany() &&
59
            '' !== $address->getCompany() &&
60
            !$address->hasVatNumber()) {
61
            $this->context->buildViolation($constraint->messageRequiredForCompany)
62
                ->atPath($constraint->vatNumberPath)
63
                ->addViolation()
64
            ;
65
66
            return false;
67
        }
68
69
        return true;
70
    }
71
72
    private function hasVatNumberForCountry(VatNumberAddressInterface $address, VatNumber $constraint): bool
73
    {
74
        if (count($this->requiredCountries) > 0 &&
75
            in_array($address->getCountryCode(), $this->requiredCountries, true) &&
76
            !$address->hasVatNumber()) {
77
            $this->context->buildViolation($constraint->messageRequired)
78
                ->atPath($constraint->vatNumberPath)
79
                ->addViolation()
80
            ;
81
82
            return false;
83
        }
84
85
        return true;
86
    }
87
88
    private function setValidator(VatNumberAddressInterface $address): bool
89
    {
90
        $countryCode = $address->getCountryCode();
91
        if (null === $countryCode || '' === $countryCode) {
92
            return false;
93
        }
94
95
        $this->validator = $this->validatorProvider->getValidator($countryCode);
96
        if ($this->validator instanceof VatNumberValidatorInterface) {
97
            return true;
98
        }
99
100
        return false;
101
    }
102
103
    private function validateVatNumberAddress(VatNumberAddressInterface $address, VatNumber $constraint): bool
104
    {
105
        if (!$this->isActive) {
106
            return false;
107
        }
108
109
        if (!$this->setValidator($address)) {
110
            return false;
111
        }
112
113
        if (!$this->validateFormat($address, $constraint)) {
114
            return false;
115
        }
116
117
        if ($this->validateCountry && !$this->validateCountry($address, $constraint)) {
118
            return false;
119
        }
120
121
        if ($this->validateExistence) {
122
            return $this->validateExistence($address, $constraint);
123
        }
124
125
        return false;
126
    }
127
128
    /**
129
     * check vat number format
130
     */
131
    private function validateFormat(VatNumberAddressInterface $address, VatNumber $constraint): bool
132
    {
133
        if ($this->validator === null) {
134
            return false;
135
        }
136
137
        $vatNumber = $address->getVatNumber();
138
139
        if (null === $vatNumber || !$this->validator->validateFormat($vatNumber)) {
140
            $this->context->buildViolation($constraint->messageFormat)
141
                ->atPath($constraint->vatNumberPath)
142
                ->addViolation()
143
            ;
144
145
            return false;
146
        }
147
148
        return true;
149
    }
150
151
    /**
152
     * check vat number country is same as address country
153
     */
154
    private function validateCountry(VatNumberAddressInterface $address, VatNumber $constraint): bool
155
    {
156
        if ($this->validator === null) {
157
            return false;
158
        }
159
160
        $vatNumber = $address->getVatNumber();
161
        $countryCode = $address->getCountryCode();
162
163
        if (null === $vatNumber ||
164
            null === $countryCode ||
165
            !$this->validator->validateCountry($vatNumber, $countryCode)) {
166
            $this->context->buildViolation($constraint->messageCountry)
167
                ->atPath($constraint->vatNumberPath)
168
                ->addViolation()
169
            ;
170
171
            return false;
172
        }
173
174
        return true;
175
    }
176
177
    /**
178
     * check vat number existence
179
     */
180
    private function validateExistence(VatNumberAddressInterface $address, VatNumber $constraint): bool
181
    {
182
        if ($this->validator === null) {
183
            return false;
184
        }
185
186
        try {
187
            $vatNumber = $address->getVatNumber();
188
189
            $valid = null !== $vatNumber && $this->validator->validate($vatNumber);
190
191
            $address->setVatValid($valid);
192
        } catch (ClientException $e) {
193
            // ignore VAT client exceptions (when the service is down)
194
            // this could mean that an unexisting VAT number passes validation,
195
            // but it's (probably) better than a hard-error
196
            return true;
197
        }
198
199
        if (false === $valid) {
200
            $this->context->buildViolation($constraint->messageVerified)
201
                ->atPath($constraint->vatNumberPath)
202
                ->addViolation()
203
            ;
204
205
            return false;
206
        }
207
208
        return true;
209
    }
210
}
211