Skip to main content

Walidacja encji

System WiseB2B umożliwia walidację encji za pomocą dwóch sposobów:

  1. Wykorzystując atrybuty assercji
  2. Napisanie dedykowanego walidatora

Walidatory są uruchamiane przed zapisem encji do bazy danych bądź przed modyfikacją encji (zapisem zmiany do bazy danych).

Assercje symfonowe

Do walidacji pól możemy wykorzystać Assercje symfonowe.

Aby np. wskazać iż pole musi być zawsze uzupełnione (nie może być puste) możemy skorzystać z atrybutu NotBlank:

use Wise\Core\Validator\Constraints as WiseAssert;

class Receiver extends AbstractEntity
{
#[WiseAssert\NotBlank(
payload: ["constraintType" => ConstraintTypeEnum::ERROR],
)]
protected ?string $lastName = null;
}

Dedykowane walidatory

W szczególnych przypadkach możemy napisać nasz dedykowany walidator. Nie musi to być jeden walidator dla całej encji, ale możemy stworzyć wiele walidatorów dla różnych pól (jednak czytelniej jest przygotowanie jeden walidując wiele pól)

Sama walidacja assercji to standardowy walidator, który kontroluje assercje Symphonowe.

Utworzenie walidatora

  1. Utwórz nowy walidator w warstwie domenowej, który dziedziczy po \Wise\Core\Validator\AbstractValidator
  2. W konstruktorze przekaż NotificationManagerInterface i wywołaj konstruktor klasy bazowej
  3. Zaimplementuj metodę supports która sprawdza czy obiekt jest obsługiwany przez walidator
  4. Zaimplementuj metodę validate która zawiera logikę walidacji
  5. Dodaj walidator do kontenera serwisów w pliku config/services.yaml wskazując na tag wise.validator ponieważ mechanizm walidacji wykorzystuje mechanizm providerów przykład:
Wise\Receiver\Domain\Receiver\Validator\ReceiverValidator:
tags: [ 'wise.validator' ]

Metoda validate - Walidacja encji

W momencie wykrycia błędu możemy wskazać błąd na dwa sposoby

  1. Wyrzucenie wyjątku W tym celu utwórz nowy wyjątek dziedziczący po \Wise\Core\Exception\CommonLogicException i rzuć go w metodzie validate

UWAGA! Nie zapomnij o dodaniu translacji

  1. Możemy bezpośrednio wskazać pole, którego dotyczy błąd za pomocą wywołania metody add w obiekcie ConstraintViolationList
$this->constraintViolationList->add(
new ConstraintViolation(
message: 'Pole imię nie może być puste',
messageTemplate: null,
parameters: [],
root: $receiver,
propertyPath: 'firstName',
invalidValue: $receiver->getFirstName(),
constraint: (new CustomConstraint())->setConstraintType(ConstraintTypeEnum::ERROR)
)
);

Właśnie w taki sposób możemy powiedzieć, że ktoś wypełnił błędne pole w komunikacie.

Zalecane jest aby w Message nie pisać bezpośrednio tekstu, a korzystać z tłumaczeń \Symfony\Contracts\Translation\TranslatorInterface

message - to jest tekst błędu propertyPath - wskazuje na pole, które jest błędne invalidValue - wskazuje na wartość, która jest błędna constraint - wskazuje na typ błędu, który jest zwracany (ERROR, WARNING, INFO)

Metoda handle - Obsługa błędów

Tak jak metoda validate jest odpowiedzialna za walidację, tak metoda handle jest odpowiedzialna za obsługę błędów.

Domyślnie metoda jest zaimplementowana i nie wymaga przeciążenia lecz w pewnych przypadkach może być to konieczne.

Przkładowy walidator

<?php
namespace Wise\Receiver\Domain\Receiver\Validator;

use Symfony\Component\Validator\ConstraintViolation;
use Symfony\Component\Validator\ConstraintViolationList;
use Wise\Core\Model\ValidatableInterface;
use Wise\Core\Notifications\Notification;
use Wise\Core\Notifications\NotificationManagerInterface;
use Wise\Core\Validator\AbstractValidator;
use Wise\Core\Validator\Constraints\CustomConstraint;
use Wise\Core\Validator\Enum\ConstraintTypeEnum;
use Wise\Receiver\Domain\Receiver\Receiver;

/**
* Przykład domenowej obsługi policy
*/
class ReceiverValidator extends AbstractValidator
{

public function __construct(
private readonly NotificationManagerInterface $notificationManager,
){
parent::__construct($notificationManager);
}

/**
* @param $object
* @return bool
* Obsługujemy tym serwisem walidacyjnym wszystkie obiekty typu Receiver
*/
public function supports($object):bool
{
return $object instanceof Receiver;
}

/**
* @param ValidatableInterface $object
* @return void
*/
public function validate($object): void
{
/** @var Receiver $object */
$receiver = $object;

if(empty($receiver->getName())){
$this->constraintViolationList->add(
new ConstraintViolation(
message: 'Musisz podać nazwę firmy',
messageTemplate: null,
parameters: [],
root: $receiver,
propertyPath: 'name',
invalidValue: $receiver->getName(),
constraint: (new CustomConstraint())->setConstraintType(ConstraintTypeEnum::ERROR)
)
);
}

if(empty($receiver->getFirstName())){
$this->constraintViolationList->add(
new ConstraintViolation(
message: 'Pole imię nie może być puste',
messageTemplate: null,
parameters: [],
root: $receiver,
propertyPath: 'firstName',
invalidValue: $receiver->getFirstName(),
constraint: (new CustomConstraint())->setConstraintType(ConstraintTypeEnum::ERROR)
)
);
}
}

public function handle(): void
{
/**
* @var ConstraintViolation $constraintViolation
*/
foreach ($this->constraintViolationList as $constraintViolation){
try{
$constraintType = $constraintViolation->getConstraint()->payload['constraintType'];
}catch (\Exception $exception){
$constraintType = ConstraintTypeEnum::ERROR;
}

$notification = Notification::fromConstraintViolation($constraintViolation, $constraintType, Receiver::class);

$this->notificationManager->add($notification);
}
}

public function getConstraintViolationList(): ConstraintViolationList
{
return $this->constraintViolationList;
}
}

Deklaracja w services.yaml:

services:
Wise\Receiver\Domain\Receiver\Validator\ReceiverValidator:
tags: [ 'wise.validator' ]