Skip to content

Refactor Services and CLI commands to use DTOs#175

Merged
armanist merged 2 commits intosoftberg:masterfrom
armanist:171-Implement-DTO-Pattern-for-Posts,-Comments-and-Users
Apr 2, 2026
Merged

Refactor Services and CLI commands to use DTOs#175
armanist merged 2 commits intosoftberg:masterfrom
armanist:171-Implement-DTO-Pattern-for-Posts,-Comments-and-Users

Conversation

@armanist
Copy link
Copy Markdown
Member

@armanist armanist commented Apr 1, 2026

Closes #171

Summary by CodeRabbit

  • Refactor

    • Switched internal payloads to typed data objects for posts, comments, and users, improving data consistency, validation, and reliability across creation and update flows.
  • Tests

    • Updated tests and helpers to use the new typed payloads, preserving behavior while ensuring coverage for the refactored flows.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 1, 2026

📝 Walkthrough

Walkthrough

Commands and services were refactored to use new DTO classes for Posts, Comments, and Users. Commands construct DTOs from inputs and pass them to services; service method signatures were updated to accept DTOs and use their serialized data for model operations. Tests and helpers were updated accordingly.

Changes

Cohort / File(s) Summary
New DTO Classes
shared/DTOs/CommentDTO.php, shared/DTOs/PostDTO.php, shared/DTOs/UserDTO.php
Added typed DTOs with constructors, fromRequest() factories, getters, and toArray() serialization.
Command Updates
shared/Commands/CommentCreateCommand.php, shared/Commands/PostCreateCommand.php, shared/Commands/PostUpdateCommand.php, shared/Commands/UserCreateCommand.php
Commands now build DTO instances (trimming/hashing as before) and pass DTOs to services instead of associative arrays; validation and control flow preserved.
Service Changes
shared/Services/CommentService.php, shared/Services/PostService.php
Service method signatures updated to accept DTOs (CommentDTO, PostDTO); services generate/fallback UUIDs and call toArray() for model fill()/create/update operations.
Tests & Helpers
tests/Helpers/functions.php, tests/Unit/shared/Services/CommentServiceTest.php, tests/Unit/shared/Services/PostServiceTest.php
Test helpers and unit tests updated to construct and pass DTO instances to services; unused imports removed and DTO imports added.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Cmd as "Command\n(shared/Commands)"
participant DTO as "DTO\n(shared/DTOs)\n(rather than plain array)"
participant Service as "Service\n(shared/Services)"
participant Model as "Model / DB"
Note over Cmd,Service: Flow for create/update operations
Cmd->>DTO: construct DTO (from args/request)
DTO-->>Cmd: DTO instance
Cmd->>Service: call add/update with DTO
Service->>DTO: read values via getters / toArray()
Service->>Model: create/fill/save with merged UUID + data
Model-->>Service: persisted model
Service-->>Cmd: return created/updated entity

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • andrey-smaelov
  • grigoryanmartin20
  • live-soft
  • annaohanesyan
  • Vahram1995

Poem

🐇 I stitched new DTOs, neat and spry,

From commands they hop, to services they fly,
Typed and trimmed, no loose array,
A rabbit's tidy pathway—hip-hop hooray! 🥕

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning PR successfully introduces CommentDTO, PostDTO, and UserDTO with constructors and getters; updates services (CommentService, PostService) to accept DTOs; refactors commands (CommentCreateCommand, PostCreateCommand, PostUpdateCommand, UserCreateCommand) to instantiate and pass DTOs; validation occurs before DTO instantiation; all tests updated. However, DTOs are placed in shared/DTOs/ rather than the specified Modules/Api/DTOs/ location. Move DTOs from shared/DTOs/ to Modules/Api/DTOs/ to align with issue #171 structural requirements and then update all import statements accordingly.
Docstring Coverage ⚠️ Warning Docstring coverage is 41.03% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately reflects the main objective—refactoring services and CLI commands to use DTOs for type-safe data transfer.
Out of Scope Changes check ✅ Passed Changes are focused on DTO introduction and service/command refactoring. Helper function and test file updates directly support DTO implementation. No unrelated out-of-scope changes detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@armanist
Copy link
Copy Markdown
Member Author

armanist commented Apr 2, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
shared/Commands/CommentCreateCommand.php (1)

80-84: Normalize CLI content before DTO creation for consistency.

CommentDTO::fromRequest() trims content, but the CLI path passes raw content. Aligning both paths avoids inconsistent stored values.

Proposed tweak
 $commentDto = new CommentDTO(
     $this->getArgument('post_uuid'),
     $this->getArgument('user_uuid'),
-    $this->getArgument('content')
+    trim((string)$this->getArgument('content'))
 );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@shared/Commands/CommentCreateCommand.php` around lines 80 - 84, The CLI path
creates CommentDTO with raw content causing inconsistency with
CommentDTO::fromRequest() which trims content; update the construction in
CommentCreateCommand (the new CommentDTO(...) call) to normalize the content
argument (e.g., apply trim on $this->getArgument('content') or otherwise
replicate the same normalization used by CommentDTO::fromRequest()) so
CLI-created DTOs match request-created DTOs.
shared/Commands/PostCreateCommand.php (1)

57-63: Unused uuid argument can be removed.

The uuid argument (line 61) is no longer used since PostService::addPost() now generates the UUID internally via uuid_ordered(). This dead code can be cleaned up.

Suggested fix
     protected array $args = [
         ['title', 'required', 'Post title'],
         ['description', 'required', 'Post description'],
         ['user_uuid', 'required', 'Author uuid'],
-        ['uuid', 'optional', 'Post uuid'],
         ['image', 'optional', 'Post image'],
     ];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@shared/Commands/PostCreateCommand.php` around lines 57 - 63, The args array
in PostCreateCommand still declares a 'uuid' entry even though
PostService::addPost() now generates UUIDs internally via uuid_ordered(); remove
the unused ['uuid', 'optional', 'Post uuid'] element from the protected array
$args in the PostCreateCommand class to eliminate dead code and keep the command
signature consistent with PostService::addPost().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@shared/Commands/UserCreateCommand.php`:
- Line 102: The command is breaking the DTO boundary by calling
service(AuthService::class)->add($userDto->toArray()); — change the
AuthService::add signature to accept the UserDTO object (e.g., UserDTO $userDto)
and update this call-site in UserCreateCommand to pass $userDto (not
->toArray()); then move the array mapping/normalization logic into
AuthService::add (or a private mapper method) so all mapping from UserDTO to
persistence/input arrays happens inside AuthService; also update any other
callers of AuthService::add to use the new UserDTO parameter.

In `@shared/DTOs/UserDTO.php`:
- Around line 146-156: The toArray() method in UserDTO is currently removing
keys with empty strings (''), which drops fields like role and image; update the
array_filter callback in UserDTO::toArray to only filter out null values (i.e.,
return $value !== null) so empty strings are preserved and keys remain present
in the serialized output, ensuring persisted defaults and response parity are
not altered.

---

Nitpick comments:
In `@shared/Commands/CommentCreateCommand.php`:
- Around line 80-84: The CLI path creates CommentDTO with raw content causing
inconsistency with CommentDTO::fromRequest() which trims content; update the
construction in CommentCreateCommand (the new CommentDTO(...) call) to normalize
the content argument (e.g., apply trim on $this->getArgument('content') or
otherwise replicate the same normalization used by CommentDTO::fromRequest()) so
CLI-created DTOs match request-created DTOs.

In `@shared/Commands/PostCreateCommand.php`:
- Around line 57-63: The args array in PostCreateCommand still declares a 'uuid'
entry even though PostService::addPost() now generates UUIDs internally via
uuid_ordered(); remove the unused ['uuid', 'optional', 'Post uuid'] element from
the protected array $args in the PostCreateCommand class to eliminate dead code
and keep the command signature consistent with PostService::addPost().
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b1b6124f-94e7-4649-8d96-813a60bcbfe3

📥 Commits

Reviewing files that changed from the base of the PR and between 7d527a5 and faf8668.

📒 Files selected for processing (12)
  • shared/Commands/CommentCreateCommand.php
  • shared/Commands/PostCreateCommand.php
  • shared/Commands/PostUpdateCommand.php
  • shared/Commands/UserCreateCommand.php
  • shared/DTOs/CommentDTO.php
  • shared/DTOs/PostDTO.php
  • shared/DTOs/UserDTO.php
  • shared/Services/CommentService.php
  • shared/Services/PostService.php
  • tests/Helpers/functions.php
  • tests/Unit/shared/Services/CommentServiceTest.php
  • tests/Unit/shared/Services/PostServiceTest.php

… restore uuid pass-through in PostDTO/PostCreateCommand
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
shared/Commands/PostCreateCommand.php (1)

83-89: Validate user_uuid/uuid before creating PostDTO.

The command validates title/content, but identifier fields used in the DTO are not validated in validationRules(). Add UUID-format checks before instantiation to keep bad IDs out of service/model layers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@shared/Commands/PostCreateCommand.php` around lines 83 - 89, The command
constructs a PostDTO with identifier fields that aren’t validated; update
PostCreateCommand::validationRules() to include UUID-format validators for both
'user_uuid' and 'uuid' (the inputs passed into the PostDTO), run the validation
and return/throw on failure before the PostDTO instantiation, and ensure
PostCreateCommand uses the validated values when calling new PostDTO so
malformed IDs never reach the service/model layers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@shared/Services/PostService.php`:
- Around line 183-186: In updatePost (PostService::updatePost), prevent UUID
mutation by not copying uuid from the incoming PostDto into the model: when
converting $postDto to an array before calling $post->fill(...) remove or
exclude the 'uuid' key (or explicitly only map allowed mutable fields) so the
model's $post->uuid remains unchanged; then save and return via
$this->getPost($post->uuid) as before.

---

Nitpick comments:
In `@shared/Commands/PostCreateCommand.php`:
- Around line 83-89: The command constructs a PostDTO with identifier fields
that aren’t validated; update PostCreateCommand::validationRules() to include
UUID-format validators for both 'user_uuid' and 'uuid' (the inputs passed into
the PostDTO), run the validation and return/throw on failure before the PostDTO
instantiation, and ensure PostCreateCommand uses the validated values when
calling new PostDTO so malformed IDs never reach the service/model layers.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4ea8d72a-03f4-4afc-8473-d20f9fb39215

📥 Commits

Reviewing files that changed from the base of the PR and between faf8668 and 51302e4.

📒 Files selected for processing (5)
  • shared/Commands/CommentCreateCommand.php
  • shared/Commands/PostCreateCommand.php
  • shared/DTOs/PostDTO.php
  • shared/DTOs/UserDTO.php
  • shared/Services/PostService.php
✅ Files skipped from review due to trivial changes (1)
  • shared/Commands/CommentCreateCommand.php
🚧 Files skipped from review as they are similar to previous changes (2)
  • shared/DTOs/UserDTO.php
  • shared/DTOs/PostDTO.php

Comment on lines +183 to 186
$post->fill($postDto->toArray());
$post->save();

return $this->getPost($post->uuid);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Prevent UUID mutation in updatePost.

$postDto->toArray() may include uuid, which allows changing the record identity during update. Keep UUID immutable in updates and fill only mutable fields.

Suggested fix
 public function updatePost(string $uuid, PostDTO $postDto): Post
 {
     $post = $this->model->findOneBy('uuid', $uuid);
-    $post->fill($postDto->toArray());
+    $payload = $postDto->toArray();
+    unset($payload['uuid']);
+    $post->fill($payload);
     $post->save();

-    return $this->getPost($post->uuid);
+    return $this->getPost($uuid);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@shared/Services/PostService.php` around lines 183 - 186, In updatePost
(PostService::updatePost), prevent UUID mutation by not copying uuid from the
incoming PostDto into the model: when converting $postDto to an array before
calling $post->fill(...) remove or exclude the 'uuid' key (or explicitly only
map allowed mutable fields) so the model's $post->uuid remains unchanged; then
save and return via $this->getPost($post->uuid) as before.

@armanist armanist merged commit 4ab62d9 into softberg:master Apr 2, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement DTO Pattern for Posts, Comments and Users

2 participants