După articolul cu Swiftype și Algolia, un cititor m-a întrebat: cum poți folosi ambele servicii fără prea mari bătăi de cap. Răspunsul a fost: folosești contracte.
În PHP, contractele astea se numesc interfețe. Practic spui că o clasă va avea anumite metode, vor accepta un anumit număr de parametri și vor returna un anumit tip de date (presupunând că ai PHP 7.3+).
Contractul§
La oricare dintre aceste două servicii avem nevoie cam de aceleași funcționalități: adăugare/editare/ștergere/căutare. Practic este un soi de CRUD overglorified.
interface Engine
{
public function indexDocument(Document $document): bool;
public function getDocumentByID(string $documentID): array;
public function search(string $query, array $filters = []): Results;
public function updateDocument(string $documentID, Document $document): array;
public function deleteDocuments(array $documentIDs, bool $hardDelete = false): bool;
}
Execuția contractului (implementarea)§
În toată treaba asta avem câteva type hints. De exemplu:
public function indexDocument(Document $document): bool;
Înseamnă că metoda asta va accepta o implementare a interfeței Document
și va returna un boolean, în funcție de rezultat.
Motivul pentru care este nevoie de interfețe este următorul: în ciuda faptului că API-ul și structura datelor dintre cele două servicii sunt asemănătoare nu sunt chiar identice 1:1. Deci este nevoie de o implementare diferită pentru fiecare. De exemplu în Swiftype avem așa:
use Swiftype\AppSearch\ClientBuilder;
use Swiftype\AppSearch\Client as SwiftypeClient;
class Swiftype implements Engine
{
protected $apiKey;
protected $apiEngine;
protected $apiEndpoint;
public function __construct($key, $engine, $endpoint)
{
$this->apiKey = $key;
$this->apiEngine = $engine;
$this->apiEndpoint = $endpoint;
}
protected function client(): SwiftypeClient
{
return ClientBuilder::create($this->apiEndpoint, $this->apiKey)->build();
}
public function deleteDocuments(array $documentIDs, bool $hardDelete = false): bool;
{
if ($hardDelete) {
return $this->client()->deleteDocuments($this->apiEngine, $documentIDs);
}
// implementează soft delete mai departe
}
Pe când implementarea din Algolia este ușor diferită:
public function deleteDocuments(array $documentIDs, bool $hardDelete = false): bool;
{
if ($hardDelete) {
return $this->index()->deleteObjects($documentIDs);
}
// implementează soft delete mai departe
}
Și este cam aceeași situație la toate metodele, mai ales la search
!
Și repetăm aceeași șmecherie și la interfețele Results
respectiv Document
, unde Results
este, practic, o colecție de Document
e. Pare mai îmbârligat decât este de fapt
Faptul că avem aceste interfețe și că implementăm codul în baza lor, ne permite să facem o scamatorie de forma:
$engine->deleteDocuments(['doc-123']);
Fără să ne facem griji despre ce tip de date acceptă metoda sau ce întoarce.
Am mai scris despre interfețe și aici: