English
English
Appearance
English
English
Appearance
The official library for the Search API and the Catalog API. It handles batching, partial failures, retries and a safe full sync.
The client has no framework dependency: HTTP goes through a PSR-18 client and PSR-17 factories that you provide, logging is optional through PSR-3. It requires PHP 8.1 or newer.
composer require getmetty/metty-phpIf your project does not have a PSR-18 client yet, install any implementation:
composer require symfony/http-client nyholm/psr7use Metty\Client\MettyClient;
$client = MettyClient::create(
publicKey: '<PUBLIC_API_KEY>', // reads
secretKey: '<SECRET_API_KEY>', // catalog writes; server side only
);The addresses of both APIs are defaults, so you do not have to state them. Pass only the key you actually need: a client with pk_ can only read, a client with sk_ can only write. Swapped keys are rejected at construction time so that a secret cannot end up in a URL.
use Metty\Client\Search\SearchQuery;
$response = $client->search()->search(
SearchQuery::for('vŕtačka')
->facet('farba', 'modrá')
->facet('farba', 'čierna') // the same field twice means OR
->category('Náradie > Vŕtačky')
->priceRange(100, 200)
->sortBy('price_asc')
->withSections('facets', 'categories', 'suggestions')
->perPage(24)
->page(2),
);
foreach ($response->products as $product) {
echo $product->id, ' ', $product->name, ' ', $product->price, PHP_EOL;
}
echo $response->total, ' results across ', $response->pages, ' pages';facets, categories, priceRange and suggestions are populated only when withSections() is used. The highlight field arrives from the server including the [] markers; the client does not compute highlighting.
The client knows about the 200 result window: hasNextPage() respects it and a query outside the window fails before the request is sent rather than as a 422.
foreach ($client->search()->searchAll(SearchQuery::for('vŕtačka')) as $product) {
echo $product->name, PHP_EOL;
}Autocomplete:
$suggest = $client->search()->suggest('vŕta', limit: 8);
$suggest->suggestions; // [['query' => 'vŕtačka', 'count' => 41], …]
$suggest->products; // at most 5 compact productsuse Metty\Client\Catalog\CatalogProduct;
$result = $client->catalog()->replace([
CatalogProduct::create('sku-1', 'Príklepová vŕtačka', 'https://eshop.sk/vrtacka',
price: 129.9, inStock: true, brand: 'Bosch', category: 'Náradie > Vŕtačky',
params: ['farba' => 'modrá', 'príkon' => '800 W']),
CatalogProduct::create('sku-2', 'Uhlová brúska', 'https://eshop.sk/bruska', price: 89.5),
]);
if ($result->hasFailures()) {
foreach ($result->failures() as $failure) {
echo $failure->id, ': ', $failure->error, ' — ', $failure->message, PHP_EOL;
}
}You can hand over a catalog of any size at once — the client splits it into batches of 100 products and merges the results.
replace() replaces a product entirely, patch() changes only the fields you send, and delete(['sku-1']) removes products. With patch() an omitted field differs from a field set to null, so clearing a value is written explicitly:
$client->catalog()->patch([
new CatalogProduct('sku-1', ['price' => 99.0, 'brand' => null]),
]);$outcome = $client->catalog()->synchronize($products);
echo $outcome['commit']['removed']; // how many stale products were droppedThe client opens a sync, uploads the whole catalog under it and only then commits. If any product fails to upload, the sync is not committed — the commit would delete exactly what failed — and a SyncIncompleteException is thrown. The sync stays open, so it can be finished:
use Metty\Client\Exception\SyncIncompleteException;
try {
$client->catalog()->synchronize($products);
} catch (SyncIncompleteException $exception) {
$client->catalog()->replace($fixed, $exception->syncId);
$client->catalog()->commit($exception->syncId);
}An empty snapshot is always rejected by the client. The force: true parameter applies solely to the server-side safeguard that rejects a snapshot covering less than half of the catalog.
foreach ($client->catalog()->export() as $product) {
echo $product['id'], PHP_EOL;
}The client pages automatically, so the loop walks the whole catalog.
429 always, honouring Retry-After; a server or network error only for methods that are safe to repeat; other 4xx neverAuthorization header reaches neither the log nor an exceptionNo idempotency key is needed: the server writes by id, so a repeated batch cannot create duplicates.
| exception | when |
|---|---|
ConfigurationException | invalid configuration, or a query the server would reject |
ApiException | the server returned an error; carries statusCode and errorCode |
SyncIncompleteException | a full sync did not complete and was not committed |
TransportException | a network error, or a response that cannot be parsed |
All of them implement Metty\Client\Exception\MettyException, so they can be caught together.
Complete usage examples are in the Examples section.