1
votes

This top-ranking Stackoverflow answer from 10 years ago suggests using POST /users/:user_id/reset_password to initiate a password reset.

I know the API platform recommends against using custom operations. The docs page for the Symfony Messenger integration uses a ResetPasswordRequest entity (with a username field). That makes sense to me. 

Say I have a User and Notification entity and maybe a joined UserNotification (with a hasRead) entity as well. I want to expose an endpoint on my API to mark all the notifications older than a month as read. So I might make a ClearOldNotification entity, again with a username field.

Another example might be I want a report showing Customers that haven't been contacted due to some criteria. So I want to join the tables in the server and return a custom JSON data object. Again I could make a CustomerNoContact entity.

The issue I see is that I now have a distinction between pure entities, like a User or Product, as opposed to these service type entities.

  • Is this method of making individual entities classes for actions the recommended, best practice for Symfony and API Platform?
  • Should I be name-spacing (or something) these entities differently within my app to distinguish them?
  • I could imagine on a really large and complex application you could have hundreds of these service entities, compared to the pure entities. Is that expected or desired?
  • Can anyone recommend some good resources on this pattern?
1
IMHO service type Entities are workarounds for limitations imposed by Api Platform. But other solution are available: As suggested in the Design Considerations a POPO can be used as a resource. And/or a DTO or a view model as output. - MetaClass

1 Answers

2
votes

You're asking for a best practice for two different use cases. Let's break it down:

ClearOldNotification

I think you've already found the solution: using Messenger. As you've read, there is an example in the documentation for this use case:

#[ApiResource(collectionOperations: [
        "post", "get", "delete",
        "reset_password" => ["status" => 202, "messenger" => "input", "input" => ResetPasswordRequest::class, "output" => false, "method" => "POST", "path" => "/users/reset_password"]
    ]
)]
final class User
{
}

The ResetPasswordRequest class is a Data Transfer Object (DTO). In your ResetPasswordRequestHandler you should inject the service that is responsible for resetting the password and sending an email.

CustomerNoContact

This could be a Custom (Doctrine ORM) Filter.