From faf86682e8a8ee33d31b97d7969c5eb13fad93e9 Mon Sep 17 00:00:00 2001 From: Arman <407448+armanist@users.noreply.github.com> Date: Wed, 1 Apr 2026 21:25:22 +0400 Subject: [PATCH 1/2] Refactor Services and CLI commands to use DTOs --- shared/Commands/CommentCreateCommand.php | 13 +- shared/Commands/PostCreateCommand.php | 16 +- shared/Commands/PostUpdateCommand.php | 15 +- shared/Commands/UserCreateCommand.php | 21 ++- shared/DTOs/CommentDTO.php | 92 ++++++++++ shared/DTOs/PostDTO.php | 113 +++++++++++++ shared/DTOs/UserDTO.php | 158 ++++++++++++++++++ shared/Services/CommentService.php | 9 +- shared/Services/PostService.php | 17 +- tests/Helpers/functions.php | 25 +-- .../shared/Services/CommentServiceTest.php | 23 ++- .../Unit/shared/Services/PostServiceTest.php | 50 +++--- 12 files changed, 463 insertions(+), 89 deletions(-) create mode 100644 shared/DTOs/CommentDTO.php create mode 100644 shared/DTOs/PostDTO.php create mode 100644 shared/DTOs/UserDTO.php diff --git a/shared/Commands/CommentCreateCommand.php b/shared/Commands/CommentCreateCommand.php index 1d24a39..b56545f 100644 --- a/shared/Commands/CommentCreateCommand.php +++ b/shared/Commands/CommentCreateCommand.php @@ -20,6 +20,7 @@ use Shared\Services\CommentService; use Quantum\Console\QtCommand; use Quantum\Validation\Rule; +use Shared\DTOs\CommentDTO; use ReflectionException; /** @@ -76,11 +77,13 @@ public function exec(): void return; } - service(CommentService::class)->addComment([ - 'post_uuid' => $this->getArgument('post_uuid'), - 'user_uuid' => $this->getArgument('user_uuid'), - 'content' => $this->getArgument('content'), - ]); + $commentDto = new CommentDTO( + $this->getArgument('post_uuid'), + $this->getArgument('user_uuid'), + $this->getArgument('content') + ); + + service(CommentService::class)->addComment($commentDto); $this->info('Comment created successfully'); } diff --git a/shared/Commands/PostCreateCommand.php b/shared/Commands/PostCreateCommand.php index d16503f..647233f 100644 --- a/shared/Commands/PostCreateCommand.php +++ b/shared/Commands/PostCreateCommand.php @@ -20,6 +20,7 @@ use Shared\Services\PostService; use Quantum\Console\QtCommand; use Quantum\Validation\Rule; +use Shared\DTOs\PostDTO; use ReflectionException; /** @@ -79,13 +80,14 @@ public function exec(): void return; } - service(PostService::class)->addPost([ - 'uuid' => $this->getArgument('uuid'), - 'user_uuid' => $this->getArgument('user_uuid'), - 'title' => $this->getArgument('title'), - 'content' => $this->getArgument('description'), - 'image' => $this->getArgument('image'), - ]); + $postDto = new PostDTO( + $this->getArgument('title'), + $this->getArgument('description'), + $this->getArgument('user_uuid'), + $this->getArgument('image') ?? '' + ); + + service(PostService::class)->addPost($postDto); $this->info('Post created successfully'); } diff --git a/shared/Commands/PostUpdateCommand.php b/shared/Commands/PostUpdateCommand.php index fca27e9..dcaf540 100644 --- a/shared/Commands/PostUpdateCommand.php +++ b/shared/Commands/PostUpdateCommand.php @@ -20,6 +20,7 @@ use Shared\Services\PostService; use Quantum\Console\QtCommand; use Quantum\Validation\Rule; +use Shared\DTOs\PostDTO; use ReflectionException; /** @@ -96,14 +97,14 @@ public function exec(): void return; } - $postData = [ - 'title' => $this->getOption('title') ?: $post->title, - 'content' => $this->getOption('description') ?: $post->content, - 'image' => $this->getOption('image') ?: $post->image ?? '', - 'updated_at' => date('Y-m-d H:i:s') - ]; + $postDto = new PostDTO( + $this->getOption('title') ?: $post->title, + $this->getOption('description') ?: $post->content, + null, + $this->getOption('image') ?: $post->image ?? '' + ); - $postService->updatePost($postId, $postData); + $postService->updatePost($postId, $postDto); $this->info('Post updated successfully'); } diff --git a/shared/Commands/UserCreateCommand.php b/shared/Commands/UserCreateCommand.php index 2373672..bc114fd 100644 --- a/shared/Commands/UserCreateCommand.php +++ b/shared/Commands/UserCreateCommand.php @@ -22,6 +22,7 @@ use Quantum\Console\QtCommand; use Quantum\Validation\Rule; use Quantum\Hasher\Hasher; +use Shared\DTOs\UserDTO; use ReflectionException; use Shared\Models\User; @@ -88,15 +89,17 @@ public function exec(): void return; } - service(AuthService::class)->add([ - 'uuid' => $this->getArgument('uuid'), - 'firstname' => $this->getArgument('firstname'), - 'lastname' => $this->getArgument('lastname'), - 'role' => $this->getArgument('role'), - 'email' => $this->getArgument('email'), - 'password' => (new Hasher())->hash($this->getArgument('password')), - 'image' => $this->getArgument('image'), - ]); + $userDto = new UserDTO( + $this->getArgument('email'), + (new Hasher())->hash($this->getArgument('password')), + $this->getArgument('firstname'), + $this->getArgument('lastname'), + $this->getArgument('role') ?? '', + $this->getArgument('uuid'), + $this->getArgument('image') ?? '' + ); + + service(AuthService::class)->add($userDto->toArray()); $this->info('User created successfully'); } diff --git a/shared/DTOs/CommentDTO.php b/shared/DTOs/CommentDTO.php new file mode 100644 index 0000000..8d4d3c9 --- /dev/null +++ b/shared/DTOs/CommentDTO.php @@ -0,0 +1,92 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 3.0.0 + */ + +namespace Shared\DTOs; + +use Quantum\Http\Request; + +/** + * Class CommentDTO + * @package Shared\DTOs + */ +class CommentDTO +{ + /** + * @var string + */ + private string $postUuid; + + /** + * @var string + */ + private string $userUuid; + + /** + * @var string + */ + private string $content; + + /** + * @param string $postUuid + * @param string $userUuid + * @param string $content + */ + public function __construct( + string $postUuid, + string $userUuid, + string $content + ) { + $this->postUuid = $postUuid; + $this->userUuid = $userUuid; + $this->content = $content; + } + + /** + * @param Request $request + * @param string $postUuid + * @param string $userUuid + * @return self + */ + public static function fromRequest(Request $request, string $postUuid, string $userUuid): self + { + return new self($postUuid, $userUuid, trim((string)$request->get('content'))); + } + + public function getPostUuid(): string + { + return $this->postUuid; + } + + public function getUserUuid(): string + { + return $this->userUuid; + } + + public function getContent(): string + { + return $this->content; + } + + /** + * @return array + */ + public function toArray(): array + { + return [ + 'post_uuid' => $this->postUuid, + 'user_uuid' => $this->userUuid, + 'content' => $this->content, + ]; + } +} diff --git a/shared/DTOs/PostDTO.php b/shared/DTOs/PostDTO.php new file mode 100644 index 0000000..42cb75e --- /dev/null +++ b/shared/DTOs/PostDTO.php @@ -0,0 +1,113 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 3.0.0 + */ + +namespace Shared\DTOs; + +use Quantum\Http\Request; + +/** + * Class PostDTO + * @package Shared\DTOs + */ +class PostDTO +{ + /** + * @var string + */ + private string $title; + + /** + * @var string + */ + private string $content; + + /** + * @var string|null + */ + private ?string $userUuid; + + /** + * @var string|null + */ + private ?string $image; + + /** + * @param string $title + * @param string $content + * @param string|null $userUuid + * @param string|null $image + */ + public function __construct( + string $title, + string $content, + ?string $userUuid = null, + ?string $image = null + ) { + $this->title = $title; + $this->content = $content; + $this->userUuid = $userUuid; + $this->image = $image; + } + + /** + * @param Request $request + * @param string|null $userUuid + * @param string|null $image + * @return self + */ + public static function fromRequest(Request $request, ?string $userUuid = null, ?string $image = null): self + { + return new self( + $request->get('title', null, true), + $request->get('content', null, true), + $userUuid, + $image + ); + } + + public function getTitle(): string + { + return $this->title; + } + + public function getContent(): string + { + return $this->content; + } + + public function getUserUuid(): ?string + { + return $this->userUuid; + } + + public function getImage(): ?string + { + return $this->image; + } + + /** + * @return array + */ + public function toArray(): array + { + return array_filter([ + 'user_uuid' => $this->userUuid, + 'title' => $this->title, + 'content' => $this->content, + 'image' => $this->image, + ], function ($value) { + return $value !== null; + }); + } +} diff --git a/shared/DTOs/UserDTO.php b/shared/DTOs/UserDTO.php new file mode 100644 index 0000000..5495847 --- /dev/null +++ b/shared/DTOs/UserDTO.php @@ -0,0 +1,158 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 3.0.0 + */ + +namespace Shared\DTOs; + +use Quantum\Http\Request; + +/** + * Class UserDTO + * @package Shared\DTOs + */ +class UserDTO +{ + /** + * @var string + */ + private string $email; + + /** + * @var string + */ + private string $password; + + /** + * @var string + */ + private string $firstname; + + /** + * @var string + */ + private string $lastname; + + /** + * @var string + */ + private string $role; + + /** + * @var string|null + */ + private ?string $uuid; + + /** + * @var string + */ + private string $image; + + /** + * @param string $email + * @param string $password + * @param string $firstname + * @param string $lastname + * @param string $role + * @param string|null $uuid + * @param string $image + */ + public function __construct( + string $email, + string $password, + string $firstname, + string $lastname, + string $role = '', + ?string $uuid = null, + string $image = '' + ) { + $this->email = $email; + $this->password = $password; + $this->firstname = $firstname; + $this->lastname = $lastname; + $this->role = $role; + $this->uuid = $uuid; + $this->image = $image; + } + + /** + * @param Request $request + * @param string $role + * @param string|null $uuid + * @return self + */ + public static function fromRequest(Request $request, string $role, ?string $uuid = null): self + { + return new self( + $request->get('email'), + $request->get('password'), + $request->get('firstname'), + $request->get('lastname'), + $role, + $uuid + ); + } + + public function getEmail(): string + { + return $this->email; + } + + public function getPassword(): string + { + return $this->password; + } + + public function getFirstname(): string + { + return $this->firstname; + } + + public function getLastname(): string + { + return $this->lastname; + } + + public function getRole(): string + { + return $this->role; + } + + public function getUuid(): ?string + { + return $this->uuid; + } + + public function getImage(): string + { + return $this->image; + } + + /** + * Converts DTO to array for framework interface compatibility + * @return array + */ + public function toArray(): array + { + return array_filter([ + 'uuid' => $this->uuid, + 'email' => $this->email, + 'password' => $this->password, + 'firstname' => $this->firstname, + 'lastname' => $this->lastname, + 'role' => $this->role, + 'image' => $this->image, + ], function ($value) { + return $value !== null && $value !== ''; + }); + } +} diff --git a/shared/Services/CommentService.php b/shared/Services/CommentService.php index 98931fa..47afc5c 100644 --- a/shared/Services/CommentService.php +++ b/shared/Services/CommentService.php @@ -18,6 +18,7 @@ use Shared\Transformers\CommentTransformer; use Quantum\App\Exceptions\BaseException; use Quantum\Service\QtService; +use Shared\DTOs\CommentDTO; use Shared\Models\Comment; use Shared\Models\User; @@ -86,16 +87,16 @@ public function getComment(string $uuid): Comment /** * Add a new comment - * @param array $data + * @param CommentDTO $commentDto * @return array * @throws ModelException */ - public function addComment(array $data): array + public function addComment(CommentDTO $commentDto): array { - $data['uuid'] = $data['uuid'] ?? uuid_ordered(); + $uuid = uuid_ordered(); $comment = $this->model->create(); - $comment->fill($data); + $comment->fill(array_merge(['uuid' => $uuid], $commentDto->toArray())); $comment->save(); return $comment->asArray(); diff --git a/shared/Services/PostService.php b/shared/Services/PostService.php index a7b2672..0953c8a 100644 --- a/shared/Services/PostService.php +++ b/shared/Services/PostService.php @@ -29,6 +29,7 @@ use Quantum\Service\QtService; use Quantum\Model\DbModel; use ReflectionException; +use Shared\DTOs\PostDTO; use Shared\Models\User; use Shared\Models\Post; @@ -152,34 +153,34 @@ public function getMyPosts(string $userUuid): ?ModelCollection /** * Add post - * @param array $data + * @param PostDTO $postDto * @return Post * @throws BaseException * @throws ModelException */ - public function addPost(array $data): Post + public function addPost(PostDTO $postDto): Post { - $data['uuid'] = $data['uuid'] ?? uuid_ordered(); + $uuid = uuid_ordered(); $post = $this->model->create(); - $post->fill($data); + $post->fill(array_merge(['uuid' => $uuid], $postDto->toArray())); $post->save(); - return $this->getPost($data['uuid']); + return $this->getPost($uuid); } /** * Update post * @param string $uuid - * @param array $data + * @param PostDTO $postDto * @return Post * @throws BaseException * @throws ModelException */ - public function updatePost(string $uuid, array $data): Post + public function updatePost(string $uuid, PostDTO $postDto): Post { $post = $this->model->findOneBy('uuid', $uuid); - $post->fill($data); + $post->fill($postDto->toArray()); $post->save(); return $this->getPost($post->uuid); diff --git a/tests/Helpers/functions.php b/tests/Helpers/functions.php index 4c483df..f6056ee 100644 --- a/tests/Helpers/functions.php +++ b/tests/Helpers/functions.php @@ -9,13 +9,14 @@ use Quantum\Router\MatchedRoute; use Shared\Services\PostService; use Shared\Services\AuthService; +use Shared\DTOs\CommentDTO; use Quantum\Hasher\Hasher; +use Shared\DTOs\PostDTO; use Quantum\Router\Route; use Quantum\Http\Request; use Shared\Models\User; use Quantum\App\App; use Faker\Factory; -use Quantum\Di\Di; function createEnvFile() { @@ -111,12 +112,12 @@ function createUserPosts(AuthUser $user): array for ($i = 0; $i < $postCountPerUser; $i++) { $title = textCleanUp($faker->realText(50)); - $posts[] = ServiceFactory::create(PostService::class)->addPost([ - 'title' => $title, - 'content' => textCleanUp($faker->realText(100)), - 'image' => slugify($title) . '.jpg', - 'user_uuid' => $user->uuid, - ]); + $posts[] = ServiceFactory::create(PostService::class)->addPost(new PostDTO( + $title, + textCleanUp($faker->realText(100)), + $user->uuid, + slugify($title) . '.jpg' + )); } return $posts; @@ -132,11 +133,11 @@ function createPostComments(AuthUser $user, array $posts): array foreach ($posts as $post) { for ($i = 0; $i < $commentCountPerUser; $i++) { - $comments[] = ServiceFactory::create(CommentService::class)->addComment([ - 'post_uuid' => $post->uuid, - 'user_uuid' => $user->uuid, - 'content' => textCleanUp($faker->realText(rand(20, 100))), - ]); + $comments[] = ServiceFactory::create(CommentService::class)->addComment(new CommentDTO( + $post->uuid, + $user->uuid, + textCleanUp($faker->realText(rand(20, 100))) + )); } } diff --git a/tests/Unit/shared/Services/CommentServiceTest.php b/tests/Unit/shared/Services/CommentServiceTest.php index f69b0bb..6cfc9d7 100644 --- a/tests/Unit/shared/Services/CommentServiceTest.php +++ b/tests/Unit/shared/Services/CommentServiceTest.php @@ -2,14 +2,13 @@ namespace Quantum\Tests\Unit\shared\Services; -use Quantum\Model\Factories\ModelFactory; use Quantum\Service\Factories\ServiceFactory; -use Shared\Models\Comment; use Shared\Services\CommentService; use Quantum\Model\ModelCollection; use Shared\Services\AuthService; use Shared\Services\PostService; use PHPUnit\Framework\TestCase; +use Shared\DTOs\CommentDTO; class CommentServiceTest extends TestCase { @@ -65,11 +64,11 @@ public function testCommentServiceAddComment() $post = $this->postService->getPosts()->first(); $user = $this->authService->getAll()->first(); - $data = $this->commentService->addComment([ - 'user_uuid' => $user->uuid, - 'post_uuid' => $post->uuid, - 'content' => 'New comment content', - ]); + $data = $this->commentService->addComment(new CommentDTO( + $post->uuid, + $user->uuid, + 'New comment content' + )); $this->assertArrayHasKey('uuid', $data); @@ -84,11 +83,11 @@ public function testCommentServiceDeleteComment() $post = $this->postService->getPosts()->first(); $user = $this->authService->getAll()->first(); - $data = $this->commentService->addComment([ - 'user_uuid' => $user->uuid, - 'post_uuid' => $post->uuid, - 'content' => 'To be deleted' - ]); + $data = $this->commentService->addComment(new CommentDTO( + $post->uuid, + $user->uuid, + 'To be deleted' + )); $uuid = $data['uuid']; diff --git a/tests/Unit/shared/Services/PostServiceTest.php b/tests/Unit/shared/Services/PostServiceTest.php index 38b2e9d..9734553 100644 --- a/tests/Unit/shared/Services/PostServiceTest.php +++ b/tests/Unit/shared/Services/PostServiceTest.php @@ -9,6 +9,7 @@ use Shared\Services\AuthService; use Shared\Services\PostService; use PHPUnit\Framework\TestCase; +use Shared\DTOs\PostDTO; use Shared\Models\Post; class PostServiceTest extends TestCase @@ -77,13 +78,12 @@ public function testPostServiceGetPostsWithSearch() $title = "Title with {$uniqueKeyword}"; $content = "Some content"; - $post = $this->postService->addPost([ - 'user_uuid' => $userUuid, - 'title' => $title, - 'content' => $content, - 'image' => '', - - ]); + $post = $this->postService->addPost(new PostDTO( + $title, + $content, + $userUuid, + '' + )); $searchTerm = 'SEARCH_TOKEN'; @@ -136,13 +136,12 @@ public function testPostServiceAddNewPost() $userUuid = $users->first()->uuid; - $newPost = $this->postService->addPost([ - 'user_uuid' => $userUuid, - 'title' => 'Just another post', - 'content' => 'Content of just another post', - 'image' => '', - - ]); + $newPost = $this->postService->addPost(new PostDTO( + 'Just another post', + 'Content of just another post', + $userUuid, + '' + )); $post = $this->postService->getPost($newPost->uuid); @@ -163,11 +162,12 @@ public function testPostServiceUpdatePost() $uuid = $posts->first()->uuid; - $this->postService->updatePost($uuid, [ - 'title' => 'Walt Disney Jr.', - 'content' => 'The best way to get started is to quit talking and begin doing.', - 'image' => 'image.jpg', - ]); + $this->postService->updatePost($uuid, new PostDTO( + 'Walt Disney Jr.', + 'The best way to get started is to quit talking and begin doing.', + null, + 'image.jpg' + )); $post = $this->postService->getPost($uuid); @@ -190,12 +190,12 @@ public function testPostServiceDeletePost() $userUuid = $users->first()->uuid; - $post = $this->postService->addPost([ - 'user_uuid' => $userUuid, - 'title' => 'Just another post', - 'content' => 'Content of just another post', - 'image' => '', - ]); + $post = $this->postService->addPost(new PostDTO( + 'Just another post', + 'Content of just another post', + $userUuid, + '' + )); $this->assertCount(11, $this->postService->getPosts()); From 51302e4b908fa84bc5833f6536101711e3f8c3d4 Mon Sep 17 00:00:00 2001 From: Arman <407448+armanist@users.noreply.github.com> Date: Thu, 2 Apr 2026 13:31:41 +0400 Subject: [PATCH 2/2] Fix UserDTO::toArray() null filter, add trim to CommentCreateCommand, restore uuid pass-through in PostDTO/PostCreateCommand --- shared/Commands/CommentCreateCommand.php | 2 +- shared/Commands/PostCreateCommand.php | 3 ++- shared/DTOs/PostDTO.php | 16 +++++++++++++++- shared/DTOs/UserDTO.php | 2 +- shared/Services/PostService.php | 2 +- 5 files changed, 20 insertions(+), 5 deletions(-) diff --git a/shared/Commands/CommentCreateCommand.php b/shared/Commands/CommentCreateCommand.php index b56545f..b36ec0d 100644 --- a/shared/Commands/CommentCreateCommand.php +++ b/shared/Commands/CommentCreateCommand.php @@ -80,7 +80,7 @@ public function exec(): void $commentDto = new CommentDTO( $this->getArgument('post_uuid'), $this->getArgument('user_uuid'), - $this->getArgument('content') + trim($this->getArgument('content')) ); service(CommentService::class)->addComment($commentDto); diff --git a/shared/Commands/PostCreateCommand.php b/shared/Commands/PostCreateCommand.php index 647233f..2959d8c 100644 --- a/shared/Commands/PostCreateCommand.php +++ b/shared/Commands/PostCreateCommand.php @@ -84,7 +84,8 @@ public function exec(): void $this->getArgument('title'), $this->getArgument('description'), $this->getArgument('user_uuid'), - $this->getArgument('image') ?? '' + $this->getArgument('image') ?? '', + $this->getArgument('uuid') ); service(PostService::class)->addPost($postDto); diff --git a/shared/DTOs/PostDTO.php b/shared/DTOs/PostDTO.php index 42cb75e..dd89350 100644 --- a/shared/DTOs/PostDTO.php +++ b/shared/DTOs/PostDTO.php @@ -42,22 +42,30 @@ class PostDTO */ private ?string $image; + /** + * @var string|null + */ + private ?string $uuid; + /** * @param string $title * @param string $content * @param string|null $userUuid * @param string|null $image + * @param string|null $uuid */ public function __construct( string $title, string $content, ?string $userUuid = null, - ?string $image = null + ?string $image = null, + ?string $uuid = null ) { $this->title = $title; $this->content = $content; $this->userUuid = $userUuid; $this->image = $image; + $this->uuid = $uuid; } /** @@ -96,12 +104,18 @@ public function getImage(): ?string return $this->image; } + public function getUuid(): ?string + { + return $this->uuid; + } + /** * @return array */ public function toArray(): array { return array_filter([ + 'uuid' => $this->uuid, 'user_uuid' => $this->userUuid, 'title' => $this->title, 'content' => $this->content, diff --git a/shared/DTOs/UserDTO.php b/shared/DTOs/UserDTO.php index 5495847..1a64468 100644 --- a/shared/DTOs/UserDTO.php +++ b/shared/DTOs/UserDTO.php @@ -152,7 +152,7 @@ public function toArray(): array 'role' => $this->role, 'image' => $this->image, ], function ($value) { - return $value !== null && $value !== ''; + return $value !== null; }); } } diff --git a/shared/Services/PostService.php b/shared/Services/PostService.php index 0953c8a..c32e08b 100644 --- a/shared/Services/PostService.php +++ b/shared/Services/PostService.php @@ -160,7 +160,7 @@ public function getMyPosts(string $userUuid): ?ModelCollection */ public function addPost(PostDTO $postDto): Post { - $uuid = uuid_ordered(); + $uuid = $postDto->getUuid() ?? uuid_ordered(); $post = $this->model->create(); $post->fill(array_merge(['uuid' => $uuid], $postDto->toArray()));