Skip to content

[Streamlining Entity Design] Introducing the DTOs #1522

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from

Conversation

yceruto
Copy link
Member

@yceruto yceruto commented Jun 14, 2024

I've made some updates to our entity designs to match what the community recommends about Forms -> DTOs -> Entities.

So, the Post entity is now separate from input/request processing, which is handled by the new PostDto. I updated all Post's properties to meet business rules about nullability. For example, since the Post title is required, it’s no longer nullable. This also let me remove the phpstan exceptions about Entity <-> DB mapping mismatches.

I kept things simple on purpose, but we might think about adding required properties in the constructor later (or through semantic constructors) to avoid invalid states.

These changes are only for the Post entity so far. If you like them, let me know, and I can update the rest.

I'll add comments to clarify any changes that might not be immediately obvious.

Cheers!

@yceruto yceruto requested a review from javiereguiluz June 14, 2024 18:32

#[ORM\Column]
private \DateTimeImmutable $publishedAt;

#[ORM\ManyToOne(targetEntity: User::class)]
Copy link
Member Author

Choose a reason for hiding this comment

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

the targetEntity is inferred from the property type


/**
* @var Collection<int, Comment>
*/
#[ORM\OneToMany(targetEntity: Comment::class, mappedBy: 'post', orphanRemoval: true, cascade: ['persist'])]
#[ORM\OneToMany(targetEntity: Comment::class, mappedBy: 'post', cascade: ['persist'], orphanRemoval: true)]
Copy link
Member Author

Choose a reason for hiding this comment

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

resorting based on the argument definition

use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;

#[UniqueEntity(fields: ['slug'], message: 'post.slug_unique', entityClass: Post::class, errorPath: 'title', identifierFieldNames: ['id'])]
Copy link
Member Author

Choose a reason for hiding this comment

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

this is now possible since Symfony 7.1

@@ -52,15 +52,18 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
->add('title', null, [
'attr' => ['autofocus' => true],
'label' => 'label.title',
'empty_data' => '',
Copy link
Member Author

Choose a reason for hiding this comment

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

[phpstan] this approach allows us to avoid nullable properties in the DTOs (forcing us to check for null before setting its value into a non-nullable argument)

If you remove the required HTML attribute, the submitted form field data will be an empty string '' instead of null, and NotBlank validator will do its job.

])
->add('content', null, [
->add('content', TextareaType::class, [
Copy link
Member Author

Choose a reason for hiding this comment

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

this improve the UX by adding more vertical space to work with the content input

{
$this->publishedAt = new \DateTimeImmutable();
Copy link
Contributor

Choose a reason for hiding this comment

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

Why have you removed this? So, now the publish date is not set when the entity is constructed.

Copy link
Member Author

Choose a reason for hiding this comment

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

This was initially set up to provide a default value during the Post creation form, but its final value was always set through the setter, because it's currently a user input.

Now the default value is set in the DTO instead, and the Post::$publishedAt value continues to be set through its setPublishedAt() setter method.

Note that (as before) this proposal does not follow the stricter path to guarantee that the entity’s state is always valid from its construction which I'd like to (waiting for confirmation)

@@ -111,7 +111,6 @@ public function new(
}

return $this->render('admin/blog/new.html.twig', [
'post' => $post,
Copy link
Member Author

Choose a reason for hiding this comment

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

removed as it's useless in the template

@setineos
Copy link

setineos commented Oct 3, 2024

Any news?

@yceruto yceruto closed this Oct 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants