From 5790ae0de305bc7b982b3f573fdbf41bd62f2dbd Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Sun, 28 Jan 2024 22:00:47 +0100 Subject: [PATCH] chubbyphp-parsing --- composer.json | 3 +- config/prod.php | 27 +++++++- src/Collection/AbstractCollection.php | 20 ++++++ src/Collection/CollectionInterface.php | 2 +- .../Collection/CollectionRequestInterface.php | 12 ++++ src/Dto/Collection/PetCollectionRequest.php | 36 +++++++++++ src/Dto/Collection/PetCollectionResponse.php | 36 +++++++++++ src/Dto/LinkResponse.php | 15 +++++ src/Dto/Model/ModelRequestInterface.php | 14 +++++ src/Dto/Model/PetRequest.php | 59 +++++++++++++++++ src/Dto/Model/PetResponse.php | 30 +++++++++ src/Dto/Model/VaccinationRequest.php | 10 +++ src/Dto/Model/VaccinationResponse.php | 10 +++ src/Enrich/Enrich.php | 53 ++++++++++++++++ src/Enrich/EnrichInterface.php | 21 +++++++ src/Model/ModelInterface.php | 2 +- src/Model/Pet.php | 17 +++++ src/Model/Vaccination.php | 9 ++- src/Parsing/ParsingInterface.php | 14 +++++ src/Parsing/PetCollectionParsing.php | 49 +++++++++++++++ src/Parsing/PetParsing.php | 53 ++++++++++++++++ .../Api/Crud/CreateRequestHandler.php | 59 ++++++++--------- .../Api/Crud/ListRequestHandler.php | 47 ++++++++------ .../Api/Crud/ReadRequestHandler.php | 20 +++++- .../Api/Crud/UpdateRequestHandler.php | 63 ++++++++----------- .../Enrich/PetEnrichFactory.php | 18 ++++++ .../Framework/RoutesByNameFactory.php | 12 ++-- src/ServiceFactory/Parsing/ParserFactory.php | 18 ++++++ .../Parsing/PetCollectionParsingFactory.php | 25 ++++++++ .../Parsing/PetParsingFactory.php | 21 +++++++ .../Crud/PetCreateRequestHandlerFactory.php | 19 +++--- .../Api/Crud/PetListRequestHandlerFactory.php | 19 +++--- .../Api/Crud/PetReadRequestHandlerFactory.php | 11 +++- .../Crud/PetUpdateRequestHandlerFactory.php | 17 +++-- 34 files changed, 714 insertions(+), 127 deletions(-) create mode 100644 src/Dto/Collection/CollectionRequestInterface.php create mode 100644 src/Dto/Collection/PetCollectionRequest.php create mode 100644 src/Dto/Collection/PetCollectionResponse.php create mode 100644 src/Dto/LinkResponse.php create mode 100644 src/Dto/Model/ModelRequestInterface.php create mode 100644 src/Dto/Model/PetRequest.php create mode 100644 src/Dto/Model/PetResponse.php create mode 100644 src/Dto/Model/VaccinationRequest.php create mode 100644 src/Dto/Model/VaccinationResponse.php create mode 100644 src/Enrich/Enrich.php create mode 100644 src/Enrich/EnrichInterface.php create mode 100644 src/Parsing/ParsingInterface.php create mode 100644 src/Parsing/PetCollectionParsing.php create mode 100644 src/Parsing/PetParsing.php create mode 100644 src/ServiceFactory/Enrich/PetEnrichFactory.php create mode 100644 src/ServiceFactory/Parsing/ParserFactory.php create mode 100644 src/ServiceFactory/Parsing/PetCollectionParsingFactory.php create mode 100644 src/ServiceFactory/Parsing/PetParsingFactory.php diff --git a/composer.json b/composer.json index 4b03f7f8..1a4ad641 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,8 @@ "chubbyphp/chubbyphp-laminas-config": "^1.4", "chubbyphp/chubbyphp-laminas-config-doctrine": "^2.1", "chubbyphp/chubbyphp-laminas-config-factory": "^1.3", - "chubbyphp/chubbyphp-negotiation": "^2.0", + "chubbyphp/chubbyphp-negotiation": "^2.1", + "chubbyphp/chubbyphp-parsing": "^1.0@dev", "chubbyphp/chubbyphp-serialization": "^4.0", "chubbyphp/chubbyphp-validation": "^4.0", "doctrine/orm": "^2.17.2", diff --git a/config/prod.php b/config/prod.php index 54cca0f7..e3d1a249 100644 --- a/config/prod.php +++ b/config/prod.php @@ -2,12 +2,15 @@ declare(strict_types=1); +use App\Enrich\EnrichInterface; use App\Factory\Collection\PetCollectionFactory; use App\Factory\Model\PetFactory; use App\Mapping\Orm\PetMapping; use App\Mapping\Orm\VaccinationMapping; use App\Model\Pet; use App\Model\Vaccination; +use App\Parsing\PetCollectionParsing; +use App\Parsing\PetParsing; use App\Repository\PetRepository; use App\RequestHandler\Api\Crud\CreateRequestHandler; use App\RequestHandler\Api\Crud\DeleteRequestHandler; @@ -21,6 +24,7 @@ use App\ServiceFactory\DecodeEncode\TypeDecodersFactory; use App\ServiceFactory\DecodeEncode\TypeEncodersFactory; use App\ServiceFactory\Deserialization\DenormalizationObjectMappingsFactory; +use App\ServiceFactory\Enrich\PetEnrichFactory; use App\ServiceFactory\Factory\Collection\PetCollectionFactoryFactory; use App\ServiceFactory\Factory\Model\PetFactoryFactory; use App\ServiceFactory\Framework\ExceptionMiddlewareFactory; @@ -34,6 +38,9 @@ use App\ServiceFactory\Logger\LoggerFactory; use App\ServiceFactory\Negotiation\AcceptNegotiatorSupportedMediaTypesFactory; use App\ServiceFactory\Negotiation\ContentTypeNegotiatorSupportedMediaTypesFactory; +use App\ServiceFactory\Parsing\ParserFactory; +use App\ServiceFactory\Parsing\PetCollectionParsingFactory; +use App\ServiceFactory\Parsing\PetParsingFactory; use App\ServiceFactory\Repository\PetRepositoryFactory; use App\ServiceFactory\RequestHandler\Api\Crud\PetCreateRequestHandlerFactory; use App\ServiceFactory\RequestHandler\Api\Crud\PetDeleteRequestHandlerFactory; @@ -46,16 +53,18 @@ use App\ServiceFactory\Validation\ValidationMappingProviderFactory; use Chubbyphp\ApiHttp\Manager\RequestManagerInterface; use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; -use Chubbyphp\ApiHttp\Middleware\AcceptAndContentTypeMiddleware; use Chubbyphp\ApiHttp\Middleware\ApiExceptionMiddleware; -use Chubbyphp\ApiHttp\ServiceFactory\AcceptAndContentTypeMiddlewareFactory; use Chubbyphp\ApiHttp\ServiceFactory\ApiExceptionMiddlewareFactory; use Chubbyphp\ApiHttp\ServiceFactory\RequestManagerFactory; use Chubbyphp\ApiHttp\ServiceFactory\ResponseManagerFactory; use Chubbyphp\Cors\CorsMiddleware; use Chubbyphp\Cors\ServiceFactory\CorsMiddlewareFactory; +use Chubbyphp\DecodeEncode\Decoder\DecoderInterface; use Chubbyphp\DecodeEncode\Decoder\TypeDecoderInterface; +use Chubbyphp\DecodeEncode\Encoder\EncoderInterface; use Chubbyphp\DecodeEncode\Encoder\TypeEncoderInterface; +use Chubbyphp\DecodeEncode\ServiceFactory\DecoderFactory; +use Chubbyphp\DecodeEncode\ServiceFactory\EncoderFactory; use Chubbyphp\Deserialization\DeserializerInterface; use Chubbyphp\Deserialization\Mapping\DenormalizationObjectMappingInterface; use Chubbyphp\Deserialization\ServiceFactory\DeserializerFactory; @@ -72,8 +81,13 @@ use Chubbyphp\Laminas\Config\Doctrine\ServiceFactory\Persistence\Mapping\Driver\ClassMapDriverFactory; use Chubbyphp\Negotiation\AcceptNegotiatorInterface; use Chubbyphp\Negotiation\ContentTypeNegotiatorInterface; +use Chubbyphp\Negotiation\Middleware\AcceptMiddleware; +use Chubbyphp\Negotiation\Middleware\ContentTypeMiddleware; +use Chubbyphp\Negotiation\ServiceFactory\AcceptMiddlewareFactory; use Chubbyphp\Negotiation\ServiceFactory\AcceptNegotiatorFactory; +use Chubbyphp\Negotiation\ServiceFactory\ContentTypeMiddlewareFactory; use Chubbyphp\Negotiation\ServiceFactory\ContentTypeNegotiatorFactory; +use Chubbyphp\Parsing\ParserInterface; use Chubbyphp\Serialization\Mapping\NormalizationObjectMappingInterface; use Chubbyphp\Serialization\SerializerInterface; use Chubbyphp\Serialization\ServiceFactory\SerializerFactory; @@ -116,7 +130,7 @@ EntityManager::class => EntityManagerInterface::class, ], 'factories' => [ - AcceptAndContentTypeMiddleware::class => AcceptAndContentTypeMiddlewareFactory::class, + AcceptMiddleware::class => AcceptMiddlewareFactory::class, AcceptNegotiatorInterface::class . 'supportedMediaTypes[]' => AcceptNegotiatorSupportedMediaTypesFactory::class, AcceptNegotiatorInterface::class => AcceptNegotiatorFactory::class, ApiExceptionMiddleware::class => ApiExceptionMiddlewareFactory::class, @@ -124,11 +138,14 @@ Command::class . '[]' => CommandsFactory::class, Connection::class => ConnectionFactory::class, ConnectionProvider::class => ContainerConnectionProviderFactory::class, + ContentTypeMiddleware::class => ContentTypeMiddlewareFactory::class, ContentTypeNegotiatorInterface::class . 'supportedMediaTypes[]' => ContentTypeNegotiatorSupportedMediaTypesFactory::class, ContentTypeNegotiatorInterface::class => ContentTypeNegotiatorFactory::class, CorsMiddleware::class => CorsMiddlewareFactory::class, + DecoderInterface::class => DecoderFactory::class, DenormalizationObjectMappingInterface::class . '[]' => DenormalizationObjectMappingsFactory::class, DeserializerInterface::class => DeserializerFactory::class, + EncoderInterface::class => EncoderFactory::class, EntityManagerInterface::class => EntityManagerFactory::class, EntityManagerProvider::class => ContainerEntityManagerProviderFactory::class, ExceptionMiddleware::class => ExceptionMiddlewareFactory::class, @@ -137,13 +154,17 @@ MiddlewareInterface::class . '[]' => MiddlewaresFactory::class, NormalizationObjectMappingInterface::class . '[]' => NormalizationObjectMappingsFactory::class, OpenapiRequestHandler::class => OpenapiRequestHandlerFactory::class, + ParserInterface::class => ParserFactory::class, Pet::class . CreateRequestHandler::class => PetCreateRequestHandlerFactory::class, Pet::class . DeleteRequestHandler::class => PetDeleteRequestHandlerFactory::class, + Pet::class . EnrichInterface::class => PetEnrichFactory::class, Pet::class . ListRequestHandler::class => PetListRequestHandlerFactory::class, Pet::class . ReadRequestHandler::class => PetReadRequestHandlerFactory::class, Pet::class . UpdateRequestHandler::class => PetUpdateRequestHandlerFactory::class, PetCollectionFactory::class => PetCollectionFactoryFactory::class, + PetCollectionParsing::class => PetCollectionParsingFactory::class, PetFactory::class => PetFactoryFactory::class, + PetParsing::class => PetParsingFactory::class, PetRepository::class => PetRepositoryFactory::class, PingRequestHandler::class => PingRequestHandlerFactory::class, RequestManagerInterface::class => RequestManagerFactory::class, diff --git a/src/Collection/AbstractCollection.php b/src/Collection/AbstractCollection.php index 83962e22..e3c2a12b 100644 --- a/src/Collection/AbstractCollection.php +++ b/src/Collection/AbstractCollection.php @@ -106,4 +106,24 @@ final public function getItems(): array { return $this->items; } + + /** + * @return array{offset: int, limit: int, filters: array, sort: array, items: array, count: int} + */ + public function jsonSerialize(): array + { + $items = []; + foreach ($this->items as $item) { + $items[] = $item->jsonSerialize(); + } + + return [ + 'offset' => $this->offset, + 'limit' => $this->limit, + 'filters' => $this->filters, + 'sort' => $this->sort, + 'items' => $items, + 'count' => $this->count, + ]; + } } diff --git a/src/Collection/CollectionInterface.php b/src/Collection/CollectionInterface.php index 3d209e90..632b81b0 100644 --- a/src/Collection/CollectionInterface.php +++ b/src/Collection/CollectionInterface.php @@ -6,7 +6,7 @@ use App\Model\ModelInterface; -interface CollectionInterface +interface CollectionInterface extends \JsonSerializable { public const LIMIT = 20; diff --git a/src/Dto/Collection/CollectionRequestInterface.php b/src/Dto/Collection/CollectionRequestInterface.php new file mode 100644 index 00000000..7b4c09c1 --- /dev/null +++ b/src/Dto/Collection/CollectionRequestInterface.php @@ -0,0 +1,12 @@ + + */ + public array $filters; + + /** + * @var array + */ + public array $sort; + + public function createCollection(): CollectionInterface + { + $collection = new PetCollection(); + $collection->setOffset($this->offset); + $collection->setLimit($this->limit); + $collection->setFilters($this->filters); + $collection->setSort($this->sort); + + return $collection; + } +} diff --git a/src/Dto/Collection/PetCollectionResponse.php b/src/Dto/Collection/PetCollectionResponse.php new file mode 100644 index 00000000..4488187e --- /dev/null +++ b/src/Dto/Collection/PetCollectionResponse.php @@ -0,0 +1,36 @@ + + */ + public array $filters; + + /** + * @var array + */ + public array $sort; + + /** + * @var array + */ + public array $items; + + public int $count; + + /** + * @var array + */ + public array $_links; +} diff --git a/src/Dto/LinkResponse.php b/src/Dto/LinkResponse.php new file mode 100644 index 00000000..097acab5 --- /dev/null +++ b/src/Dto/LinkResponse.php @@ -0,0 +1,15 @@ + + */ + public array $attributes; +} diff --git a/src/Dto/Model/ModelRequestInterface.php b/src/Dto/Model/ModelRequestInterface.php new file mode 100644 index 00000000..3f4d377f --- /dev/null +++ b/src/Dto/Model/ModelRequestInterface.php @@ -0,0 +1,14 @@ + + */ + public array $vaccinations; + + public function createModel(): ModelInterface + { + $vaccinations = []; + foreach ($this->vaccinations as $vaccinationRequest) { + $vaccination = new Vaccination(); + $vaccination->setName($vaccinationRequest->name); + + $vaccinations[] = $vaccination; + } + + $model = new Pet(); + $model->setName($this->name); + $model->setTag($this->tag); + $model->setVaccinations($vaccinations); + + return $model; + } + + /** + * @param Pet $model + */ + public function updateModel(ModelInterface $model): ModelInterface + { + $vaccinations = []; + foreach ($this->vaccinations as $vaccinationRequest) { + $vaccination = new Vaccination(); + $vaccination->setName($vaccinationRequest->name); + + $vaccinations[] = $vaccination; + } + + $model->setName($this->name); + $model->setTag($this->tag); + $model->setVaccinations($vaccinations); + + return $model; + } +} diff --git a/src/Dto/Model/PetResponse.php b/src/Dto/Model/PetResponse.php new file mode 100644 index 00000000..f1bbdc69 --- /dev/null +++ b/src/Dto/Model/PetResponse.php @@ -0,0 +1,30 @@ + + */ + public array $vaccinations; + + /** + * @var array + */ + public array $_links; +} diff --git a/src/Dto/Model/VaccinationRequest.php b/src/Dto/Model/VaccinationRequest.php new file mode 100644 index 00000000..5e298f5c --- /dev/null +++ b/src/Dto/Model/VaccinationRequest.php @@ -0,0 +1,10 @@ + + */ + public function enrichModel(ModelInterface $model): array + { + return [ + ...$model->jsonSerialize(), + '_links' => [ + 'read' => ['href' => $this->basePath.$model->getId(), 'attributes' => ['method' => 'GET']], + 'update' => ['href' => $this->basePath.$model->getId(), 'attributes' => ['method' => 'PUT']], + 'delete' => ['href' => $this->basePath.$model->getId(), 'attributes' => ['method' => 'DELETE']], + ], + ]; + } + + /** + * @return array + */ + public function enrichCollection(CollectionInterface $collection): array + { + $collectionData = $collection->jsonSerialize(); + + return [ + ...$collectionData, + 'items' => array_map(function (array $item) { + return [ + ...$item, + '_links' => [ + 'read' => ['href' => $this->basePath.$item['id'], 'attributes' => ['method' => 'GET']], + 'update' => ['href' => $this->basePath.$item['id'], 'attributes' => ['method' => 'PUT']], + 'delete' => ['href' => $this->basePath.$item['id'], 'attributes' => ['method' => 'DELETE']], + ], + ]; + }, $collectionData['items']), + '_links' => [ + 'create' => ['href' => $this->basePath, 'attributes' => ['method' => 'POST']], + ], + ]; + } +} diff --git a/src/Enrich/EnrichInterface.php b/src/Enrich/EnrichInterface.php new file mode 100644 index 00000000..8b4a3485 --- /dev/null +++ b/src/Enrich/EnrichInterface.php @@ -0,0 +1,21 @@ + + */ + public function enrichModel(ModelInterface $model): array; + + /** + * @return array + */ + public function enrichCollection(CollectionInterface $collection): array; +} diff --git a/src/Model/ModelInterface.php b/src/Model/ModelInterface.php index 3742af10..4c4dcd29 100644 --- a/src/Model/ModelInterface.php +++ b/src/Model/ModelInterface.php @@ -4,7 +4,7 @@ namespace App\Model; -interface ModelInterface +interface ModelInterface extends \JsonSerializable { public function getId(): string; diff --git a/src/Model/Pet.php b/src/Model/Pet.php index c341ec8d..8ce07efc 100644 --- a/src/Model/Pet.php +++ b/src/Model/Pet.php @@ -106,4 +106,21 @@ public function getVaccinations(): array { return $this->vaccinations->getValues(); } + + public function jsonSerialize(): array + { + $vaccinations = []; + foreach ($this->vaccinations as $vaccination) { + $vaccinations[] = $vaccination->jsonSerialize(); + } + + return [ + 'id' => $this->id, + 'createdAt' => $this->createdAt, + 'updatedAt' => $this->updatedAt, + 'name' => $this->name, + 'tag' => $this->tag, + 'vaccinations' => $vaccinations, + ]; + } } diff --git a/src/Model/Vaccination.php b/src/Model/Vaccination.php index a5087ea3..70fe1fe0 100644 --- a/src/Model/Vaccination.php +++ b/src/Model/Vaccination.php @@ -6,7 +6,7 @@ use Ramsey\Uuid\Uuid; -final class Vaccination +final class Vaccination implements \JsonSerializable { private string $id; @@ -38,4 +38,11 @@ public function setPet(?Pet $pet): void { $this->pet = $pet; } + + public function jsonSerialize(): array + { + return [ + 'name' => $this->name, + ]; + } } diff --git a/src/Parsing/ParsingInterface.php b/src/Parsing/ParsingInterface.php new file mode 100644 index 00000000..8f07b0c8 --- /dev/null +++ b/src/Parsing/ParsingInterface.php @@ -0,0 +1,14 @@ +parser; + + return $p->object([ + 'offset' => $p->union([$p->string()->default('0')->toInt(), $p->int()->default(0)]), + 'limit' => $p->union([$p->string()->default((string) CollectionInterface::LIMIT)->toInt(), $p->int()->default(CollectionInterface::LIMIT)]), + 'filters' => $p->record($p->string())->default([]), + 'sort' => $p->record($p->string())->default([]), + ], PetCollectionRequest::class); + } + + public function getResponeSchema(): SchemaInterface + { + $p = $this->parser; + + return $p->object([ + 'offset' => $p->int(), + 'limit' => $p->int(), + 'filters' => $p->record($p->string()), + 'sort' => $p->record($p->string()), + 'items' => $p->array($this->petParsing->getResponeSchema()), + 'count' => $p->int(), + '_links' => $p->record( + $p->object([ + 'href' => $p->string(), + 'attributes' => $p->array($p->string()), + ], LinkResponse::class) + ), + ], PetCollectionResponse::class); + } +} diff --git a/src/Parsing/PetParsing.php b/src/Parsing/PetParsing.php new file mode 100644 index 00000000..efafb5ea --- /dev/null +++ b/src/Parsing/PetParsing.php @@ -0,0 +1,53 @@ +parser; + + return $p->object([ + 'name' => $p->string(), + 'tag' => $p->string()->nullable(), + 'vaccinations' => $p->array($p->object([ + 'name' => $p->string(), + ], VaccinationRequest::class)), + ], PetRequest::class)->ignore(['id', 'createdAt', 'updatedAt', '_links']); + } + + public function getResponeSchema(): SchemaInterface + { + $p = $this->parser; + + return $p->object([ + 'id' => $p->string(), + 'createdAt' => $p->dateTime()->toString(), + 'updatedAt' => $p->dateTime()->nullable()->toString(), + 'name' => $p->string(), + 'tag' => $p->string()->nullable(), + 'vaccinations' => $p->array($p->object([ + 'name' => $p->string(), + ], VaccinationResponse::class)), + '_links' => $p->record( + $p->object([ + 'href' => $p->string(), + 'attributes' => $p->array($p->string()), + ], LinkResponse::class) + ), + ], PetResponse::class); + } +} diff --git a/src/RequestHandler/Api/Crud/CreateRequestHandler.php b/src/RequestHandler/Api/Crud/CreateRequestHandler.php index 820d7453..b63ec386 100644 --- a/src/RequestHandler/Api/Crud/CreateRequestHandler.php +++ b/src/RequestHandler/Api/Crud/CreateRequestHandler.php @@ -4,18 +4,15 @@ namespace App\RequestHandler\Api\Crud; -use App\Factory\ModelFactoryInterface; -use App\Model\ModelInterface; +use App\Dto\Model\ModelRequestInterface; +use App\Enrich\EnrichInterface; +use App\Parsing\ParsingInterface; use App\Repository\RepositoryInterface; -use Chubbyphp\ApiHttp\Manager\RequestManagerInterface; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; -use Chubbyphp\Deserialization\Denormalizer\DenormalizerContextBuilder; -use Chubbyphp\Deserialization\Denormalizer\DenormalizerContextInterface; +use Chubbyphp\DecodeEncode\Decoder\DecoderInterface; +use Chubbyphp\DecodeEncode\Encoder\EncoderInterface; use Chubbyphp\HttpException\HttpException; -use Chubbyphp\Serialization\Normalizer\NormalizerContextBuilder; -use Chubbyphp\Serialization\Normalizer\NormalizerContextInterface; -use Chubbyphp\Validation\Error\ApiProblemErrorMessages; -use Chubbyphp\Validation\ValidatorInterface; +use Chubbyphp\Parsing\ParserErrorException; +use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; @@ -23,11 +20,12 @@ final class CreateRequestHandler implements RequestHandlerInterface { public function __construct( - private ModelFactoryInterface $factory, + private DecoderInterface $decoder, + private ParsingInterface $parsing, private RepositoryInterface $repository, - private RequestManagerInterface $requestManager, - private ResponseManagerInterface $responseManager, - private ValidatorInterface $validator + private EnrichInterface $enrich, + private EncoderInterface $encoder, + private ResponseFactoryInterface $responseFactory, ) {} public function handle(ServerRequestInterface $request): ResponseInterface @@ -35,31 +33,28 @@ public function handle(ServerRequestInterface $request): ResponseInterface $accept = $request->getAttribute('accept'); $contentType = $request->getAttribute('contentType'); - /** @var ModelInterface $model */ - $model = $this->requestManager->getDataFromRequestBody( - $request, - $this->factory->create(), - $contentType, - $this->getDenormalizerContext() - ); + $input = $this->decoder->decode((string) $request->getBody(), $contentType); - if ([] !== $errors = $this->validator->validate($model)) { - throw HttpException::createUnprocessableEntity(['invalidParameters' => (new ApiProblemErrorMessages($errors))->getMessages()]); + try { + /** @var ModelRequestInterface $modelRequest */ + $modelRequest = $this->parsing->getRequestSchema()->parse($input); + } catch (ParserErrorException $e) { + throw HttpException::createUnprocessableEntity(['invalidParameters' => $e->getApiProblemErrorMessages()]); } + $model = $modelRequest->createModel(); + $this->repository->persist($model); $this->repository->flush(); - return $this->responseManager->create($model, $accept, 201, $this->getNormalizerContext($request)); - } + $output = $this->encoder->encode( + (array) $this->parsing->getResponeSchema()->parse($this->enrich->enrichModel($model)), + $accept + ); - private function getDenormalizerContext(): DenormalizerContextInterface - { - return DenormalizerContextBuilder::create()->setClearMissing(true)->getContext(); - } + $response = $this->responseFactory->createResponse(201)->withHeader('Content-Type', $accept); + $response->getBody()->write($output); - private function getNormalizerContext(ServerRequestInterface $request): NormalizerContextInterface - { - return NormalizerContextBuilder::create()->setRequest($request)->getContext(); + return $response; } } diff --git a/src/RequestHandler/Api/Crud/ListRequestHandler.php b/src/RequestHandler/Api/Crud/ListRequestHandler.php index 66ac1367..65297b94 100644 --- a/src/RequestHandler/Api/Crud/ListRequestHandler.php +++ b/src/RequestHandler/Api/Crud/ListRequestHandler.php @@ -4,15 +4,15 @@ namespace App\RequestHandler\Api\Crud; -use App\Collection\CollectionInterface; -use App\Factory\CollectionFactoryInterface; +use App\Dto\Collection\CollectionRequestInterface; +use App\Enrich\EnrichInterface; +use App\Parsing\ParsingInterface; use App\Repository\RepositoryInterface; -use Chubbyphp\ApiHttp\Manager\RequestManagerInterface; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; +use Chubbyphp\DecodeEncode\Decoder\DecoderInterface; +use Chubbyphp\DecodeEncode\Encoder\EncoderInterface; use Chubbyphp\HttpException\HttpException; -use Chubbyphp\Serialization\Normalizer\NormalizerContextBuilder; -use Chubbyphp\Validation\Error\ApiProblemErrorMessages; -use Chubbyphp\Validation\ValidatorInterface; +use Chubbyphp\Parsing\ParserErrorException; +use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; @@ -20,30 +20,39 @@ final class ListRequestHandler implements RequestHandlerInterface { public function __construct( - private CollectionFactoryInterface $factory, + private DecoderInterface $decoder, + private ParsingInterface $parsing, private RepositoryInterface $repository, - private RequestManagerInterface $requestManager, - private ResponseManagerInterface $responseManager, - private ValidatorInterface $validator + private EnrichInterface $enrich, + private EncoderInterface $encoder, + private ResponseFactoryInterface $responseFactory, ) {} public function handle(ServerRequestInterface $request): ResponseInterface { $accept = $request->getAttribute('accept'); - /** @var CollectionInterface $collection */ - $collection = $this->requestManager->getDataFromRequestQuery($request, $this->factory->create()); + $input = $request->getQueryParams(); - if ([] !== $errors = $this->validator->validate($collection)) { - throw HttpException::createBadRequest([ - 'invalidParameters' => (new ApiProblemErrorMessages($errors))->getMessages(), - ]); + try { + /** @var CollectionRequestInterface $collectionRequest */ + $collectionRequest = $this->parsing->getRequestSchema()->parse($input); + } catch (ParserErrorException $e) { + throw HttpException::createBadRequest(['invalidParameters' => $e->getApiProblemErrorMessages()]); } + $collection = $collectionRequest->createCollection(); + $this->repository->resolveCollection($collection); - $context = NormalizerContextBuilder::create()->setRequest($request)->getContext(); + $output = $this->encoder->encode( + (array) $this->parsing->getResponeSchema()->parse($this->enrich->enrichCollection($collection)), + $accept + ); + + $response = $this->responseFactory->createResponse(200)->withHeader('Content-Type', $accept); + $response->getBody()->write($output); - return $this->responseManager->create($collection, $accept, 200, $context); + return $response; } } diff --git a/src/RequestHandler/Api/Crud/ReadRequestHandler.php b/src/RequestHandler/Api/Crud/ReadRequestHandler.php index 3c4cc671..74b0f5ed 100644 --- a/src/RequestHandler/Api/Crud/ReadRequestHandler.php +++ b/src/RequestHandler/Api/Crud/ReadRequestHandler.php @@ -4,11 +4,14 @@ namespace App\RequestHandler\Api\Crud; +use App\Enrich\EnrichInterface; +use App\Parsing\ParsingInterface; use App\Repository\RepositoryInterface; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; +use Chubbyphp\DecodeEncode\Encoder\EncoderInterface; use Chubbyphp\HttpException\HttpException; use Chubbyphp\Serialization\Normalizer\NormalizerContextBuilder; use Chubbyphp\Serialization\Normalizer\NormalizerContextInterface; +use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; @@ -17,8 +20,11 @@ final class ReadRequestHandler implements RequestHandlerInterface { public function __construct( + private ParsingInterface $parsing, private RepositoryInterface $repository, - private ResponseManagerInterface $responseManager + private EnrichInterface $enrich, + private EncoderInterface $encoder, + private ResponseFactoryInterface $responseFactory, ) {} public function handle(ServerRequestInterface $request): ResponseInterface @@ -30,7 +36,15 @@ public function handle(ServerRequestInterface $request): ResponseInterface throw HttpException::createNotFound(); } - return $this->responseManager->create($model, $accept, 200, $this->getNormalizerContext($request)); + $output = $this->encoder->encode( + (array) $this->parsing->getResponeSchema()->parse($this->enrich->enrichModel($model)), + $accept + ); + + $response = $this->responseFactory->createResponse(200)->withHeader('Content-Type', $accept); + $response->getBody()->write($output); + + return $response; } private function getNormalizerContext(ServerRequestInterface $request): NormalizerContextInterface diff --git a/src/RequestHandler/Api/Crud/UpdateRequestHandler.php b/src/RequestHandler/Api/Crud/UpdateRequestHandler.php index 6690580f..026935c0 100644 --- a/src/RequestHandler/Api/Crud/UpdateRequestHandler.php +++ b/src/RequestHandler/Api/Crud/UpdateRequestHandler.php @@ -4,17 +4,15 @@ namespace App\RequestHandler\Api\Crud; -use App\Model\ModelInterface; +use App\Dto\ModelRequestInterface; +use App\Enrich\EnrichInterface; +use App\Parsing\ParsingInterface; use App\Repository\RepositoryInterface; -use Chubbyphp\ApiHttp\Manager\RequestManagerInterface; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; -use Chubbyphp\Deserialization\Denormalizer\DenormalizerContextBuilder; -use Chubbyphp\Deserialization\Denormalizer\DenormalizerContextInterface; +use Chubbyphp\DecodeEncode\Decoder\DecoderInterface; +use Chubbyphp\DecodeEncode\Encoder\EncoderInterface; use Chubbyphp\HttpException\HttpException; -use Chubbyphp\Serialization\Normalizer\NormalizerContextBuilder; -use Chubbyphp\Serialization\Normalizer\NormalizerContextInterface; -use Chubbyphp\Validation\Error\ApiProblemErrorMessages; -use Chubbyphp\Validation\ValidatorInterface; +use Chubbyphp\Parsing\ParserErrorException; +use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; @@ -23,10 +21,12 @@ final class UpdateRequestHandler implements RequestHandlerInterface { public function __construct( + private DecoderInterface $decoder, + private ParsingInterface $parsing, private RepositoryInterface $repository, - private RequestManagerInterface $requestManager, - private ResponseManagerInterface $responseManager, - private ValidatorInterface $validator + private EnrichInterface $enrich, + private EncoderInterface $encoder, + private ResponseFactoryInterface $responseFactory, ) {} public function handle(ServerRequestInterface $request): ResponseInterface @@ -39,39 +39,28 @@ public function handle(ServerRequestInterface $request): ResponseInterface throw HttpException::createNotFound(); } - /** @var ModelInterface $model */ - $model = $this->requestManager->getDataFromRequestBody( - $request, - $model, - $contentType, - $this->getDenormalizerContext() - ); + $input = $this->decoder->decode((string) $request->getBody(), $contentType); - if ([] !== $errors = $this->validator->validate($model)) { - throw HttpException::createUnprocessableEntity([ - 'invalidParameters' => (new ApiProblemErrorMessages($errors))->getMessages(), - ]); + try { + /** @var ModelRequestInterface $modelRequest */ + $modelRequest = $this->parsing->getRequestSchema()->parse($input); + } catch (ParserErrorException $e) { + throw HttpException::createUnprocessableEntity(['invalidParameters' => $e->getApiProblemErrorMessages()]); } - $model->setUpdatedAt(new \DateTimeImmutable()); + $model = $modelRequest->updateModel($model); $this->repository->persist($model); $this->repository->flush(); - return $this->responseManager->create($model, $accept, 200, $this->getNormalizerContext($request)); - } + $output = $this->encoder->encode( + (array) $this->parsing->getResponeSchema()->parse($this->enrich->enrichModel($model)), + $accept + ); - private function getDenormalizerContext(): DenormalizerContextInterface - { - return DenormalizerContextBuilder::create() - ->setAllowedAdditionalFields(['id', 'createdAt', 'updatedAt', '_links']) - ->setClearMissing(true) - ->getContext() - ; - } + $response = $this->responseFactory->createResponse(200)->withHeader('Content-Type', $accept); + $response->getBody()->write($output); - private function getNormalizerContext(ServerRequestInterface $request): NormalizerContextInterface - { - return NormalizerContextBuilder::create()->setRequest($request)->getContext(); + return $response; } } diff --git a/src/ServiceFactory/Enrich/PetEnrichFactory.php b/src/ServiceFactory/Enrich/PetEnrichFactory.php new file mode 100644 index 00000000..9c531ba0 --- /dev/null +++ b/src/ServiceFactory/Enrich/PetEnrichFactory.php @@ -0,0 +1,18 @@ +getRoutes() ); } diff --git a/src/ServiceFactory/Parsing/ParserFactory.php b/src/ServiceFactory/Parsing/ParserFactory.php new file mode 100644 index 00000000..2e52462c --- /dev/null +++ b/src/ServiceFactory/Parsing/ParserFactory.php @@ -0,0 +1,18 @@ +resolveDependency($container, Parser::class, ParserFactory::class); + + /** @var PetParsing $petParsing */ + $petParsing = $this->resolveDependency($container, PetParsing::class, PetParsingFactory::class); + + return new PetCollectionParsing($parser, $petParsing); + } +} diff --git a/src/ServiceFactory/Parsing/PetParsingFactory.php b/src/ServiceFactory/Parsing/PetParsingFactory.php new file mode 100644 index 00000000..a714eafc --- /dev/null +++ b/src/ServiceFactory/Parsing/PetParsingFactory.php @@ -0,0 +1,21 @@ +resolveDependency($container, Parser::class, ParserFactory::class); + + return new PetParsing($parser); + } +} diff --git a/src/ServiceFactory/RequestHandler/Api/Crud/PetCreateRequestHandlerFactory.php b/src/ServiceFactory/RequestHandler/Api/Crud/PetCreateRequestHandlerFactory.php index 1e83dc35..a4340aac 100644 --- a/src/ServiceFactory/RequestHandler/Api/Crud/PetCreateRequestHandlerFactory.php +++ b/src/ServiceFactory/RequestHandler/Api/Crud/PetCreateRequestHandlerFactory.php @@ -4,24 +4,27 @@ namespace App\ServiceFactory\RequestHandler\Api\Crud; -use App\Factory\Model\PetFactory; +use App\Enrich\EnrichInterface; +use App\Model\Pet; +use App\Parsing\PetParsing; use App\Repository\PetRepository; use App\RequestHandler\Api\Crud\CreateRequestHandler; -use Chubbyphp\ApiHttp\Manager\RequestManagerInterface; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; -use Chubbyphp\Validation\ValidatorInterface; +use Chubbyphp\DecodeEncode\Decoder\DecoderInterface; +use Chubbyphp\DecodeEncode\Encoder\EncoderInterface; use Psr\Container\ContainerInterface; +use Psr\Http\Message\ResponseFactoryInterface; final class PetCreateRequestHandlerFactory { public function __invoke(ContainerInterface $container): CreateRequestHandler { return new CreateRequestHandler( - $container->get(PetFactory::class), + $container->get(DecoderInterface::class), + $container->get(PetParsing::class), $container->get(PetRepository::class), - $container->get(RequestManagerInterface::class), - $container->get(ResponseManagerInterface::class), - $container->get(ValidatorInterface::class) + $container->get(Pet::class.EnrichInterface::class), + $container->get(EncoderInterface::class), + $container->get(ResponseFactoryInterface::class), ); } } diff --git a/src/ServiceFactory/RequestHandler/Api/Crud/PetListRequestHandlerFactory.php b/src/ServiceFactory/RequestHandler/Api/Crud/PetListRequestHandlerFactory.php index ec68b929..909d7d8d 100644 --- a/src/ServiceFactory/RequestHandler/Api/Crud/PetListRequestHandlerFactory.php +++ b/src/ServiceFactory/RequestHandler/Api/Crud/PetListRequestHandlerFactory.php @@ -4,24 +4,27 @@ namespace App\ServiceFactory\RequestHandler\Api\Crud; -use App\Factory\Collection\PetCollectionFactory; +use App\Enrich\EnrichInterface; +use App\Model\Pet; +use App\Parsing\PetCollectionParsing; use App\Repository\PetRepository; use App\RequestHandler\Api\Crud\ListRequestHandler; -use Chubbyphp\ApiHttp\Manager\RequestManagerInterface; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; -use Chubbyphp\Validation\ValidatorInterface; +use Chubbyphp\DecodeEncode\Decoder\DecoderInterface; +use Chubbyphp\DecodeEncode\Encoder\EncoderInterface; use Psr\Container\ContainerInterface; +use Psr\Http\Message\ResponseFactoryInterface; final class PetListRequestHandlerFactory { public function __invoke(ContainerInterface $container): ListRequestHandler { return new ListRequestHandler( - $container->get(PetCollectionFactory::class), + $container->get(DecoderInterface::class), + $container->get(PetCollectionParsing::class), $container->get(PetRepository::class), - $container->get(RequestManagerInterface::class), - $container->get(ResponseManagerInterface::class), - $container->get(ValidatorInterface::class) + $container->get(Pet::class.EnrichInterface::class), + $container->get(EncoderInterface::class), + $container->get(ResponseFactoryInterface::class), ); } } diff --git a/src/ServiceFactory/RequestHandler/Api/Crud/PetReadRequestHandlerFactory.php b/src/ServiceFactory/RequestHandler/Api/Crud/PetReadRequestHandlerFactory.php index 4ce4666c..47c9ee55 100644 --- a/src/ServiceFactory/RequestHandler/Api/Crud/PetReadRequestHandlerFactory.php +++ b/src/ServiceFactory/RequestHandler/Api/Crud/PetReadRequestHandlerFactory.php @@ -4,18 +4,25 @@ namespace App\ServiceFactory\RequestHandler\Api\Crud; +use App\Enrich\EnrichInterface; +use App\Model\Pet; +use App\Parsing\PetParsing; use App\Repository\PetRepository; use App\RequestHandler\Api\Crud\ReadRequestHandler; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; +use Chubbyphp\DecodeEncode\Encoder\EncoderInterface; use Psr\Container\ContainerInterface; +use Psr\Http\Message\ResponseFactoryInterface; final class PetReadRequestHandlerFactory { public function __invoke(ContainerInterface $container): ReadRequestHandler { return new ReadRequestHandler( + $container->get(PetParsing::class), $container->get(PetRepository::class), - $container->get(ResponseManagerInterface::class) + $container->get(Pet::class.EnrichInterface::class), + $container->get(EncoderInterface::class), + $container->get(ResponseFactoryInterface::class), ); } } diff --git a/src/ServiceFactory/RequestHandler/Api/Crud/PetUpdateRequestHandlerFactory.php b/src/ServiceFactory/RequestHandler/Api/Crud/PetUpdateRequestHandlerFactory.php index b092ab8e..ab6486ad 100644 --- a/src/ServiceFactory/RequestHandler/Api/Crud/PetUpdateRequestHandlerFactory.php +++ b/src/ServiceFactory/RequestHandler/Api/Crud/PetUpdateRequestHandlerFactory.php @@ -4,22 +4,27 @@ namespace App\ServiceFactory\RequestHandler\Api\Crud; +use App\Enrich\EnrichInterface; +use App\Model\Pet; +use App\Parsing\PetParsing; use App\Repository\PetRepository; use App\RequestHandler\Api\Crud\UpdateRequestHandler; -use Chubbyphp\ApiHttp\Manager\RequestManagerInterface; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; -use Chubbyphp\Validation\ValidatorInterface; +use Chubbyphp\DecodeEncode\Decoder\DecoderInterface; +use Chubbyphp\DecodeEncode\Encoder\EncoderInterface; use Psr\Container\ContainerInterface; +use Psr\Http\Message\ResponseFactoryInterface; final class PetUpdateRequestHandlerFactory { public function __invoke(ContainerInterface $container): UpdateRequestHandler { return new UpdateRequestHandler( + $container->get(DecoderInterface::class), + $container->get(PetParsing::class), $container->get(PetRepository::class), - $container->get(RequestManagerInterface::class), - $container->get(ResponseManagerInterface::class), - $container->get(ValidatorInterface::class) + $container->get(Pet::class.EnrichInterface::class), + $container->get(EncoderInterface::class), + $container->get(ResponseFactoryInterface::class), ); } }