Renvoyer un nouveau code

Nous allons refaire une route POST resend-activation-code sur l'entité User, et qui demandera un email valide en payload.

/**
 * @ORM\Entity(repositoryClass=UserRepository::class)
 * @ApiResource(
 *      normalizationContext={"groups"={"user:read"}},
 *      denormalizationContext={"groups"={"user:write"}},
 *       collectionOperations={
 *           "get",
 *           "post",
 *           "activationCode"={
 *               "method"="POST",
 *               "path"="activation-code",
 *               "input": ActivationCodeInput::class,
 *               "denormalization_context"={"groups"={"activation:write"}}
 *           },
 *            "resendActivationCode"={
 *                "method"="POST",
 *                "path"="resend-activation-code",
 *                "input": ResendActivationCodeInput::class,
 *                "denormalization_context"={"groups"={"activation:write"}}
 *            }
 *       }
 * )
 */
class User implements UserInterface

Voici notre input qui vérifie si notre input est valide

<?php

// src/DTO/ResendActivationCodeInput.php

namespace App\DTO;

use App\Validator\Constraint\IsEmailExist;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Serializer\Annotation\Groups;

class ResendActivationCodeInput
{
    /**
     * @Assert\NotBlank()
     * @Assert\Email()
     * @Groups({"activation:write"})
     */
    public $email;
}

Nous créons un DataTransformer

<?php

// src/DataTransformer/ResendActivationCodeDataTransformer.php

namespace App\DataTransformer;

use ApiPlatform\Core\DataTransformer\DataTransformerInterface;
use App\Entity\ActivationCode;
use App\Entity\User;

final class ResendActivationCodeDataTransformer implements DataTransformerInterface
{
    /**
     * {@inheritdoc}
     */
    public function transform($data, string $to, array $context = [])
    {
        return $data;
    }

    /**
     * {@inheritdoc}
     */
    public function supportsTransformation($data, string $to, array $context = []): bool
    {
        // in the case of an input, the value given here is an array (the JSON decoded).
        if ($data instanceof User) {
            return false;
        }

        return (User::class === $to) && null !== ($context['input']['class'] ?? null) && (array_key_exists('collection_operation_name', $context) && $context['collection_operation_name'] === 'resendActivationCode');
    }
}

Dans la méthode transform nous vérifions si l'email est présent en base de données. Si oui nous générons un nouveau.

Nous allons factoriser la partie suivante

$activationCode = ActivationCodeService::generateNewCode();
$user->setActivationCode($activationCode['code']);
$user->setActivationCodeExpiresAt($activationCode['expirationDate']);
$this->entityManager->persist($user);
$this->entityManager->flush();

car elle est également dans UserDataPersister.

dans src/Security/ActivationCodeService.php rajoutons la méthode generateCodeAndSendByMail :

<?php

namespace App\Security;

use App\Entity\User;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;
use Twig\Environment;

class ActivationCodeService
{
    private $mailer;
    private $twig;

    public function __construct(
        MailerInterface $mailer,
        Environment $twig
    ) {
        $this->mailer = $mailer;
        $this->twig = $twig;
    }

    const VALIDATION_HOURS = 2;

    public static function generateNewCode()
    {
        return
        [
            'code' => random_int(100000, 999999),
            'expirationDate' => new \DateTime('+ ' . self::VALIDATION_HOURS . 'hour')
        ];
    }

    public function generateCodeAndSendByMail(User $user) {
        $activationCode = self::generateNewCode();
        $user->setActivationCode($activationCode['code']);
        $user->setActivationCodeExpiresAt($activationCode['expirationDate']);

        $email = (new Email())
            ->from('hello@example.com')
            ->to($user->getEmail())
            ->subject('Activez votre mail')
            ->html(
                $this->twig->render('emails/registration.html.twig', [
                    'activationCode' => $activationCode
                ])
            )
        ;

        $this->mailer->send($email);

        return $user;
    }
}

Maintenant nous créons ResendActivationCodeDataPersister qui est appelé quand on arrive de ResendActivationCodeInput et nous envoyons le nouveau code par mail avec la méthode generateCodeAndSendByMail

<?php

// src/DataPersister/ResendActivationCodeDataPersister.php

namespace App\DataPersister;

use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
use App\DTO\ResendActivationCodeInput;
use App\Repository\UserRepository;
use App\Security\ActivationCodeService;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;

class ResendActivationCodeDataPersister implements ContextAwareDataPersisterInterface
{
    private $userRepository;
    private $entityManager;
    private $activationCodeService;

    public function __construct(UserRepository $userRepository, EntityManagerInterface $entityManager, ActivationCodeService $activationCodeService)
    {
        $this->userRepository = $userRepository;
        $this->entityManager = $entityManager;
        $this->activationCodeService = $activationCodeService;
    }

    public function supports($data, array $context = []): bool
    {
        return $data instanceof ResendActivationCodeInput;
    }

    public function persist($data, array $context = [])
    {
        $user = $this->userRepository->findOneBy(['email' => $data->email]);
        if ($user) {
            $this->activationCodeService->generateCodeAndSendByMail($user);
            $this->entityManager->persist($user);
            $this->entityManager->flush();
        }

        return new JsonResponse(['message' => 'A new code has been sent to you'], 200);
    }

    public function remove($data, array $context = [])
    {
    }
}

et dans UserDataPersister nous appelons également generateCodeAndSendByMail :

public function persist($data, array $context = [])
{
    if ($data->getId() === null) {
        if ($data->getPlainPassword()) {
            $data->setPassword(
                $this->userPasswordEncoder->encodePassword($data, $data->getPlainPassword())
            );
            $data->eraseCredentials();
        }

        $this->activationCodeService->generateCodeAndSendByMail($data);
    }

    return $this->decorated->persist($data, $context);
}
prev next

Commentaires

Connectez-vous pour laisser un commentaire