becklyn/vatin-bundle
Symfony bundle integrating the VATIN library to validate EU VAT numbers. Provides a @Vatin constraint for format checks and optional VIES existence validation, plus services to validate VATINs and query the VIES SOAP web service directly.
Installation:
composer require ddeboer/vatin-bundle
Add the bundle to config/app.php under providers:
Ddeboer\VatinBundle\DdeboerVatinBundle::class,
First Use Case: Validate a VAT number format in a Laravel model using annotations:
use Ddeboer\VatinBundle\Validator\Constraints\Vatin;
class Company
{
/**
* @Vatin
*/
public $vatNumber;
}
Laravel’s validator will automatically check the VAT format during form submissions or model validation.
Ddeboer\VatinBundle\Validator\Constraints\Vatin for annotation-based validation.ddeboer_vatin.vatin_validator for direct validation logic.ddeboer_vatin.vies.client for interacting with the EU VAT Information Exchange System.Annotation-Based Validation (Recommended for Forms/Models):
use Ddeboer\VatinBundle\Validator\Constraints\Vatin;
class UserRequest extends FormRequest
{
/**
* @Vatin(checkExistence=true)
*/
public $vatNumber;
}
checkExistence=true to validate against the VIES service (requires internet access).Manual Validation in Controllers/Commands:
use Ddeboer\VatinBundle\Validator\VatinValidator;
public function store(Request $request)
{
$validator = app(VatinValidator::class);
$isValid = $validator->isValid($request->vat_number, false); // false = format only
if (!$isValid) {
return back()->withErrors(['vat_number' => 'Invalid VAT format.']);
}
}
VIES Service Integration:
use Ddeboer\VatinBundle\Client\ViesClient;
public function checkVatExistence(string $vatNumber)
{
$vies = app(ViesClient::class);
try {
$response = $vies->checkVat(explode(' ', $vatNumber)[0], explode(' ', $vatNumber)[1]);
return $response->isValid();
} catch (\Exception $e) {
// Handle VIES service unavailability
return false;
}
}
Form Handling:
ValidatorException for VIES service failures gracefully:
try {
$validator->validate($entity);
} catch (ValidatorException $e) {
// Log or notify admin about VIES service issues
return back()->withErrors(['vat_number' => 'Unable to verify VAT. Please try again later.']);
}
API Validation:
ValidatesWhenResolved trait to integrate with API requests:
use Illuminate\Validation\ValidatesWhenResolved;
class CreateCompanyRequest extends FormRequest
{
use ValidatesWhenResolved;
public function rules()
{
return [
'vat_number' => ['required', new Vatin(['checkExistence' => true])],
];
}
}
Batch Processing:
$validator = app(VatinValidator::class);
$companies = Company::all();
foreach ($companies as $company) {
if (!$validator->isValid($company->vat_number)) {
$company->markAsInvalid();
}
}
Laravel Service Container:
$this->app->bind(VatinValidator::class, function ($app) {
return new VatinValidator();
});
Custom Validation Logic:
class CustomVatinValidator extends VatinValidator
{
public function isValidForBusiness(string $vatNumber): bool
{
$isValid = parent::isValid($vatNumber);
return $isValid && $this->isApprovedCountry($vatNumber);
}
}
Caching VIES Responses:
$cacheKey = 'vies_' . md5($vatNumber);
$response = Cache::remember($cacheKey, now()->addHours(1), function () use ($vies, $vatNumber) {
return $vies->checkVat(...);
});
Fallback for Offline Use:
try {
$response = $vies->checkVat(...);
} catch (\Exception $e) {
$response = Cache::get('vies_fallback_' . $vatNumber, false);
}
VIES Service Unavailability:
try-catch block and handle failures gracefully.Symfony Dependency:
$this->app->bind('validator', function () {
return Validation::makeValidator();
});
Annotation Parsing:
// Instead of annotations:
$validator = Validator::make($data, [
'vat_number' => [new Vatin(['checkExistence' => true])],
]);
VAT Number Format:
COUNTRYCODE123456789B01 (e.g., NL123456789B01). Ensure your input matches this format before validation.$vatNumber = strtoupper(trim($request->vat_number));
Testing:
$this->mock(ViesClient::class)->shouldReceive('checkVat')->andReturn((object) ['isValid' => true]);
Validator Errors:
$errors = $validator->getErrors();
// Handle errors like 'Invalid VAT format' or 'VAT number not found in VIES'.
VIES Service Issues:
try {
$response = $vies->checkVat(...);
\Log::debug('VIES Response', ['data' => $response]);
} catch (\Exception $e) {
\Log::error('VIES Error', ['error' => $e->getMessage()]);
}
Performance Bottlenecks:
$start = microtime(true);
$response = $vies->checkVat(...);
$duration = microtime(true) - $start;
\Log::info('VIES Request Duration', ['duration' => $duration]);
Customizing the VIES Client:
$this->app->bind(ViesClient::class, function ($app) {
$client = new ViesClient();
$client->setOptions(['timeout' => 10]); // Custom timeout
return $client;
});
Validator Configuration:
checkExistence option enables VIES validation. Disable it for offline use or when the service is unreliable:
$validator->isValid($vatNumber, false); // false = format only
Supported Countries:
How can I help you explore Laravel packages today?