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; } }