Skip to content

Make it easier to use simple DTO #1590

Description

@lyrixx

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:

  • I had hard time to find a working solution. I tried with some events without success (always an issue because it's not a real entity) and finally with a controller. It was not easy to find the custom attribute to use. I had to open all listener to understand how it really works.
  • I had to validate the entity by myself. But there is a ValidatorListener. But it is ran after the controller. Why ? cf Why is validation process executed on kernel.view instead of kernel.request ? #1538
  • The documentation is a bit light on using a DTO like this.

Could we do something to improve this experience ?


Anyway, thanks a lot for this project

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions