Feat: Implement fine grained teamer profile data validation
This commit is contained in:
@@ -51,7 +51,7 @@ class ProfileController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Validate teamer data to show missing data right away
|
// 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 = $this->createForm(TeamerProfileType::class, $teamer, ['upload_session' => $uploadSession]);
|
||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|||||||
@@ -2,27 +2,29 @@
|
|||||||
|
|
||||||
namespace App\Entity\Embeddable;
|
namespace App\Entity\Embeddable;
|
||||||
|
|
||||||
use App\BusProNet\Model\Address as BpnAddress;
|
use App\Validator\Constraints as AppAssert;
|
||||||
use App\BusProNet\Model\ProfileResponse;
|
use App\BusProNet\Model\ProfileResponse;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Symfony\Component\Validator\Constraints as Assert;
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
|
|
||||||
#[ORM\Embeddable]
|
#[ORM\Embeddable]
|
||||||
|
#[AppAssert\Address(message: 'Bitte vervollständige deine Anschrift', groups: ['profile_preflight'])]
|
||||||
class Address
|
class Address
|
||||||
{
|
{
|
||||||
#[ORM\Column(type: 'string', nullable: true)]
|
#[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;
|
protected ?string $street = null;
|
||||||
|
|
||||||
#[ORM\Column(type: 'string', nullable: true)]
|
#[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;
|
protected ?string $postCode = null;
|
||||||
|
|
||||||
#[ORM\Column(type: 'string', nullable: true)]
|
#[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;
|
protected ?string $city = null;
|
||||||
|
|
||||||
#[ORM\Column(type: 'string', nullable: true)]
|
#[ORM\Column(type: 'string', nullable: true)]
|
||||||
|
#[Assert\NotBlank(message: 'Bitte gib deinen Land an', groups: ['profile'])]
|
||||||
protected ?string $country = null;
|
protected ?string $country = null;
|
||||||
|
|
||||||
public function toPayload(): array
|
public function toPayload(): array
|
||||||
|
|||||||
@@ -7,20 +7,24 @@ use Doctrine\ORM\Mapping as ORM;
|
|||||||
use Symfony\Component\Validator\Constraints as Assert;
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
|
|
||||||
#[ORM\Embeddable]
|
#[ORM\Embeddable]
|
||||||
#[AppAssert\BankAccount]
|
#[AppAssert\BankAccount(message: 'Bitte vervollständige deine Bankverbindung', groups: ['profile_preflight'])]
|
||||||
class BankAccount
|
class BankAccount
|
||||||
{
|
{
|
||||||
#[ORM\Column(nullable: true)]
|
#[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;
|
private ?string $iban = null;
|
||||||
|
|
||||||
#[ORM\Column(nullable: true)]
|
#[ORM\Column(nullable: true)]
|
||||||
|
#[Assert\NotBlank(message: 'Bitte gib den BIC an', groups: ['profile'])]
|
||||||
private ?string $bic = null;
|
private ?string $bic = null;
|
||||||
|
|
||||||
#[ORM\Column(nullable: true)]
|
#[ORM\Column(nullable: true)]
|
||||||
|
#[Assert\NotBlank(message: 'Bitte gib den Namen deiner Bank an', groups: ['profile'])]
|
||||||
private ?string $bank = null;
|
private ?string $bank = null;
|
||||||
|
|
||||||
#[ORM\Column(nullable: true)]
|
#[ORM\Column(nullable: true)]
|
||||||
|
#[Assert\NotBlank(message: 'Bitte gib den Kontoinhaber an', groups: ['profile'])]
|
||||||
private ?string $holder = null;
|
private ?string $holder = null;
|
||||||
|
|
||||||
public function getIban(bool $obfuscated = false): ?string
|
public function getIban(bool $obfuscated = false): ?string
|
||||||
|
|||||||
@@ -2,23 +2,25 @@
|
|||||||
|
|
||||||
namespace App\Entity\Embeddable;
|
namespace App\Entity\Embeddable;
|
||||||
|
|
||||||
|
use App\Validator\Constraints as AppAssert;
|
||||||
use App\BusProNet\Model\ProfileResponse;
|
use App\BusProNet\Model\ProfileResponse;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Symfony\Component\Validator\Constraints as Assert;
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
|
|
||||||
#[ORM\Embeddable]
|
#[ORM\Embeddable]
|
||||||
|
#[AppAssert\Communication(message: 'Bitte vervollständige deine Kontaktdaten', groups: ['profile_preflight'])]
|
||||||
class Communication
|
class Communication
|
||||||
{
|
{
|
||||||
#[ORM\Column(type: 'string', nullable: true)]
|
#[ORM\Column(type: 'string', nullable: true)]
|
||||||
protected ?string $phone = null;
|
protected ?string $phone = null;
|
||||||
|
|
||||||
#[ORM\Column(type: 'string', nullable: true)]
|
#[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;
|
protected ?string $mobile = null;
|
||||||
|
|
||||||
#[ORM\Column(type: 'string', nullable: true)]
|
#[ORM\Column(type: 'string', nullable: true)]
|
||||||
#[Assert\Email(mode: 'strict', message: 'Diese E-Mail-Adresse ist ungültig')]
|
#[Assert\Email(mode: 'strict', message: 'Bitte gib eine gültige E-Mail-Adresse an', groups: ['profile'])]
|
||||||
#[Assert\NotBlank(message: 'Bitte angeben')]
|
#[Assert\NotBlank(message: 'Bitte gib deine E-Mail-Adresse an', groups: ['profile'])]
|
||||||
protected ?string $email = null;
|
protected ?string $email = null;
|
||||||
|
|
||||||
public function toPayload(): array
|
public function toPayload(): array
|
||||||
|
|||||||
@@ -32,19 +32,19 @@ class Teamer implements TimestampableEntityInterface
|
|||||||
private string $uuid;
|
private string $uuid;
|
||||||
|
|
||||||
#[ORM\Column(length: 255)]
|
#[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;
|
private ?string $firstName = null;
|
||||||
|
|
||||||
#[ORM\Column(length: 255)]
|
#[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;
|
private ?string $lastName = null;
|
||||||
|
|
||||||
#[ORM\Column(length: 1)]
|
#[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;
|
private ?string $gender = null;
|
||||||
|
|
||||||
#[ORM\Column(type: Types::DATE_IMMUTABLE)]
|
#[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;
|
private ?\DateTimeImmutable $dateOfBirth = null;
|
||||||
|
|
||||||
#[ORM\Column(length: 255, nullable: true)]
|
#[ORM\Column(length: 255, nullable: true)]
|
||||||
@@ -54,7 +54,7 @@ class Teamer implements TimestampableEntityInterface
|
|||||||
private ?string $salutation = null;
|
private ?string $salutation = null;
|
||||||
|
|
||||||
#[ORM\Column(length: 255, nullable: true)]
|
#[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;
|
private ?string $nationality = null;
|
||||||
|
|
||||||
#[ORM\Embedded(class: Address::class)]
|
#[ORM\Embedded(class: Address::class)]
|
||||||
@@ -70,11 +70,11 @@ class Teamer implements TimestampableEntityInterface
|
|||||||
private ?BankAccount $bankAccount = null;
|
private ?BankAccount $bankAccount = null;
|
||||||
|
|
||||||
#[ORM\Column(length: 255, nullable: true)]
|
#[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;
|
private ?string $taxId = null;
|
||||||
|
|
||||||
#[ORM\Column(length: 255, nullable: true)]
|
#[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;
|
private ?string $healthInsuranceCompany = null;
|
||||||
|
|
||||||
#[ORM\Column(length: 32)]
|
#[ORM\Column(length: 32)]
|
||||||
@@ -87,6 +87,7 @@ class Teamer implements TimestampableEntityInterface
|
|||||||
private Collection $availabilities;
|
private Collection $availabilities;
|
||||||
|
|
||||||
#[ORM\OneToOne(cascade: ['persist', 'remove'])]
|
#[ORM\OneToOne(cascade: ['persist', 'remove'])]
|
||||||
|
#[Assert\NotNull(message: 'Bitte lade ein Foto von dir hoch', groups: ['profile', 'profile_preflight'])]
|
||||||
private ?Upload $photo = null;
|
private ?Upload $photo = null;
|
||||||
|
|
||||||
#[ORM\OneToMany(mappedBy: 'teamer', targetEntity: Feedback::class)]
|
#[ORM\OneToMany(mappedBy: 'teamer', targetEntity: Feedback::class)]
|
||||||
|
|||||||
@@ -70,13 +70,14 @@ class TeamerProfileType extends AbstractType
|
|||||||
],
|
],
|
||||||
])
|
])
|
||||||
->add('address', AddressType::class, [
|
->add('address', AddressType::class, [
|
||||||
'label' => 'false',
|
'label' => false,
|
||||||
])
|
])
|
||||||
->add('communication', CommunicationType::class, [
|
->add('communication', CommunicationType::class, [
|
||||||
'label' => false,
|
'label' => false,
|
||||||
])
|
])
|
||||||
->add('bankAccount', BankAccountType::class, [
|
->add('bankAccount', BankAccountType::class, [
|
||||||
'label' => false,
|
'label' => false,
|
||||||
|
'error_bubbling' => false,
|
||||||
])
|
])
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
@@ -95,6 +96,9 @@ class TeamerProfileType extends AbstractType
|
|||||||
$resolver
|
$resolver
|
||||||
->setDefaults([
|
->setDefaults([
|
||||||
'data_class' => Teamer::class,
|
'data_class' => Teamer::class,
|
||||||
|
'validation_groups' => [
|
||||||
|
'profile',
|
||||||
|
],
|
||||||
])
|
])
|
||||||
->setRequired(['upload_session'])
|
->setRequired(['upload_session'])
|
||||||
->setAllowedTypes('upload_session', UploadSessionDto::class)
|
->setAllowedTypes('upload_session', UploadSessionDto::class)
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Validator\Constraints;
|
||||||
|
|
||||||
|
use Symfony\Component\Validator\Constraint;
|
||||||
|
|
||||||
|
#[\Attribute]
|
||||||
|
class Address extends Constraint
|
||||||
|
{
|
||||||
|
public string $message = 'Die Anschrift ist unvollständig';
|
||||||
|
|
||||||
|
public function __construct(string $message = null, array $groups = null, $payload = null)
|
||||||
|
{
|
||||||
|
parent::__construct([], $groups, $payload);
|
||||||
|
|
||||||
|
$this->message = $message ?? $this->message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTargets(): string
|
||||||
|
{
|
||||||
|
return Constraint::CLASS_CONSTRAINT;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Validator\Constraints;
|
||||||
|
|
||||||
|
use App\Entity\Embeddable\Address as AddressEntity;
|
||||||
|
use Symfony\Component\Validator\Constraint;
|
||||||
|
use Symfony\Component\Validator\ConstraintValidator;
|
||||||
|
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||||
|
|
||||||
|
class AddressValidator extends ConstraintValidator
|
||||||
|
{
|
||||||
|
public function validate(mixed $value, Constraint $constraint): void
|
||||||
|
{
|
||||||
|
if (!$value instanceof AddressEntity) {
|
||||||
|
throw new UnexpectedTypeException($value, AddressEntity::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$constraint instanceof Address) {
|
||||||
|
throw new UnexpectedTypeException($constraint, Address::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
$isIncomplete = null === $value->getStreet()
|
||||||
|
|| null === $value->getCity()
|
||||||
|
|| null === $value->getPostCode()
|
||||||
|
|| null === $value->getCountry()
|
||||||
|
;
|
||||||
|
|
||||||
|
if ($isIncomplete) {
|
||||||
|
$this->context
|
||||||
|
->buildViolation($constraint->message)
|
||||||
|
->addViolation()
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,7 +28,6 @@ class BankAccountValidator extends ConstraintValidator
|
|||||||
if ($isIncomplete) {
|
if ($isIncomplete) {
|
||||||
$this->context
|
$this->context
|
||||||
->buildViolation($constraint->message)
|
->buildViolation($constraint->message)
|
||||||
->atPath('iban')
|
|
||||||
->addViolation()
|
->addViolation()
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Validator\Constraints;
|
||||||
|
|
||||||
|
use Symfony\Component\Validator\Constraint;
|
||||||
|
|
||||||
|
#[\Attribute]
|
||||||
|
class Communication extends Constraint
|
||||||
|
{
|
||||||
|
public string $message = 'Die Kontaktdaten sind unvollständig';
|
||||||
|
|
||||||
|
public function __construct(string $message = null, array $groups = null, $payload = null)
|
||||||
|
{
|
||||||
|
parent::__construct([], $groups, $payload);
|
||||||
|
|
||||||
|
$this->message = $message ?? $this->message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTargets(): string
|
||||||
|
{
|
||||||
|
return Constraint::CLASS_CONSTRAINT;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Validator\Constraints;
|
||||||
|
|
||||||
|
use App\Entity\Embeddable\Communication as CommunicationEntity;
|
||||||
|
use Symfony\Component\Validator\Constraint;
|
||||||
|
use Symfony\Component\Validator\ConstraintValidator;
|
||||||
|
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||||
|
|
||||||
|
class CommunicationValidator extends ConstraintValidator
|
||||||
|
{
|
||||||
|
public function validate(mixed $value, Constraint $constraint): void
|
||||||
|
{
|
||||||
|
if (!$value instanceof CommunicationEntity) {
|
||||||
|
throw new UnexpectedTypeException($value, CommunicationEntity::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$constraint instanceof Communication) {
|
||||||
|
throw new UnexpectedTypeException($constraint, Communication::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
$isIncomplete = null === $value->getEmail() || null === $value->getMobile();
|
||||||
|
|
||||||
|
if ($isIncomplete) {
|
||||||
|
$this->context
|
||||||
|
->buildViolation($constraint->message)
|
||||||
|
->addViolation()
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,12 +7,21 @@
|
|||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
<div{% with {attr: row_attr} %}{{ block('attributes') }}{% endwith %}>
|
<div{% with {attr: row_attr} %}{{ block('attributes') }}{% endwith %}>
|
||||||
{{- form_label(form) -}}
|
{{- form_label(form) -}}
|
||||||
{{- form_errors(form) -}}
|
|
||||||
{{- form_widget(form, widget_attr) -}}
|
{{- form_widget(form, widget_attr) -}}
|
||||||
|
{{- form_errors(form) -}}
|
||||||
{{- form_help(form) -}}
|
{{- form_help(form) -}}
|
||||||
</div>
|
</div>
|
||||||
{%- endblock form_row -%}
|
{%- 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 -%}
|
{%- block form_errors -%}
|
||||||
{%- if errors|length > 0 -%}
|
{%- if errors|length > 0 -%}
|
||||||
<ul>
|
<ul>
|
||||||
|
|||||||
@@ -73,6 +73,7 @@
|
|||||||
Anschrift
|
Anschrift
|
||||||
</h3>
|
</h3>
|
||||||
<div class="flex flex-col space-y-4 pb-8">
|
<div class="flex flex-col space-y-4 pb-8">
|
||||||
|
{{ form_errors(form.address) }}
|
||||||
{{ form_row(form.address.street) }}
|
{{ form_row(form.address.street) }}
|
||||||
{{ form_row(form.address.postCode) }}
|
{{ form_row(form.address.postCode) }}
|
||||||
{{ form_row(form.address.city) }}
|
{{ form_row(form.address.city) }}
|
||||||
@@ -82,6 +83,7 @@
|
|||||||
Kontakt
|
Kontakt
|
||||||
</h3>
|
</h3>
|
||||||
<div class="flex flex-col space-y-4">
|
<div class="flex flex-col space-y-4">
|
||||||
|
{{ form_errors(form.communication) }}
|
||||||
{{ form_row(form.communication.email) }}
|
{{ form_row(form.communication.email) }}
|
||||||
{{ form_row(form.communication.phone) }}
|
{{ form_row(form.communication.phone) }}
|
||||||
{{ form_row(form.communication.mobile) }}
|
{{ form_row(form.communication.mobile) }}
|
||||||
@@ -93,6 +95,7 @@
|
|||||||
Bankverbindung
|
Bankverbindung
|
||||||
</h3>
|
</h3>
|
||||||
<div class="flex flex-col space-y-4">
|
<div class="flex flex-col space-y-4">
|
||||||
|
{{ form_errors(form.bankAccount) }}
|
||||||
{{ form_row(form.bankAccount.iban) }}
|
{{ form_row(form.bankAccount.iban) }}
|
||||||
{{ form_row(form.bankAccount.bic) }}
|
{{ form_row(form.bankAccount.bic) }}
|
||||||
{{ form_row(form.bankAccount.bank) }}
|
{{ form_row(form.bankAccount.bank) }}
|
||||||
|
|||||||
Reference in New Issue
Block a user