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: