An easy way to “test” my package on a new version of Laravel

Yesterday I received a PR (thanks Javier Núñez) to update one of my packages that I wrote some while ago to validate the European VAT.
I started to develop a Laravel Package [laravel-vat-eu-validator] for fun and testing my abilities.
Everything’s started when I saw this 💩:
public static function controllaPIVA($pi)
{
if ($pi === '') return false;
if (strlen($pi) != 11)
return false;
if (strlen($pi) == 11) {
if (preg_match("/^[0-9]+\$/D", $pi) != 1)
return false;
$s = 0;
for ($i = 0; $i <= 9; $i += 2)
$s += ord($pi[$i]) - ord('0');
for ($i = 1; $i <= 9; $i += 2) {
$c = 2 * (ord($pi[$i]) - ord('0'));
if ($c > 9) $c = $c - 9;
$s += $c;
}
if ((10 - $s % 10) % 10 != ord($pi[10]) - ord('0'))
return false;
}
return true;
}
wait.
I can’t even read it, at first look it’s impossible to understand, you wouldn’t either. My head is blowing up.
I spent a lot of time trying to understand the code above, finally today I realized it was wrong and a bad implementation of Luhn algorithm. I will talk about refactor in a new post (luhn-example).
It’s seems the Italian VAT is validated by Lhun Algo https://en.wikipedia.org/wiki/VAT_identification_number like the SIM serial number and the credit card number.
So I wrote this super clean and simple class:
<?php
namespace Danielebarbaro\LaravelVatEuValidator;
use Danielebarbaro\LaravelVatEuValidator\Vies\Client;
class VatValidator
{
/**
* Regular expression patterns per country code
*
* @var array
* @link http://ec.europa.eu/taxation_customs/vies/faq.html?locale=en#item_11
*/
protected static $pattern_expression = array(
'AT' => 'U[A-Z\d]{8}',
'BE' => '(0\d{9}|\d{10})',
'BG' => '\d{9,10}',
'CY' => '\d{8}[A-Z]',
'CZ' => '\d{8,10}',
'DE' => '\d{9}',
'DK' => '(\d{2} ?){3}\d{2}',
'EE' => '\d{9}',
'EL' => '\d{9}',
'ES' => '[A-Z]\d{7}[A-Z]|\d{8}[A-Z]|[A-Z]\d{8}',
'FI' => '\d{8}',
'FR' => '([A-Z]{2}|\d{2})\d{9}',
'GB' => '\d{9}|\d{12}|(GD|HA)\d{3}',
'HR' => '\d{11}',
'HU' => '\d{8}',
'IE' => '[A-Z\d]{8}|[A-Z\d]{9}',
'IT' => '\d{11}',
'LT' => '(\d{9}|\d{12})',
'LU' => '\d{8}',
'LV' => '\d{11}',
'MT' => '\d{8}',
'NL' => '\d{9}B\d{2}',
'PL' => '\d{10}',
'PT' => '\d{9}',
'RO' => '\d{2,10}',
'SE' => '\d{12}',
'SI' => '\d{8}',
'SK' => '\d{10}'
);
/**
* Vies Client.
*/
private $client;
/**
* VatValidator constructor.
* @param Client|null $client
*/
public function __construct(Client $client = null)
{
$this->client = $client;
if (!$this->client) {
$this->client = new Client();
}
}
/**
* Validate a VAT number format.
* @param string $vatNumber
* @return boolean
*/
public function validateFormat(string $vatNumber): bool
{
$vatNumber = $this->vatCleaner($vatNumber);
list($country, $number) = $this->splitVat($vatNumber);
if (!isset(self::$pattern_expression[$country])) {
return false;
}
return preg_match('/^' . self::$pattern_expression[$country] . '$/', $number) > 0;
}
/**
* Check existence VAT number
* @param string $vatNumber
* @return boolean
* @throws Vies\ViesException
*/
public function validateExistence(string $vatNumber): bool
{
$vatNumber = $this->vatCleaner($vatNumber);
$result = $this->validateFormat($vatNumber);
if ($result) {
list($country, $number) = $this->splitVat($vatNumber);
$result = $this->client->check($country, $number);
}
return $result;
}
/**
* Validates a VAT number .
*
* @param string $vatNumber Either the full VAT number (incl. country).
* @return boolean
* @throws Vies\ViesException
*/
public function validate(string $vatNumber): bool
{
return $this->validateFormat($vatNumber) && $this->validateExistence($vatNumber);
}
/**
* @param string $vatNumber
* @return string
*/
private function vatCleaner(string $vatNumber): string
{
$vatNumber_no_spaces = trim($vatNumber);
return strtoupper($vatNumber_no_spaces);
}
/**
* @param string $vatNumber
* @return array
*/
private function splitVat(string $vatNumber): array
{
return [
substr($vatNumber, 0, 2),
substr($vatNumber, 2)
];
}
}
and then I have created a Laravel package.
When I received the PR I thought “Thanks God my package has tests”, thus it was super easy to extend the compatibility.

Every test passed so I merged the PR.
But for me it wasn’t enough, I would test it on a fresh L7 installation.
$ composer create-project --prefer-dist laravel/laravel laravel-seven
I added in composer.json
just after "require-dev"
"repositories": {
"dev-laravel-vat-eu-validator": {
"type": "path",
"url": "laravel-vat-eu-validator",
"options": {
"symlink": true
}
}
},
I cloned in laravel-seven
path my repo:
$ cd laravel-seven
$ git clone git@github.com:danielebarbaro/laravel-vat-eu-validator.git
So now I have my package in a specific folder and (eventually) I could switch branch easily directly in laravel-vat-eu-validator folder.

I wrote a simple controller:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
class VatTesterController extends Controller
{
/**
* Show the form for creating a new resource.
*
* @return Response
*/
public function create()
{
return view('vat');
}
/**
* Store a newly created resource in storage.
*
* @param Request $request
* @return string
*/
public function store(Request $request)
{
$request->validate([
'vat' => 'required|vat_number_exist|vat_number|vat_number_format',
]);
return 'Well Done.';
}
}
I wrote a clean blade:
<!DOCTYPE html>
<html>
<head>
<title>Package test</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css"/>
</head>
<body class="bg-light">
<div class="container">
<div class="row">
<div class="col-md-6 offset-3 mt-5">
<div class="card ">
<div class="card-header bg-dark text-light">
<h5>VAT Form Tester</h5>
</div>
<div class="card-body">
@if (count($errors) > 0)
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form action="{{ route('vat.store') }}" method="POST">
@csrf
<div class="form-group">
<label>Vat : </label>
<input type="text" name="vat" class="form-control">
</div>
<div class="form-group text-center">
<input type="submit" class="btn btn-success" name="submit" value="Check">
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
I added default routes:
Route::get('vat-verify','VatTesterController@create')->name('vat.create');
Route::post('vat-verify','VatTesterController@store')->name('vat.store');
I added in validation.php
all necessary strings:
'vat_number' => 'The :attribute must be a valid VAT number.',
'vat_number_format' => 'The :attribute must be write in a valid number format {country_name}{vat_number}.',
'vat_number_exist' => 'VAT number :attribute not exist.',
and once I started the application with $ php artisan serve
, on http://localhost:8000/vat-verify
magic happened

After having understood Luhn Algo, today i added a little checker on my package, take a look at https://github.com/danielebarbaro/laravel-vat-eu-validator/commit/6a9ff5de6bb4dc5a147b2d1616765652210fad6c.
so now I’m ready to enjoy a ☕.
cheers, happy coding