feat: validate application date range against existing dispositions

This commit is contained in:
Björn Fromme
2024-09-23 15:38:09 +02:00
parent 7d816d36ec
commit 6ca41f6b9c
5 changed files with 85 additions and 1 deletions
+2
View File
@@ -4,11 +4,13 @@ namespace App\Entity;
use App\Entity\Traits\TimestampableEntity; use App\Entity\Traits\TimestampableEntity;
use App\Repository\ApplicationRepository; use App\Repository\ApplicationRepository;
use App\Validator\Constraints as AppAssert;
use Doctrine\DBAL\Types\Types; use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Uid\Uuid; use Symfony\Component\Uid\Uuid;
#[ORM\Entity(repositoryClass: ApplicationRepository::class)] #[ORM\Entity(repositoryClass: ApplicationRepository::class)]
#[AppAssert\Application]
class Application implements TimestampableEntityInterface class Application implements TimestampableEntityInterface
{ {
use TimestampableEntity; use TimestampableEntity;
+16
View File
@@ -0,0 +1,16 @@
<?php
namespace App\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
#[\Attribute]
class Application extends Constraint
{
public string $message = 'Diese Bewerbung überlappt sich mit einem deiner bestätigten Einsätze';
public function getTargets(): array|string
{
return static::CLASS_CONSTRAINT;
}
}
@@ -0,0 +1,65 @@
<?php
namespace App\Validator\Constraints;
use App\Repository\DispositionRepository;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class ApplicationValidator extends ConstraintValidator
{
public function __construct(private readonly DispositionRepository $dispositionRepository)
{
}
public function validate(mixed $value, Constraint $constraint): void
{
/** @var \App\Entity\Application $application */
$application = $value;
$teamer = $application->getTeamer();
$assignment = $application->getAssignment();
$destination = $assignment->getDestination();
$applicationDateFrom = $destination->getDateFrom();
$applicationDateTo = $destination->getDateTo();
$qb = $this
->dispositionRepository
->createQueryBuilder('disposition')
;
$conflictingCount = $qb
->select($qb->expr()->count('disposition'))
->innerJoin('disposition.assignment', 'assignment')
->innerJoin('assignment.destination', 'destination')
->where($qb->expr()->andX(
$qb->expr()->eq('disposition.teamer', ':teamer'),
$qb->expr()->orX(
$qb->expr()->andX(
$qb->expr()->gt('destination.dateTo', ':application_date_from'),
$qb->expr()->lt('destination.dateTo', ':application_date_to'),
),
$qb->expr()->andX(
$qb->expr()->lt('destination.dateFrom', ':application_date_to'),
$qb->expr()->gt('destination.dateFrom', ':application_date_from'),
)
)
))
->setParameter('teamer', $teamer)
->setParameter('application_date_from', $applicationDateFrom)
->setParameter('application_date_to', $applicationDateTo)
->getQuery()
->getSingleScalarResult()
;
if (0 < $conflictingCount) {
$this
->context
->buildViolation($constraint->message)
->addViolation()
;
}
}
}
+1 -1
View File
@@ -24,7 +24,7 @@
{%- block form_errors -%} {%- block form_errors -%}
{%- if errors|length > 0 -%} {%- if errors|length > 0 -%}
<ul> <ul class="pb-2">
{%- for error in errors -%} {%- for error in errors -%}
<li class="text-red-500">{{ error.message }}</li> <li class="text-red-500">{{ error.message }}</li>
{%- endfor -%} {%- endfor -%}
@@ -16,6 +16,7 @@
</h2> </h2>
{% include '_partials/_assignment_requirements_info.html.twig' %} {% include '_partials/_assignment_requirements_info.html.twig' %}
{{ form_start(form) }} {{ form_start(form) }}
{{ form_errors(form) }}
<div class="flex flex-col space-y-4 pb-8"> <div class="flex flex-col space-y-4 pb-8">
{% if form.pickup is defined %} {% if form.pickup is defined %}
{{ form_row(form.pickup) }} {{ form_row(form.pickup) }}