Skip to content
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

Added basic doc for dealing with DTO #369

Merged
merged 2 commits into from
Dec 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions core/dto.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Handling Data Transfer Objects (DTOs)

## How to use a DTO for Writing

Sometimes it's easier to use a DTO than an Entity when performing simple
operation. For example, the application should be able to send an email when
someone has lost its password.

So let's create a basic DTO for this request:

```php
// api/src/Api/Dto/ForgotPasswordRequest.php

namespace App\Api\Dto;

use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\Validator\Constraints as Assert;

/**
* @ApiResource(
* collectionOperations={
* "post"={
* "method"="POST",
* "path"="/users/forgot-password-request",
* },
* },
* itemOperations={},
* )
*/
final class ForgotPasswordRequest
{
/**
* @Assert\NotBlank()
* @Assert\Email()
*/
public $email;
}
```

In this case, we disable all operations except `POST`.

Then, thanks to [the event system](events.md), it's possible to intercept the
`POST` request and to handle it.

First, an event subscriber is needed:

```php
<?php
// api/src/Api/EventSubscriber/UserSubscriber.php

namespace App\Api\EventSubscriber;

use ApiPlatform\Core\EventListener\EventPriorities;
use App\Entity\User;
use App\Manager\UserManager;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
use Symfony\Component\HttpKernel\KernelEvents;

final class UserSubscriber implements EventSubscriberInterface
{
private $userManager;

public function __construct(UserManager $userManager)
{
$this->userManager = $userManager;
}

public static function getSubscribedEvents()
{
return [
KernelEvents::VIEW => ['sendPasswordReset', EventPriorities::POST_VALIDATE],
];
}

public function sendPasswordReset(GetResponseForControllerResultEvent $event)
{
$request = $event->getRequest();

if ('api_forgot_password_requests_post_collection' !== $request->attributes->get('_route')) {
Copy link
Contributor

Choose a reason for hiding this comment

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

wouldn't better to check the model instance instead ?

if (
    !Request::METHOD_POST === $request->getMethod()
    || !$event->getControllerResult() instanceof ForgotPasswordRequest
) {
    return;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually, Now I prefer to match the route name. This reduce side effect.

But anyway, It's not really perfect IMHO

Copy link
Contributor

Choose a reason for hiding this comment

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

ahh ok, IMO there is no perfect way ;)

return;
}

$forgotPasswordRequest = $event->getControllerResult();

$user = $this->userManager->findOneByEmail($forgotPasswordRequest->email);

// We do nothing if the user does not exist in the database
if ($user) {
$this->userManager->requestPasswordReset($user);
}

$event->setResponse(new JsonResponse(null, 204));
}
}
```

Then this class should be registered as a service, then tagged.

If service autowiring and autoconfiguration are enabled (it's the case by
default), you are done!

Otherwise, the following configuration is needed:

```yaml
# app/config/services.yml
services:

# ...

'App\Api\EventSubscriber\UserSubscriber':
arguments:
- '@app.manager.user'
tags: [ 'kernel.event_subscriber' ]
```
1 change: 1 addition & 0 deletions index.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
1. [Overall Process](core/serialization.md#overall-process)
2. [Available Serializers](core/serialization.md#available-serializers)
3. [Decorating a Serializer and Add Extra Data](core/serialization.md#decorating-a-serializer-and-add-extra-data)
27. [Handling Data Transfer Objects (DTOs)](core/dto.md)

## The Schema Generator Component: Generate Data Models from Open Vocabularies

Expand Down