266 lines
8.8 KiB
PHP
266 lines
8.8 KiB
PHP
<?php
|
|
|
|
namespace Ulmus\Api;
|
|
|
|
use Psr\Http\Message\RequestInterface;
|
|
use Psr\Http\Message\ResponseInterface;
|
|
use Psr\Http\Message\ServerRequestInterface;
|
|
use Psr\Http\Message\StreamInterface;
|
|
use Psr\Http\Message\UriInterface;
|
|
use Ulmus\Api\Attribute\Obj\Api\ApiAction;
|
|
use Ulmus\Api\Common\MethodEnum;
|
|
use Ulmus\Api\RequestBuilder\Filter;
|
|
use Ulmus\Api\RequestBuilder\UrlParameter;
|
|
use Ulmus\Api\SearchRequest\ApiSearchRequest;
|
|
use Ulmus\Api\Stream\JsonStream;
|
|
use Ulmus\Api\Stream\Stream;
|
|
use Ulmus\Api\Request\JsonRequest;
|
|
use Ulmus\Api\Request\Request;
|
|
use Ulmus\Api\Transport\CurlClient;
|
|
use Ulmus\Api\Transport\CurlTransport;
|
|
use Ulmus\EntityCollection;
|
|
use Ulmus\SearchRequest\SearchRequestInterface;
|
|
use Ulmus\Ulmus;
|
|
|
|
class ApiRepository extends \Ulmus\Repository
|
|
{
|
|
const DEFAULT_ALIAS = "";
|
|
|
|
public CurlClient $client;
|
|
|
|
public ResponseInterface $lastResponse;
|
|
|
|
public function __construct(string $entity, string $alias = self::DEFAULT_ALIAS, ConnectionAdapter $adapter = null)
|
|
{
|
|
parent::__construct($entity, $alias, $adapter);
|
|
}
|
|
|
|
public function loadOne(): ? object
|
|
{
|
|
return $this->loadOneFromAttribute(Attribute\Obj\Api\Read::class);
|
|
}
|
|
|
|
public function loadOneFromAttribute(string $attributeClass): ? object
|
|
{
|
|
$response = $this->executeRequest($attributeClass);
|
|
|
|
return $this->instanciateEntity()->fromArray($response->getParsedBody());
|
|
}
|
|
|
|
public function loadAll() : EntityCollection
|
|
{
|
|
return $this->loadAllFromAttribute(Attribute\Obj\Api\Collection::class);
|
|
}
|
|
|
|
public function loadAllFromAttribute(string $attributeClass) : EntityCollection
|
|
{
|
|
$response = $this->executeRequest($attributeClass);
|
|
|
|
return $this->instanciateEntityCollection()->fromArray($response->getParsedBody());
|
|
}
|
|
|
|
public function search() : EntityCollection
|
|
{
|
|
return $this->loadAllFromAttribute(Attribute\Obj\Api\Search::class);
|
|
}
|
|
|
|
public function save(object|array $entity, ?array $fieldsAndValue = null, bool $replace = false): bool
|
|
{
|
|
$response = $this->executeRequest(Attribute\Obj\Api\Create::class, $entity->entityGetDataset());
|
|
|
|
$entity->fromArray($response->getParsedBody());
|
|
|
|
return $response->getStatusCode() === 200;
|
|
}
|
|
|
|
public function executeRequest(string $attributeClass, mixed $data = null) : ResponseInterface
|
|
{
|
|
$attribute = $this->getApiAttribute($attributeClass);
|
|
|
|
if ($attribute === null) {
|
|
throw new \RuntimeException(sprintf("Could not find attribute class '%s' for class '%s'", $attributeClass, $this->entityClass));
|
|
}
|
|
|
|
$request = $this->prepareRequest($this->buildRequestUrl($attribute->url), $attribute->method, $data, $this->adapter->adapter()->getHeaders(), $this->adapter->adapter()->getParameters());
|
|
|
|
$request = $this->compileRequestParameters($request, $attribute);
|
|
|
|
$this->callApiRequestCallback($request, $attribute);
|
|
|
|
$response = $this->launchRequest($request, $attribute);
|
|
|
|
$response = $this->callApiResponseCallback($response, $attribute);
|
|
|
|
return $response;
|
|
}
|
|
|
|
protected function buildRequestUrl(string $uri) : string
|
|
{
|
|
return $this->adapter->adapter()->url . '/' . ltrim($uri, '/');
|
|
}
|
|
|
|
protected function launchRequest(RequestInterface $request, object $attribute) : ResponseInterface
|
|
{
|
|
$transport = $this->adapter->adapter()->connect();
|
|
|
|
$transport->timeout = $attribute->timeout;
|
|
|
|
$this->lastResponse = $transport->fromRequest($request);
|
|
|
|
$this->callApiDebugCallback($transport);
|
|
|
|
return $this->lastResponse;
|
|
}
|
|
|
|
protected function prepareUri(string $route, array $arguments) : string
|
|
{
|
|
if ( preg_match_all('~{(.*?)}~si', $route, $matches, PREG_SET_ORDER) ) {
|
|
$search = [];
|
|
|
|
foreach($matches as $item) {
|
|
$default = null;
|
|
|
|
$variable = $item[1];
|
|
|
|
# Handles default
|
|
if (strpos($variable, "=") !== false) {
|
|
list($variable, $default) = explode('=', $item[1]);
|
|
}
|
|
|
|
if ( array_key_exists($variable, $arguments) ) {
|
|
$value = $arguments[$variable];
|
|
|
|
unset($arguments[$variable]);
|
|
}
|
|
else {
|
|
if ($default ?? false) {
|
|
$value = $default;
|
|
}
|
|
elseif ( strpos($route, "[{$matches[0][0]}]") !== false && $this->enforceExistingArguments) {
|
|
throw new \RuntimeException(sprintf("Error while preparing route %s : could not match variable '%s' into given arguments ( %s ) from %s::%s", $route, $variable, json_encode($arguments), $routeParam['class'], $routeParam['classMethod']));
|
|
}
|
|
}
|
|
|
|
$search[$item[0]] = rawurlencode($value ?? "");
|
|
}
|
|
|
|
$route = str_replace(array_keys($search), array_values($search), $route);
|
|
}
|
|
|
|
return $route;
|
|
}
|
|
|
|
protected function prepareRequest(string|UriInterface $uri, MethodEnum $method, mixed $body = null, array $headers = [], array $queryParameters = []) : ServerRequestInterface
|
|
{
|
|
$request = new JsonRequest($uri, $method, $body === null ? Stream::fromTemp() : JsonStream::fromContent($body), $headers);
|
|
|
|
# Adding parameters
|
|
$request = $request->withQueryParams($queryParameters);
|
|
|
|
return $request;
|
|
}
|
|
|
|
protected function compileRequestParameters(ServerRequestInterface $request, object $attribute) : ServerRequestInterface
|
|
{
|
|
$requestOptions = $this->queryBuilder->render();
|
|
|
|
$request = $this->applyFiltering($request, $attribute, $requestOptions[Filter::KEY] ?? []);
|
|
|
|
$request = $this->applyUrlBinding($request, $attribute, $requestOptions[UrlParameter::KEY] ?? []);
|
|
|
|
return $request;
|
|
}
|
|
|
|
protected function applyUrlBinding(ServerRequestInterface $request, object $attribute, array $bindings) : ServerRequestInterface
|
|
{
|
|
$uriObj = $request->getUri();
|
|
|
|
$uri = $this->prepareUri($request->getUri()->render(), $bindings);
|
|
|
|
return $request->withUri($uriObj->from($uri));
|
|
}
|
|
|
|
protected function applyFiltering(ServerRequestInterface $request, object $attribute, array $filters) : ServerRequestInterface
|
|
{
|
|
if ($attribute->searchMethod === MethodEnum::Get) {
|
|
$request = $request->withQueryParams($filters);
|
|
}
|
|
elseif ($attribute->searchMethod === MethodEnum::POST) {
|
|
$request = $request->withParsedBody($filters);
|
|
}
|
|
|
|
return $request;
|
|
}
|
|
|
|
public function getApiAttribute(string $type) : ? object
|
|
{
|
|
return $this->entityClass::resolveEntity()->getAttributeImplementing($type);
|
|
}
|
|
|
|
protected function callApiRequestCallback(RequestInterface $request, ApiAction $attribute) : ServerRequestInterface
|
|
{
|
|
return $this->adapter->apiHandler->handleRequest($request, $attribute);
|
|
}
|
|
|
|
protected function callApiResponseCallback(ResponseInterface $response, ApiAction $attribute) : ResponseInterface
|
|
{
|
|
return $this->adapter->apiHandler->handleResponse($response, $attribute);
|
|
}
|
|
|
|
protected function callApiDebugCallback(CurlTransport $transport) : void
|
|
{
|
|
if (method_exists($this->adapter->apiHandler, 'debugResponse')) {
|
|
$this->adapter->apiHandler->debugResponse($transport, $this);
|
|
}
|
|
}
|
|
|
|
public function collectionFromQuery(? string $entityClass = null) : EntityCollection
|
|
{
|
|
$entityClass ??= $this->entityClass;
|
|
|
|
$entityCollection = $entityClass::entityCollection();
|
|
|
|
$this->finalizeQuery();
|
|
|
|
foreach(Ulmus::iterateQueryBuilder($this->queryBuilder, $this->adapter) as $entityData) {
|
|
$entity = $this->instanciateEntity($entityClass);
|
|
$entity->loadedFromAdapter = $this->adapter->name;
|
|
|
|
$entityCollection->append( $entity->resetVirtualProperties()->entityFillFromDataset($entityData) );
|
|
}
|
|
|
|
$this->eventExecute(\Ulmus\Event\Repository\CollectionFromQueryInterface::class, $entityCollection);
|
|
|
|
return $entityCollection;
|
|
}
|
|
|
|
public function filterServerRequest(SearchRequestInterface $searchRequest, bool $count = true) : \Ulmus\Repository
|
|
{
|
|
if ($searchRequest instanceof ApiSearchRequest) {
|
|
$this->bindings($searchRequest->bindings());
|
|
}
|
|
|
|
return parent::filterServerRequest($searchRequest, false);
|
|
}
|
|
|
|
public function count(): int
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
public function bindUrl(string|\Stringable $field, mixed $value) : self
|
|
{
|
|
$this->queryBuilder->bindUrl($field, $value);
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function bindings(array $fieldValues) : self
|
|
{
|
|
foreach($fieldValues as $field => $value) {
|
|
$this->bindUrl($field, $value);
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
} |