Skip to content

Commit 2cb6463

Browse files
committed
ISSUE-345: add validator
1 parent a177a59 commit 2cb6463

File tree

6 files changed

+153
-55
lines changed

6 files changed

+153
-55
lines changed

src/Controller/SubscriberController.php

Lines changed: 18 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@
44

55
namespace PhpList\RestBundle\Controller;
66

7+
use PhpList\RestBundle\Entity\SubscriberRequest;
8+
use PhpList\RestBundle\Service\Manager\SubscriberManager;
9+
use PhpList\RestBundle\Validator\RequestValidator;
710
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
8-
use PhpList\Core\Domain\Model\Subscription\Subscriber;
911
use PhpList\Core\Domain\Repository\Subscription\SubscriberRepository;
1012
use PhpList\Core\Security\Authentication;
1113
use PhpList\RestBundle\Controller\Traits\AuthenticationTrait;
1214
use Symfony\Component\HttpFoundation\JsonResponse;
1315
use Symfony\Component\HttpFoundation\Request;
1416
use Symfony\Component\HttpFoundation\Response;
15-
use Symfony\Component\HttpKernel\Exception\ConflictHttpException;
16-
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
1717
use Symfony\Component\Routing\Attribute\Route;
1818
use Symfony\Component\Serializer\SerializerInterface;
1919
use OpenApi\Attributes as OA;
@@ -28,11 +28,16 @@ class SubscriberController extends AbstractController
2828
use AuthenticationTrait;
2929

3030
private SubscriberRepository $subscriberRepository;
31+
private SubscriberManager $subscriberManager;
3132

32-
public function __construct(Authentication $authentication, SubscriberRepository $repository)
33-
{
33+
public function __construct(
34+
Authentication $authentication,
35+
SubscriberRepository $repository,
36+
SubscriberManager $subscriberManager,
37+
) {
3438
$this->authentication = $authentication;
3539
$this->subscriberRepository = $repository;
40+
$this->subscriberManager = $subscriberManager;
3641
}
3742

3843
#[Route('/subscribers', name: 'create_subscriber', methods: ['POST'])]
@@ -126,25 +131,16 @@ public function __construct(Authentication $authentication, SubscriberRepository
126131
)
127132
]
128133
)]
129-
public function postAction(Request $request, SerializerInterface $serializer): JsonResponse
130-
{
134+
public function postAction(
135+
Request $request,
136+
SerializerInterface $serializer,
137+
RequestValidator $validator
138+
): JsonResponse {
131139
$this->requireAuthentication($request);
132-
$data = $request->getPayload();
133-
$this->validateSubscriber($request);
134-
135-
$email = $data->get('email');
136-
if ($this->subscriberRepository->findOneByEmail($email) !== null) {
137-
throw new ConflictHttpException('This resource already exists.', null, 1513439108);
138-
}
139-
$confirmed = (bool)$data->get('request_confirmation', true);
140-
$subscriber = new Subscriber();
141-
$subscriber->setEmail($email);
142-
$subscriber->setConfirmed(!$confirmed);
143-
$subscriber->setBlacklisted(false);
144-
$subscriber->setHtmlEmail((bool)$data->get('html_email', true));
145-
$subscriber->setDisabled(false);
146140

147-
$this->subscriberRepository->save($subscriber);
141+
/** @var SubscriberRequest $subscriberRequest */
142+
$subscriberRequest = $validator->validate($request, SubscriberRequest::class);
143+
$subscriber = $this->subscriberManager->createSubscriber($subscriberRequest);
148144

149145
return new JsonResponse(
150146
$serializer->serialize($subscriber, 'json'),
@@ -256,37 +252,4 @@ public function getAction(Request $request, int $subscriberId, SerializerInterfa
256252

257253
return new JsonResponse($data, Response::HTTP_OK, [], true);
258254
}
259-
260-
/**
261-
* @param Request $request
262-
*
263-
* @return void
264-
*
265-
* @throws UnprocessableEntityHttpException
266-
*/
267-
private function validateSubscriber(Request $request): void
268-
{
269-
/** @var string[] $invalidFields */
270-
$invalidFields = [];
271-
if (filter_var($request->getPayload()->get('email'), FILTER_VALIDATE_EMAIL) === false) {
272-
$invalidFields[] = 'email';
273-
}
274-
275-
$booleanFields = ['request_confirmation', 'html_email'];
276-
foreach ($booleanFields as $fieldKey) {
277-
if ($request->getPayload()->get($fieldKey) !== null
278-
&& !is_bool($request->getPayload()->get($fieldKey))
279-
) {
280-
$invalidFields[] = $fieldKey;
281-
}
282-
}
283-
284-
if (!empty($invalidFields)) {
285-
throw new UnprocessableEntityHttpException(
286-
'Some fields invalid:' . implode(', ', $invalidFields),
287-
null,
288-
1513446736
289-
);
290-
}
291-
}
292255
}

src/Entity/SubscriberRequest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpList\RestBundle\Entity;
6+
7+
use Symfony\Component\Validator\Constraints as Assert;
8+
use PhpList\RestBundle\Validator\UniqueEmail;
9+
10+
class SubscriberRequest
11+
{
12+
#[Assert\NotBlank]
13+
#[Assert\Email]
14+
#[UniqueEmail]
15+
public string $email;
16+
17+
#[Assert\Type(type: 'bool')]
18+
public ?bool $request_confirmation = null;
19+
20+
#[Assert\Type(type: 'bool')]
21+
public ?bool $html_email = null;
22+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpList\RestBundle\Service\Manager;
6+
7+
use PhpList\Core\Domain\Model\Subscription\Subscriber;
8+
use PhpList\Core\Domain\Repository\Subscription\SubscriberRepository;
9+
use PhpList\RestBundle\Entity\SubscriberRequest;
10+
11+
class SubscriberManager
12+
{
13+
private SubscriberRepository $subscriberRepository;
14+
15+
public function __construct(SubscriberRepository $subscriberRepository)
16+
{
17+
$this->subscriberRepository = $subscriberRepository;
18+
}
19+
20+
public function createSubscriber(SubscriberRequest $subscriberRequest): Subscriber
21+
{
22+
$subscriber = new Subscriber();
23+
$subscriber->setEmail($subscriberRequest->email);
24+
$confirmed = (bool)$subscriberRequest->request_confirmation;
25+
$subscriber->setConfirmed(!$confirmed);
26+
$subscriber->setBlacklisted(false);
27+
$subscriber->setHtmlEmail((bool)$subscriberRequest->html_email);
28+
$subscriber->setDisabled(false);
29+
30+
$this->subscriberRepository->save($subscriber);
31+
32+
return $subscriber;
33+
}
34+
}

src/Validator/RequestValidator.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpList\RestBundle\Validator;
6+
7+
use Symfony\Component\HttpFoundation\Request;
8+
use Symfony\Component\Serializer\SerializerInterface;
9+
use Symfony\Component\Validator\Validator\ValidatorInterface;
10+
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
11+
12+
class RequestValidator
13+
{
14+
public function __construct(
15+
private readonly SerializerInterface $serializer,
16+
private readonly ValidatorInterface $validator
17+
) {}
18+
19+
public function validate(Request $request, string $dtoClass): object
20+
{
21+
$dto = $this->serializer->deserialize($request->getContent(), $dtoClass, 'json');
22+
23+
$errors = $this->validator->validate($dto);
24+
25+
if (count($errors) > 0) {
26+
throw new BadRequestHttpException((string) $errors);
27+
}
28+
29+
return $dto;
30+
}
31+
}

src/Validator/UniqueEmail.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpList\RestBundle\Validator;
6+
7+
use Symfony\Component\Validator\Constraint;
8+
9+
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::IS_REPEATABLE)]
10+
class UniqueEmail extends Constraint
11+
{
12+
public string $message = 'The email "{{ value }}" is already in use.';
13+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpList\RestBundle\Validator;
6+
7+
use PhpList\Core\Domain\Repository\Subscription\SubscriberRepository;
8+
use Symfony\Component\Validator\Constraint;
9+
use Symfony\Component\Validator\ConstraintValidator;
10+
11+
class UniqueEmailValidator extends ConstraintValidator
12+
{
13+
private SubscriberRepository $subscriberRepository;
14+
15+
public function __construct(SubscriberRepository $subscriberRepository)
16+
{
17+
$this->subscriberRepository = $subscriberRepository;
18+
}
19+
20+
public function validate($value, Constraint $constraint)
21+
{
22+
/* @var $constraint UniqueEmail */
23+
24+
if (null === $value || '' === $value) {
25+
return;
26+
}
27+
28+
if ($this->subscriberRepository->findOneByEmail($value)) {
29+
$this->context->buildViolation($constraint->message)
30+
->setParameter('{{ value }}', $value)
31+
->addViolation();
32+
}
33+
}
34+
}
35+

0 commit comments

Comments
 (0)