Hello.
I have one simple need: A user should be able to call an endpoint to reset his password.
(Note: I could have been something totally different, but let's stick to this use case)
I have tried many thing to be the closest possible to API Platform. I mean, I want to use all the feature
of API Platform: de-serialization, validation, error management, documation, etc.
So I end up to write the following code that works, but I think we can do better.
A simple DTO, with validation and declared as an ApiRessource
<?php
namespace AppBundle\Api\Dto;
use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\Validator\Constraints as Assert;
/**
* @ApiResource(
* collectionOperations={
* "post"={"route_name"="api_users_forgot_password_request"},
* },
* itemOperations={},
* )
*/
class ForgotPasswordRequest
{
/**
* @Assert\NotBlank()
* @Assert\Email()
*/
public $email;
}
Then, I had to write the associated controller:
<?php
namespace AppBundle\Controller\Api2;
use AppBundle\Api\Dto\ForgotPasswordRequest;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
class ForgotPasswordRequestController extends Controller
{
/**
* @Route(
* name="api_users_forgot_password_request",
* path="/users/forgot-password-request",
* defaults={"_api_resource_class"=ForgotPasswordRequest::class, "_api_collection_operation_name"="special"}
* )
* @Method("POST")
*/
public function requestAction(ForgotPasswordRequest $data)
{
$errors = $this->get('validator')->validate($data);
if (count($errors) > 0) {
return $errors;
}
// My custom logic here
return new JsonResponse(null, 204);
}
}
So basically it works but:
Could we do something to improve this experience ?
Anyway, thanks a lot for this project
EDIT
I finally manage to leverage the event system. So now I don't use the a custom controller \o/
I made a ver stupid typo. Here is my fix:
public static function getSubscribedEvents()
{
return [
- KernelEvents::REQUEST => ['resolveMe', EventPriorities::PRE_READ],
- KernelEvents::VIEW => ['sendPasswordReset', EventPriorities::PRE_WRITE],
- KernelEvents::VIEW => ['sendForgotPasswordRequest', EventPriorities::PRE_WRITE],
- KernelEvents::VIEW => ['updatePassword', EventPriorities::PRE_WRITE],
+ KernelEvents::REQUEST => ['resolveMe', EventPriorities::POST_READ],
+ KernelEvents::VIEW => [
+ ['sendForgotPasswordRequest', EventPriorities::PRE_VALIDATE],
+ ['sendPasswordReset', EventPriorities::PRE_WRITE],
+ ['updatePassword', EventPriorities::PRE_WRITE],
+ ]
];
}
I'm closing this issue as it's solve for me.
Maybe we could keep this open? I feel there might be room for improvements in our documentation about DTO and the event system ! Would you mind contributing some of your above research to the docs?
Hello.
Thanks @soyuka . You doc is really good but indeed there is a lack.
but unfortunately I'm not able to contribute to the doc. I'm not a native EN speaker :/
And I lack time too ;)
I'm not a native EN speaker :/
Neither are most of our doc contributors!
And I lack time too ;)
We all do don't we? ;)
Don't worry though, I'm sure this can already help some people!
@dunglas , @soyuka WDYT about moving validator listener from onKernelView to another event, like "get controller args"? This allows validate DTO's before they pass to the controller and we can write some logic inside controllers (not in event listeners) before flush listener would called.
I thinks it's a good idea, but this would be a big BC break :/
I have opened a PR in the documentation ;)
The Doc PR has been merged. I close the issue, but if you think there is more to do feel free to re-open.
Thanks.
I am struggling to understand when to use a DTO and when to use an EventListener.
I feel DTO's are the way to go and I had assumed that DTO's were validated prior to receiving the data. It doesn't feel right to be referring to route names when using the listeners to decide what to do :|
Most helpful comment
I am struggling to understand when to use a DTO and when to use an EventListener.
I feel DTO's are the way to go and I had assumed that DTO's were validated prior to receiving the data. It doesn't feel right to be referring to route names when using the listeners to decide what to do :|