Як послідовно застосовувати групи валідації
Дата оновлення перекладу 2024-06-08
Як послідовно застосовувати групи валідації
У деяких випадках, вам буде потрібно валідувати ваші групи покроково. Щоб зробити це,
ви можете використати функцію GroupSequence
. У цьому випадку, обʼєкт визначає групову
послідовність, яка визначає порядок, в якому потрібно валідувати групи.
Наприклад, уявіть, що у вас є клас User
, і ви хочете валідувати, чи відрізняються
імʼя користувача та пароль лише у випадку, якщо решта валідації пройде успішно (щоб
уникнути багатьох повідомлень про помилку).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
// src/Entity/User.php
namespace App\Entity;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;
#[Assert\GroupSequence(['User', 'Strict'])]
class User implements UserInterface
{
#[Assert\NotBlank]
private string $username;
#[Assert\NotBlank]
private string $password;
#[Assert\IsTrue(
message: 'Пароль не може співпадати з вашим іменем користувача',
groups: ['Strict'],
)]
public function isPasswordSafe(): bool
{
return ($this->username !== $this->password);
}
}
У цьому прикладі, спочатку будуть валідовані всі обмеження в групі User
(що те ж саме, що і група Default
). Лише якщо всі обмеження у цій групі
будуть валідні, буде валідована друга група Strict
.
Caution
Як ви вже бачили в Як застосувати лише підмножину усіх ваших обмежень валідації (групи валідації), група Default
і група, що містить
імʼя класу (наприклад, User
) були ідентичні. Однак, при використанні групової
послідовності, вони більше не будуть ідентичними. Група Default
тепер посилатиметься
на групову послідовність замість всіх обмежень, які не належать жодній групі.
Це означає, що вам потрібно використати групу {ClassName}
(наприклад, User
)
при вказанні групової послідовності. При використанні Default
ви отримаєте нескінченну
рекурсію (так як група Default
посилається на групову послідовність, яка містить
групу Default
, яка посилається на ту ж групову послідовність...).
Caution
Виклик validate()
з групою у послідовності (Strict
у попередньому прикаді)
призведе до валідації лише з цією групою, а не з усіма групами у послідовності.
Тому що послідовність тепер спрямована до групової валідації Default
.
Ви також можете визначити групову послідовність в опції форми validation_groups
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// src/Form/MyType.php
namespace App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\GroupSequence;
// ...
class MyType extends AbstractType
{
// ...
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'validation_groups' => new GroupSequence(['First', 'Second']),
]);
}
}
Постачальники групової послідовності
Уявіть сутність User
, яка може бути нормальним або преміум користувачем. Якщо це преміум
користувач, необхідно додати деякі додаткові обмеження до сутності користувача (наприклад,
інформацію про кредитну картку). Щоб динамічно визначити, які групи варто активувати, ви можете
створити постачальника групової послідовності. Спочатку створіть сутність та нову групу обмежень
під назвою Premium
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// src/Entity/User.php
namespace App\Entity;
use Symfony\Component\Validator\Constraints as Assert;
class User
{
#[Assert\NotBlank]
private string $name;
#[Assert\CardScheme(
schemes: [Assert\CardScheme::VISA],
groups: ['Premium'],
)]
private string $creditCard;
// ...
}
Тепер змініть клас User
, щоб реалізувати
GroupSequenceProviderInterface та додайте метод
getGroupSequence(),
який має повернути масив груп для використання:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
// src/Entity/User.php
namespace App\Entity;
// ...
use Symfony\Component\Validator\GroupSequenceProviderInterface;
class User implements GroupSequenceProviderInterface
{
// ...
public function getGroupSequence(): array|GroupSequence
{
// при поверненні простого масиву, якщо в будь-якій групі є порушення,
// решта груп не валідується. Наприклад, якщо 'User' зазнає невдачі,
// 'Premium' та 'Api' не валідуються:
return ['User', 'Premium', 'Api'];
// при поверненні вкладеного масиву, всі групи, включені у кожний масив,
// валідуються. Наприклад, якщо 'User' зазнає невдачі, 'Premium' також валідується
// (і ви отримаєте і його порушення), але 'Api' не буде валідовано:
return [['User', 'Premium'], 'Api'];
}
}
Нарешті, вам потрібно повідомити компонент Validator про те, що ваш клас User
нада послідовність груп для валідації:
1 2 3 4 5 6 7 8 9 10
// src/Entity/User.php
namespace App\Entity;
// ...
#[Assert\GroupSequenceProvider]
class User implements GroupSequenceProviderInterface
{
// ...
}
Просунутий постачальник груп валідації
У попередньому розділі ви дізналися, як змінювати послідовність груп динамічно на основі стану вашої сутності. Однак, у більш складних випадках вам може знадобитися використання зовнішньої конфігурації або сервісу для визначення цієї послідовність груп.
Керування ініціалізацією сутності та ручне встановлення її залежностей може бути громіздким, а реалізація може не узгоджуватися з обов'язками сутності. Щоб вирішити цю проблему, ви можете сконфігурувати реалізацію за допомогою GroupProviderInterface поза сутністю і навіть зареєструвати постачальника груп як сервіс.
Ось як ви можете цього досягти:
- Визначте окремий клас постачальника груп: створіть клас, який реалізує
- GroupProviderInterface` і обробляє логіку динамічної послідовності груп;
- Налаштуйте користувача з постачальником: використовуйте опцію
provider
атрибуту GroupSequenceProvider, щоб зв'язати сутність з класом постачальника;- Автомонтування або ручне тегування: якщо автомонтування увімкнено, ваш користувацький постачальник буде зв'язано автоматично. В іншому випадку, ви повинні тегувати ваш сервіс вручну за допомогою тегу
validator.group_provider
.
1 2 3 4 5 6 7 8 9 10 11
// src/Entity/User.php
namespace App\Entity;
// ...
use App\Validator\UserGroupProvider;
#[Assert\GroupSequenceProvider(provider: UserGroupProvider::class)]
class User
{
// ...
}
За допомогою цього підходу ви можете підтримувати чіткий розподіл між структурою сутності та логікою групової послідовності, що дозволяє створювати більш просунуті варіанти використання.
Як послідовно застосовувати обмеження в одній властивості
Індові вам може захотітися застосувати обмеження послідовно в одній властивості.
Обмеження Sequentially може вирішити
це за вас простішим способом, ніж використання GroupSequence
.