diff --git a/config/api_platform/User/Order.yaml b/config/api_platform/User/Order.yaml new file mode 100644 index 0000000..e504a34 --- /dev/null +++ b/config/api_platform/User/Order.yaml @@ -0,0 +1,36 @@ +Acme\Domain\Order\Order: + properties: + uuid: + identifier: true + collectionOperations: + create_order: + method: POST + path: '/orders' + messenger: true + input: Acme\Application\UseCase\Command\Order\CreateOrder\CreateOrderInput + output: Acme\UI\Http\Rest\Presentation\Order\OrderView + normalization_context: + groups: [ order ] + itemOperations: + update_order: + method: PUT + path: '/orders/{uuid}' + status: 202 + messenger: true + input: Acme\Application\UseCase\Command\Order\UpdateOrder\UpdateOrderInput + output: false + read: false + openapi_context: + summary: Update order async + parameters: + - in: path + name: uuid + type: string + required: true + responses: + 202: + description: Order updating in process + 400: + description: Invalid input + 409: + description: Conflict diff --git a/config/api_platform/User/User.yaml b/config/api_platform/User/User.yaml index efbe90b..3bb6fef 100644 --- a/config/api_platform/User/User.yaml +++ b/config/api_platform/User/User.yaml @@ -6,14 +6,14 @@ Acme\Domain\User\User: get: method: GET filters: [ 'user.search_filter' ] - output: Acme\UI\Http\Rest\Presentation\User\UserView - normalization_context: - groups: [ profile ] - get_v2: - path: v2/users - method: GET - query: Acme\Application\UseCase\Query\User\GetUsers\GetUsersQuery - filters: [ 'user.search_filter' ] + output: Acme\UI\Http\Rest\Presentation\User\UserView + normalization_context: + groups: [ profile ] + get_v2: + path: v2/users + method: GET + query: Acme\Application\UseCase\Query\User\GetUsers\GetUsersQuery + filters: [ 'user.search_filter' ] output: Acme\UI\Http\Rest\Presentation\User\UserProfileView post: method: POST diff --git a/config/packages/doctrine/orm/mapping/order.yaml b/config/packages/doctrine/orm/mapping/order.yaml new file mode 100644 index 0000000..0f8db3c --- /dev/null +++ b/config/packages/doctrine/orm/mapping/order.yaml @@ -0,0 +1,9 @@ +doctrine: + orm: + mappings: + Order: + is_bundle: false + type: xml + dir: '%kernel.project_dir%/src/Infrastructure/Order/Doctrine/Orm/Mapping' + prefix: 'Acme\Domain\Order' + alias: Order diff --git a/config/packages/security.yaml b/config/packages/security.yaml index 487f58b..a41ca87 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -36,6 +36,11 @@ security: stateless: true anonymous: true + api_orders: + pattern: ^/api/orders + stateless: true + anonymous: true + api_secured: pattern: ^/api provider: users @@ -67,4 +72,5 @@ security: - { path: ^/api/auth, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/api/signup, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/api/doc, roles: IS_AUTHENTICATED_ANONYMOUSLY } + - { path: ^/api/orders, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/api/, roles: IS_AUTHENTICATED_FULLY } diff --git a/src/Application/UseCase/Command/Order/CreateOrder/CreateOrderCommand.php b/src/Application/UseCase/Command/Order/CreateOrder/CreateOrderCommand.php new file mode 100644 index 0000000..ac29cf7 --- /dev/null +++ b/src/Application/UseCase/Command/Order/CreateOrder/CreateOrderCommand.php @@ -0,0 +1,36 @@ +number = $number; + $this->state = $state; + $this->total = $total; + } + + public function getNumber(): string + { + return $this->number; + } + + public function getState(): string + { + return $this->state; + } + + public function getTotal(): int + { + return $this->total; + } +} diff --git a/src/Application/UseCase/Command/Order/CreateOrder/CreateOrderCommandHandler.php b/src/Application/UseCase/Command/Order/CreateOrder/CreateOrderCommandHandler.php new file mode 100644 index 0000000..5d19006 --- /dev/null +++ b/src/Application/UseCase/Command/Order/CreateOrder/CreateOrderCommandHandler.php @@ -0,0 +1,30 @@ +orderStore = $orderStore; + } + + public function __invoke(CreateOrderCommand $command) + { + $order = Order::create($command->getNumber(), $command->getState(), $command->getTotal()); + + $this->orderStore->store($order); + + /** @TODO Think about this line because it breaks DDD */ + return OrderView::create($order); + } +} diff --git a/src/Application/UseCase/Command/Order/CreateOrder/CreateOrderDataTransformer.php b/src/Application/UseCase/Command/Order/CreateOrder/CreateOrderDataTransformer.php new file mode 100644 index 0000000..64253db --- /dev/null +++ b/src/Application/UseCase/Command/Order/CreateOrder/CreateOrderDataTransformer.php @@ -0,0 +1,38 @@ +validator = $validator; + } + + public function transform($object, string $to, array $context = []) + { + if (!$object instanceof CreateOrderInput) { + throw new \InvalidArgumentException(\sprintf('Object is not an instance of %s', CreateOrderInput::class)); + } + + $this->validator->validate($object, $context); + + return new CreateOrderCommand( + $object->number, + $object->state, + $object->total + ); + } + + public function supportsTransformation($data, string $to, array $context = []): bool + { + return CreateOrderInput::class === ($context['input']['class'] ?? null); + } +} diff --git a/src/Application/UseCase/Command/Order/CreateOrder/CreateOrderInput.php b/src/Application/UseCase/Command/Order/CreateOrder/CreateOrderInput.php new file mode 100644 index 0000000..fc98525 --- /dev/null +++ b/src/Application/UseCase/Command/Order/CreateOrder/CreateOrderInput.php @@ -0,0 +1,28 @@ +uuid; + } + + public function setUuid(UuidInterface $uuid): void + { + $this->uuid = $uuid; + } + + public function getNumber(): string + { + return $this->number; + } + + public function setNumber(string $number): void + { + $this->number = $number; + } + + public function getState(): string + { + return $this->state; + } + + public function setState(string $state): void + { + $this->state = $state; + } + + public function getTotal(): int + { + return $this->total; + } + + public function setTotal(int $total): void + { + $this->total = $total; + } +} diff --git a/src/Application/UseCase/Command/Order/UpdateOrder/UpdateOrderCommandHandler.php b/src/Application/UseCase/Command/Order/UpdateOrder/UpdateOrderCommandHandler.php new file mode 100644 index 0000000..cdf35e0 --- /dev/null +++ b/src/Application/UseCase/Command/Order/UpdateOrder/UpdateOrderCommandHandler.php @@ -0,0 +1,37 @@ +orderStore = $orderStore; + } + + public function __invoke(UpdateOrderCommand $command) + { + try { + $order = $this->orderStore->find($command->getUuid()); + } catch (NotFoundException $e) { + throw new NotFoundHttpException($e->getMessage()); + } + + $order->setNumber($command->getNumber()); + $order->setState($command->getState()); + $order->setTotal($command->getTotal()); + $order->setUpdatedAt(DateTime::now()); + + $this->orderStore->store($order); + } +} diff --git a/src/Application/UseCase/Command/Order/UpdateOrder/UpdateOrderDataTransformer.php b/src/Application/UseCase/Command/Order/UpdateOrder/UpdateOrderDataTransformer.php new file mode 100644 index 0000000..d6bf8d7 --- /dev/null +++ b/src/Application/UseCase/Command/Order/UpdateOrder/UpdateOrderDataTransformer.php @@ -0,0 +1,50 @@ +validator = $validator; + } + + public function transform($object, string $to, array $context = []) + { + if (!$object instanceof UpdateOrderInput) { + throw new \InvalidArgumentException(\sprintf('Object is not an instance of %s', UpdateOrderInput::class)); + } + + if (!isset($context['uuid'])) { + throw new \RuntimeException(\sprintf('Missing uuid value in context')); + } + + if (($uuid = $context['uuid']) && !$uuid instanceof UuidInterface) { + throw new \InvalidArgumentException(\sprintf('Given uuid must be an instance of %s', UuidInterface::class)); + } + + $this->validator->validate($object, $context); + + $command = new UpdateOrderCommand(); + $command->setUuid($uuid); + $command->setNumber($object->number); + $command->setState($object->state); + $command->setTotal($object->total); + + return $command; + } + + public function supportsTransformation($data, string $to, array $context = []): bool + { + return UpdateOrderInput::class === ($context['input']['class'] ?? null); + } +} diff --git a/src/Application/UseCase/Command/Order/UpdateOrder/UpdateOrderInput.php b/src/Application/UseCase/Command/Order/UpdateOrder/UpdateOrderInput.php new file mode 100644 index 0000000..22f1efb --- /dev/null +++ b/src/Application/UseCase/Command/Order/UpdateOrder/UpdateOrderInput.php @@ -0,0 +1,11 @@ +setUuid(Uuid::uuid4()); + $order->setNumber($number); + $order->setState($state); + $order->setTotal($total); + $order->setCreatedAt(DateTime::now()); + + return $order; + } + + public function setNumber(string $number): void + { + $this->number = $number; + } + + public function getNumber(): string + { + return $this->number; + } + + public function setTotal(int $total): void + { + $this->total = $total; + } + + public function getTotal(): int + { + return $this->total; + } + + public function setState(string $state): void + { + $this->state = $state; + } + + public function getState(): string + { + return $this->state; + } + + public function setCreatedAt(DateTime $createdAt): void + { + $this->createdAt = $createdAt; + } + + public function setUpdatedAt(DateTime $updatedAt): void + { + $this->updatedAt = $updatedAt; + } +} diff --git a/src/Domain/Order/Repository/OrderRepositoryInterface.php b/src/Domain/Order/Repository/OrderRepositoryInterface.php new file mode 100644 index 0000000..165b769 --- /dev/null +++ b/src/Domain/Order/Repository/OrderRepositoryInterface.php @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/src/Infrastructure/Order/Repository/OrderStore.php b/src/Infrastructure/Order/Repository/OrderStore.php new file mode 100644 index 0000000..c6b64b5 --- /dev/null +++ b/src/Infrastructure/Order/Repository/OrderStore.php @@ -0,0 +1,37 @@ +oneByIdOrException($uuid->getBytes()); + } + + public function store(Order $order): void + { + $this->register($order); + } +} diff --git a/src/Infrastructure/Shared/Migration/2020/09/Version20200929135231.php b/src/Infrastructure/Shared/Migration/2020/09/Version20200929135231.php new file mode 100644 index 0000000..22bf970 --- /dev/null +++ b/src/Infrastructure/Shared/Migration/2020/09/Version20200929135231.php @@ -0,0 +1,31 @@ +addSql('CREATE TABLE orders (uuid BINARY(16) NOT NULL COMMENT \'(DC2Type:uuid_binary)\', number VARCHAR(255) NOT NULL, state VARCHAR(255) NOT NULL, total INT NOT NULL, created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', updated_at DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\', PRIMARY KEY(uuid)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + } + + public function down(Schema $schema) : void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP TABLE orders'); + } +} diff --git a/src/UI/Http/Rest/Presentation/Order/OrderView.php b/src/UI/Http/Rest/Presentation/Order/OrderView.php new file mode 100644 index 0000000..5732787 --- /dev/null +++ b/src/UI/Http/Rest/Presentation/Order/OrderView.php @@ -0,0 +1,47 @@ +id = $order->getAggregateRootId(); + $view->number = $order->getNumber(); + $view->state = $order->getState(); + $view->total = $order->getTotal(); + + return $view; + } +}