DriveRight Client Library¶
Overview¶
Standalone PHP client for the DriveRight vehicle data API. Provides Dutch license plate lookup and vehicle fitment data.
Package: atraxion/driveright-client
Installation¶
composer require atraxion/driveright-client
Configuration¶
use Atraxion\DriveRight\Config;
use Atraxion\DriveRight\DriveRightClient;
$config = new Config(
apiKey: 'your-api-key',
baseUrl: 'https://api.driveright.nl/v2',
timeout: 30
);
$client = new DriveRightClient($config);
Core Features¶
License Plate Lookup¶
$vehicle = $client->lookupPlate('AB-123-CD');
if ($vehicle) {
echo $vehicle->getMake(); // "Volkswagen"
echo $vehicle->getModel(); // "Golf"
echo $vehicle->getYear(); // 2020
echo $vehicle->getFuelType(); // "petrol"
echo $vehicle->getEngineCode(); // "DFYA"
echo $vehicle->getPowerKw(); // 110
echo $vehicle->getPowerHp(); // 150
}
Get Vehicle Fitments¶
$fitments = $client->getFitments($vehicle->getVehicleId());
// Tyre fitments
foreach ($fitments->getTyreSizes() as $size) {
echo $size->getWidth(); // 225
echo $size->getAspectRatio(); // 45
echo $size->getDiameter(); // 17
echo $size->getCode(); // "225/45R17"
echo $size->isOriginalEquipment(); // true
echo $size->getPosition(); // "both" or "front"/"rear"
}
// Wheel fitments
foreach ($fitments->getWheelSpecs() as $spec) {
echo $spec->getDiameter(); // 17
echo $spec->getWidth(); // 7.5
echo $spec->getPcd(); // "5x112"
echo $spec->getCenterBore(); // 57.1
echo $spec->getOffsetMin(); // 35
echo $spec->getOffsetMax(); // 50
}
Get All Makes¶
$makes = $client->getMakes();
foreach ($makes as $make) {
echo $make->getId(); // "volkswagen"
echo $make->getName(); // "Volkswagen"
echo $make->getLogoUrl(); // "https://..."
}
Get Models by Make¶
$models = $client->getModels('volkswagen');
foreach ($models as $model) {
echo $model->getId(); // "golf"
echo $model->getName(); // "Golf"
echo $model->getYearFrom(); // 1974
echo $model->getYearTo(); // null (still in production)
}
Get Generations¶
$generations = $client->getGenerations('volkswagen', 'golf');
foreach ($generations as $gen) {
echo $gen->getId(); // "golf-8"
echo $gen->getName(); // "Golf 8 (CD)"
echo $gen->getYearFrom(); // 2019
echo $gen->getYearTo(); // null
echo $gen->getBodyType(); // "hatchback"
}
Get Variants¶
$variants = $client->getVariants('volkswagen', 'golf', 'golf-8');
foreach ($variants as $variant) {
echo $variant->getId(); // "golf-8-20-tdi-150"
echo $variant->getName(); // "2.0 TDI 150hp"
echo $variant->getEngineCode(); // "DFYA"
echo $variant->getPowerKw(); // 110
echo $variant->getFuelType(); // "diesel"
}
Models¶
Vehicle¶
readonly class Vehicle
{
public function __construct(
public string $vehicleId,
public string $make,
public string $model,
public int $year,
public ?string $variant,
public ?string $engineCode,
public string $fuelType,
public ?int $powerKw,
public ?int $powerHp,
public ?int $cylinderCapacity,
public ?string $bodyType,
public ?int $doors,
public ?string $transmission,
public ?string $driveType,
public ?float $weight,
public ?string $vin
) {}
public function getDisplayName(): string
{
$name = "{$this->make} {$this->model}";
if ($this->variant) {
$name .= " {$this->variant}";
}
return $name;
}
}
TyreSize¶
readonly class TyreSize
{
public function __construct(
public int $width,
public int $aspectRatio,
public float $diameter,
public string $position = 'both',
public bool $isOriginalEquipment = false,
public ?string $loadIndex = null,
public ?string $speedIndex = null
) {}
public function getCode(): string
{
return sprintf('%d/%dR%.0f', $this->width, $this->aspectRatio, $this->diameter);
}
public function getFullCode(): string
{
$code = $this->getCode();
if ($this->loadIndex && $this->speedIndex) {
$code .= " {$this->loadIndex}{$this->speedIndex}";
}
return $code;
}
}
WheelSpec¶
readonly class WheelSpec
{
public function __construct(
public float $diameter,
public float $width,
public string $pcd,
public float $centerBore,
public int $offsetMin,
public int $offsetMax,
public int $boltCount,
public float $boltCircle
) {}
public function getPcdParts(): array
{
// Parse "5x112" into [5, 112]
preg_match('/(\d+)x(\d+(?:\.\d+)?)/', $this->pcd, $matches);
return [
'bolts' => (int) $matches[1],
'circle' => (float) $matches[2],
];
}
public function isOffsetInRange(int $offset): bool
{
return $offset >= $this->offsetMin && $offset <= $this->offsetMax;
}
}
VehicleFitments¶
readonly class VehicleFitments
{
public function __construct(
/** @var TyreSize[] */
public array $tyreSizes,
/** @var WheelSpec[] */
public array $wheelSpecs,
public array $originalEquipment = []
) {}
public function getOeTyreSizes(): array
{
return array_filter(
$this->tyreSizes,
fn(TyreSize $size) => $size->isOriginalEquipment
);
}
public function hasDifferentFrontRear(): bool
{
$positions = array_unique(array_map(
fn(TyreSize $s) => $s->position,
$this->tyreSizes
));
return in_array('front', $positions) || in_array('rear', $positions);
}
}
Error Handling¶
use Atraxion\DriveRight\Exception\ApiException;
use Atraxion\DriveRight\Exception\VehicleNotFoundException;
use Atraxion\DriveRight\Exception\InvalidPlateException;
use Atraxion\DriveRight\Exception\RateLimitException;
try {
$vehicle = $client->lookupPlate('XX-000-XX');
} catch (InvalidPlateException $e) {
// Invalid plate format
echo "Invalid license plate format";
} catch (VehicleNotFoundException $e) {
// Plate not found in database
echo "Vehicle not found";
} catch (RateLimitException $e) {
// Too many requests
$retryAfter = $e->getRetryAfterSeconds();
} catch (ApiException $e) {
// Other API error
$this->logger->error('DriveRight error', [
'code' => $e->getCode(),
'message' => $e->getMessage(),
]);
}
Caching¶
use Atraxion\DriveRight\Cache\CachedDriveRightClient;
$cachedClient = new CachedDriveRightClient(
client: $client,
cache: $psr16Cache,
plateTtl: 86400, // Cache plate lookups for 24 hours
fitmentsTtl: 604800, // Cache fitments for 7 days
catalogTtl: 86400 // Cache makes/models for 24 hours
);
Testing¶
Mock Client¶
use Atraxion\DriveRight\Testing\MockDriveRightClient;
$mock = new MockDriveRightClient();
$mock->addVehicle('AB-123-CD', new Vehicle(
vehicleId: 'VH123',
make: 'Volkswagen',
model: 'Golf',
year: 2020,
fuelType: 'petrol'
));
$mock->addFitments('VH123', new VehicleFitments(
tyreSizes: [
new TyreSize(225, 45, 17, 'both', true),
],
wheelSpecs: [
new WheelSpec(17, 7.5, '5x112', 57.1, 35, 50, 5, 112),
]
));
// Use in tests
$vehicle = $mock->lookupPlate('AB-123-CD');
Gherkin Scenarios¶
Feature: DriveRight Client
As a developer
I want to lookup vehicles by license plate
Using the DriveRight API
Scenario: Lookup Dutch license plate
Given valid DriveRight credentials
When I lookup plate "AB-123-CD"
Then I should receive vehicle details
And vehicle should have make and model
Scenario: Get fitments for vehicle
Given vehicle "VH123" exists
When I request fitments
Then I should receive tyre sizes
And I should receive wheel specifications
Scenario: Invalid license plate format
When I lookup plate "INVALID"
Then InvalidPlateException should be thrown
Scenario: Vehicle not found
When I lookup plate "00-000-00"
Then VehicleNotFoundException should be thrown
Scenario: Browse vehicle catalog
When I request all makes
Then I should receive list of car manufacturers
And I can get models for each make
Scenario: Get OE tyre sizes
Given vehicle has multiple tyre options
When I request fitments
Then I can filter original equipment sizes
Configuration Reference¶
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
apiKey |
string | Yes | - | DriveRight API key |
baseUrl |
string | No | production | API base URL |
timeout |
int | No | 30 | Request timeout in seconds |
retryOnRateLimit |
bool | No | true | Auto-retry on 429 |
maxRetries |
int | No | 3 | Max retry attempts |
API Endpoints Used¶
| Endpoint | Method | Purpose |
|---|---|---|
/kenteken/{plate} |
GET | License plate lookup |
/voertuig/{id}/passingen |
GET | Get vehicle fitments |
/merken |
GET | List all makes |
/merken/{make}/modellen |
GET | List models for make |
/modellen/{model}/generaties |
GET | List generations |
/generaties/{gen}/uitvoeringen |
GET | List variants |