This guide explains how to add new endpoints to the SDK.
Each endpoint is a single class that defines everything:
- API parameter (
what) - Allowed filters
- XML structure for PUT operations
- Schema files for validation
No external registry updates needed!
Create a new class in src/Endpoint/:
<?php
declare(strict_types=1);
namespace Directo\Endpoint;
final class OrdersEndpoint extends AbstractEndpoint
{
public function what(): string
{
return 'order'; // The API "what" parameter value
}
public function allowedFilters(): array
{
return [
'number',
'customer',
'status',
'ts',
];
}
public function xmlElements(): array
{
return [
'root' => 'tellimused', // Root XML element for PUT
'record' => 'tellimus', // Record element name
'key' => 'number', // Key attribute (null if no key)
];
}
public function schemas(): array
{
return [
'list' => 'ws_tellimused.xsd', // Schema for list() responses
'put' => 'xml_IN_tellimused.xsd', // Schema for put() requests
];
}
}Add a method to Client:
// In Client.php
private ?OrdersEndpoint $ordersEndpoint = null;
public function orders(): OrdersEndpoint
{
return $this->ordersEndpoint ??= $this->createEndpoint(OrdersEndpoint::class);
}That's it! The endpoint is ready to use.
The EndpointInterface requires these methods:
| Method | Purpose | Example |
|---|---|---|
what() |
API parameter value | 'order' |
allowedFilters() |
Valid filter keys | ['number', 'customer'] |
xmlElements() |
XML structure for PUT | ['root' => 'tellimused', ...] |
schemas() |
Schema files per operation | ['list' => 'ws_tellimused.xsd'] |
Plus inherited operations: list(), put(), putBatch(), putRaw()
<?php
declare(strict_types=1);
namespace Directo\Endpoint;
final class OrdersEndpoint extends AbstractEndpoint
{
public function what(): string
{
return 'order';
}
public function allowedFilters(): array
{
return ['number', 'customer', 'date_from', 'date_to', 'ts'];
}
public function xmlElements(): array
{
return [
'root' => 'tellimused',
'record' => 'tellimus',
'key' => 'number',
];
}
public function schemas(): array
{
return [
'list' => 'ws_tellimused.xsd',
'put' => 'xml_IN_tellimused.xsd',
];
}
/**
* Get orders by customer.
*/
public function byCustomer(string $customerCode): array
{
return $this->list(['customer' => $customerCode]);
}
}$client = new Client($config);
// List all orders
$orders = $client->orders()->list();
// List by customer
$orders = $client->orders()->byCustomer('CUST001');
// Create order
$result = $client->orders()->put([
'number' => 'ORD001',
'customer' => 'CUST001',
'date' => '2024-01-15',
]);
// Batch create
$result = $client->orders()->putBatch([
['number' => 'ORD002', 'customer' => 'CUST001'],
['number' => 'ORD003', 'customer' => 'CUST002'],
]);The xmlElements() method returns:
| Key | Type | Description |
|---|---|---|
root |
string | Root element wrapping all records |
record |
string | Element name for each record |
key |
string|null | Attribute name for record key (null if none) |
Given:
public function xmlElements(): array
{
return [
'root' => 'tellimused',
'record' => 'tellimus',
'key' => 'number',
];
}PUT with ['number' => 'ORD001', 'customer' => 'CUST001'] produces:
<tellimused>
<tellimus number="ORD001">
<customer>CUST001</customer>
</tellimus>
</tellimused>The schemas() method returns schema files keyed by operation:
| Key | Purpose | Example |
|---|---|---|
list |
Validate list() responses | 'ws_tellimused.xsd' |
put |
Validate put() requests | 'xml_IN_tellimused.xsd' |
Return null to skip validation for an operation:
public function schemas(): array
{
return [
'list' => 'ws_tellimused.xsd',
'put' => null, // No input schema available
];
}test('orders endpoint what value', function () {
$endpoint = createEndpoint(OrdersEndpoint::class);
expect($endpoint->what())->toBe('order');
});
test('orders schemas', function () {
$endpoint = createEndpoint(OrdersEndpoint::class);
expect($endpoint->schemas())->toBe([
'list' => 'ws_tellimused.xsd',
'put' => 'xml_IN_tellimused.xsd',
]);
});
test('lists orders', function () {
$xml = '<results><order><number>ORD001</number></order></results>';
$client = mockClient($xml);
$orders = $client->orders()->list();
expect($orders)->toHaveCount(1);
expect($orders[0]['number'])->toBe('ORD001');
});- Create endpoint class extending
AbstractEndpoint - Implement
what()returning the API parameter value - Implement
allowedFilters()with valid filter keys - Implement
xmlElements()returning root/record/key config - Implement
schemas()returning schema files per operation - Add custom methods for common queries (optional)
- Add client method to
Client - Write unit tests
- Add documentation in
docs/endpoints/