From 7f7a35e7a2f8484b70f492afbc8f581d83de9016 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Tue, 19 Sep 2023 18:42:01 +0200 Subject: [PATCH] Feat: Implement fine grained teamer profile data validation --- src/Controller/Teamer/ProfileController.php | 2 +- src/Entity/Embeddable/Address.php | 10 +++--- src/Entity/Embeddable/BankAccount.php | 8 +++-- src/Entity/Embeddable/Communication.php | 8 +++-- src/Entity/Teamer.php | 15 ++++---- src/Form/TeamerProfileType.php | 6 +++- src/Validator/Constraints/Address.php | 23 ++++++++++++ .../Constraints/AddressValidator.php | 35 +++++++++++++++++++ .../Constraints/BankAccountValidator.php | 1 - src/Validator/Constraints/Communication.php | 23 ++++++++++++ .../Constraints/CommunicationValidator.php | 31 ++++++++++++++++ templates/forms.html.twig | 11 +++++- templates/teamer/profile.html.twig | 3 ++ 13 files changed, 156 insertions(+), 20 deletions(-) create mode 100644 src/Validator/Constraints/Address.php create mode 100644 src/Validator/Constraints/AddressValidator.php create mode 100644 src/Validator/Constraints/Communication.php create mode 100644 src/Validator/Constraints/CommunicationValidator.php diff --git a/src/Controller/Teamer/ProfileController.php b/src/Controller/Teamer/ProfileController.php index 2581728..3225890 100644 --- a/src/Controller/Teamer/ProfileController.php +++ b/src/Controller/Teamer/ProfileController.php @@ -51,7 +51,7 @@ class ProfileController extends AbstractController } // Validate teamer data to show missing data right away - $errors = $this->validator->validate($teamer); + $errors = $this->validator->validate($teamer, null, ['profile_preflight']); $form = $this->createForm(TeamerProfileType::class, $teamer, ['upload_session' => $uploadSession]); $form->handleRequest($request); diff --git a/src/Entity/Embeddable/Address.php b/src/Entity/Embeddable/Address.php index 320e88f..6a3f21f 100644 --- a/src/Entity/Embeddable/Address.php +++ b/src/Entity/Embeddable/Address.php @@ -2,27 +2,29 @@ namespace App\Entity\Embeddable; -use App\BusProNet\Model\Address as BpnAddress; +use App\Validator\Constraints as AppAssert; use App\BusProNet\Model\ProfileResponse; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; #[ORM\Embeddable] +#[AppAssert\Address(message: 'Bitte vervollständige deine Anschrift', groups: ['profile_preflight'])] class Address { #[ORM\Column(type: 'string', nullable: true)] - #[Assert\NotBlank(message: 'Bitte gib deine Straße an')] + #[Assert\NotBlank(message: 'Bitte gib deine Straße an', groups: ['profile'])] protected ?string $street = null; #[ORM\Column(type: 'string', nullable: true)] - #[Assert\NotBlank(message: 'Bitte gib deine Postleitzahl an')] + #[Assert\NotBlank(message: 'Bitte gib deine Postleitzahl an', groups: ['profile'])] protected ?string $postCode = null; #[ORM\Column(type: 'string', nullable: true)] - #[Assert\NotBlank(message: 'Bitte gib deinen Ort an')] + #[Assert\NotBlank(message: 'Bitte gib deinen Ort an', groups: ['profile'])] protected ?string $city = null; #[ORM\Column(type: 'string', nullable: true)] + #[Assert\NotBlank(message: 'Bitte gib deinen Land an', groups: ['profile'])] protected ?string $country = null; public function toPayload(): array diff --git a/src/Entity/Embeddable/BankAccount.php b/src/Entity/Embeddable/BankAccount.php index 25ab6b3..2f51d89 100644 --- a/src/Entity/Embeddable/BankAccount.php +++ b/src/Entity/Embeddable/BankAccount.php @@ -7,20 +7,24 @@ use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; #[ORM\Embeddable] -#[AppAssert\BankAccount] +#[AppAssert\BankAccount(message: 'Bitte vervollständige deine Bankverbindung', groups: ['profile_preflight'])] class BankAccount { #[ORM\Column(nullable: true)] - #[Assert\Iban(message: 'Die IBAN ist ungültig')] + #[Assert\NotBlank(message: 'Bitte gib die IBAN an', groups: ['profile'])] + #[Assert\Iban(message: 'Bitte gib eine gültige IBAN an', groups: ['profile'])] private ?string $iban = null; #[ORM\Column(nullable: true)] + #[Assert\NotBlank(message: 'Bitte gib den BIC an', groups: ['profile'])] private ?string $bic = null; #[ORM\Column(nullable: true)] + #[Assert\NotBlank(message: 'Bitte gib den Namen deiner Bank an', groups: ['profile'])] private ?string $bank = null; #[ORM\Column(nullable: true)] + #[Assert\NotBlank(message: 'Bitte gib den Kontoinhaber an', groups: ['profile'])] private ?string $holder = null; public function getIban(bool $obfuscated = false): ?string diff --git a/src/Entity/Embeddable/Communication.php b/src/Entity/Embeddable/Communication.php index a974be5..292d4a7 100644 --- a/src/Entity/Embeddable/Communication.php +++ b/src/Entity/Embeddable/Communication.php @@ -2,23 +2,25 @@ namespace App\Entity\Embeddable; +use App\Validator\Constraints as AppAssert; use App\BusProNet\Model\ProfileResponse; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; #[ORM\Embeddable] +#[AppAssert\Communication(message: 'Bitte vervollständige deine Kontaktdaten', groups: ['profile_preflight'])] class Communication { #[ORM\Column(type: 'string', nullable: true)] protected ?string $phone = null; #[ORM\Column(type: 'string', nullable: true)] - #[Assert\NotBlank(message: 'Bitte angeben')] + #[Assert\NotBlank(message: 'Bitte gib deine Mobilnummer an', groups: ['profile'])] protected ?string $mobile = null; #[ORM\Column(type: 'string', nullable: true)] - #[Assert\Email(mode: 'strict', message: 'Diese E-Mail-Adresse ist ungültig')] - #[Assert\NotBlank(message: 'Bitte angeben')] + #[Assert\Email(mode: 'strict', message: 'Bitte gib eine gültige E-Mail-Adresse an', groups: ['profile'])] + #[Assert\NotBlank(message: 'Bitte gib deine E-Mail-Adresse an', groups: ['profile'])] protected ?string $email = null; public function toPayload(): array diff --git a/src/Entity/Teamer.php b/src/Entity/Teamer.php index 84e44c1..543b164 100644 --- a/src/Entity/Teamer.php +++ b/src/Entity/Teamer.php @@ -32,19 +32,19 @@ class Teamer implements TimestampableEntityInterface private string $uuid; #[ORM\Column(length: 255)] - #[Assert\NotBlank(message: 'Bitte gib deinen Vornamen an')] + #[Assert\NotBlank(message: 'Bitte gib deinen Vornamen an', groups: ['profile'])] private ?string $firstName = null; #[ORM\Column(length: 255)] - #[Assert\NotBlank(message: 'Bitte gib deinen Nachnamen an')] + #[Assert\NotBlank(message: 'Bitte gib deinen Nachnamen an', groups: ['profile'])] private ?string $lastName = null; #[ORM\Column(length: 1)] - #[Assert\NotBlank(message: 'Bitte gib dein Geschlecht an')] + #[Assert\NotBlank(message: 'Bitte gib dein Geschlecht an', groups: ['profile'])] private ?string $gender = null; #[ORM\Column(type: Types::DATE_IMMUTABLE)] - #[Assert\NotNull(message: 'Bitte gib dein Geburtsdatum an')] + #[Assert\NotNull(message: 'Bitte gib dein Geburtsdatum an', groups: ['profile'])] private ?\DateTimeImmutable $dateOfBirth = null; #[ORM\Column(length: 255, nullable: true)] @@ -54,7 +54,7 @@ class Teamer implements TimestampableEntityInterface private ?string $salutation = null; #[ORM\Column(length: 255, nullable: true)] - #[Assert\NotBlank(message: 'Bitte gib deine Nationalität an')] + #[Assert\NotBlank(message: 'Bitte gib deine Nationalität an', groups: ['profile'])] private ?string $nationality = null; #[ORM\Embedded(class: Address::class)] @@ -70,11 +70,11 @@ class Teamer implements TimestampableEntityInterface private ?BankAccount $bankAccount = null; #[ORM\Column(length: 255, nullable: true)] - #[Assert\NotBlank(message: 'Bitte gib deine SteuerID an')] + #[Assert\NotBlank(message: 'Bitte gib deine SteuerID an', groups: ['profile', 'profile_preflight'])] private ?string $taxId = null; #[ORM\Column(length: 255, nullable: true)] - #[Assert\NotBlank(message: 'Bitte gib deine Krankenversicherung an')] + #[Assert\NotBlank(message: 'Bitte gib deine Krankenversicherung an', groups: ['profile', 'profile_preflight'])] private ?string $healthInsuranceCompany = null; #[ORM\Column(length: 32)] @@ -87,6 +87,7 @@ class Teamer implements TimestampableEntityInterface private Collection $availabilities; #[ORM\OneToOne(cascade: ['persist', 'remove'])] + #[Assert\NotNull(message: 'Bitte lade ein Foto von dir hoch', groups: ['profile', 'profile_preflight'])] private ?Upload $photo = null; #[ORM\OneToMany(mappedBy: 'teamer', targetEntity: Feedback::class)] diff --git a/src/Form/TeamerProfileType.php b/src/Form/TeamerProfileType.php index 2a2ff7f..f8372c2 100644 --- a/src/Form/TeamerProfileType.php +++ b/src/Form/TeamerProfileType.php @@ -70,13 +70,14 @@ class TeamerProfileType extends AbstractType ], ]) ->add('address', AddressType::class, [ - 'label' => 'false', + 'label' => false, ]) ->add('communication', CommunicationType::class, [ 'label' => false, ]) ->add('bankAccount', BankAccountType::class, [ 'label' => false, + 'error_bubbling' => false, ]) ; } @@ -95,6 +96,9 @@ class TeamerProfileType extends AbstractType $resolver ->setDefaults([ 'data_class' => Teamer::class, + 'validation_groups' => [ + 'profile', + ], ]) ->setRequired(['upload_session']) ->setAllowedTypes('upload_session', UploadSessionDto::class) diff --git a/src/Validator/Constraints/Address.php b/src/Validator/Constraints/Address.php new file mode 100644 index 0000000..7b173a0 --- /dev/null +++ b/src/Validator/Constraints/Address.php @@ -0,0 +1,23 @@ +message = $message ?? $this->message; + } + + public function getTargets(): string + { + return Constraint::CLASS_CONSTRAINT; + } +} \ No newline at end of file diff --git a/src/Validator/Constraints/AddressValidator.php b/src/Validator/Constraints/AddressValidator.php new file mode 100644 index 0000000..1a2f19d --- /dev/null +++ b/src/Validator/Constraints/AddressValidator.php @@ -0,0 +1,35 @@ +getStreet() + || null === $value->getCity() + || null === $value->getPostCode() + || null === $value->getCountry() + ; + + if ($isIncomplete) { + $this->context + ->buildViolation($constraint->message) + ->addViolation() + ; + } + } +} \ No newline at end of file diff --git a/src/Validator/Constraints/BankAccountValidator.php b/src/Validator/Constraints/BankAccountValidator.php index e1c9ebc..c584c8c 100644 --- a/src/Validator/Constraints/BankAccountValidator.php +++ b/src/Validator/Constraints/BankAccountValidator.php @@ -28,7 +28,6 @@ class BankAccountValidator extends ConstraintValidator if ($isIncomplete) { $this->context ->buildViolation($constraint->message) - ->atPath('iban') ->addViolation() ; } diff --git a/src/Validator/Constraints/Communication.php b/src/Validator/Constraints/Communication.php new file mode 100644 index 0000000..174a8d6 --- /dev/null +++ b/src/Validator/Constraints/Communication.php @@ -0,0 +1,23 @@ +message = $message ?? $this->message; + } + + public function getTargets(): string + { + return Constraint::CLASS_CONSTRAINT; + } +} \ No newline at end of file diff --git a/src/Validator/Constraints/CommunicationValidator.php b/src/Validator/Constraints/CommunicationValidator.php new file mode 100644 index 0000000..b33b322 --- /dev/null +++ b/src/Validator/Constraints/CommunicationValidator.php @@ -0,0 +1,31 @@ +getEmail() || null === $value->getMobile(); + + if ($isIncomplete) { + $this->context + ->buildViolation($constraint->message) + ->addViolation() + ; + } + } +} \ No newline at end of file diff --git a/templates/forms.html.twig b/templates/forms.html.twig index 62e4072..07731ef 100644 --- a/templates/forms.html.twig +++ b/templates/forms.html.twig @@ -7,12 +7,21 @@ {%- endif -%} {{- form_label(form) -}} - {{- form_errors(form) -}} {{- form_widget(form, widget_attr) -}} + {{- form_errors(form) -}} {{- form_help(form) -}} {%- endblock form_row -%} +{%- block form_label -%} + {% set class = ' font-bold' %} + {% if errors|length %} + {% set class = class ~ ' text-red-500' %} + {% endif %} + {% set label_attr = label_attr|merge({'class': (label_attr.class|default('') ~ class)|trim}) %} + {{ parent() }} +{%- endblock form_label -%} + {%- block form_errors -%} {%- if errors|length > 0 -%}