diff --git a/src/Controller/Admin/Teamer/TrainingAttendance/CreateController.php b/src/Controller/Admin/Teamer/TrainingAttendance/CreateController.php index 1d414be..99ed80f 100644 --- a/src/Controller/Admin/Teamer/TrainingAttendance/CreateController.php +++ b/src/Controller/Admin/Teamer/TrainingAttendance/CreateController.php @@ -3,10 +3,10 @@ namespace App\Controller\Admin\Teamer\TrainingAttendance; use App\Controller\Traits\ReturnUrlTrait; -use App\Entity\JobProfile; use App\Entity\Teamer; use App\Entity\Training; use App\Entity\TrainingAttendance; +use App\Event\TrainingAttendanceCreatedEvent; use App\Form\AbbreviatedDateType; use App\Htmx\HxRedirectResponse; use Doctrine\ORM\EntityManagerInterface; @@ -17,6 +17,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; class CreateController extends AbstractController { @@ -24,11 +25,15 @@ class CreateController extends AbstractController public function __construct( private readonly EntityManagerInterface $entityManager, + private readonly EventDispatcherInterface $eventDispatcher, private readonly LoggerInterface $logger ) { } - #[Route('/admin/teamer/skills/training-attendance/create/{training_id}/{teamer_id}', name: 'app_admin_teamer_skills_training_attendance_create')] + #[Route( + path: '/admin/teamer/skills/training-attendance/create/{training_id}/{teamer_id}', + name: 'app_admin_teamer_skills_training_attendance_create' + )] #[IsGranted('ROLE_ADMIN')] public function index( #[MapEntity(mapping: ['training_id' => 'id'])] @@ -58,9 +63,13 @@ class CreateController extends AbstractController if ($form->isSubmitted() && $form->isValid()) { $this->entityManager->persist($attendance); -// $this->assignJobProfiles($teamer, $attendance); $this->entityManager->flush(); + $this->eventDispatcher->dispatch( + new TrainingAttendanceCreatedEvent($attendance), + TrainingAttendanceCreatedEvent::NAME + ); + $this->addFlash('success', 'Die Teilnahme wurde hinzugefügt'); $this->logger->info('Create training attendance', [ 'attendance_id' => $attendance->getId(), @@ -81,25 +90,4 @@ class CreateController extends AbstractController 'form' => $form->createView(), ]); } - - private function assignJobProfiles(Teamer $teamer, TrainingAttendance $attendance): void - { - $qb = $this - ->entityManager - ->getRepository(JobProfile::class) - ->createQueryBuilder('job_profile') - ; - - $jobProfiles = $qb - ->leftJoin('job_profile.requiredTrainings', 'required_trainings') - ->where($qb->expr()->in('required_trainings', ':training')) - ->setParameter('training', $attendance->getTraining()) - ->getQuery() - ->getResult() - ; - - foreach ($jobProfiles as $jobProfile) { - $teamer->addJobProfile($jobProfile); - } - } } \ No newline at end of file diff --git a/src/Controller/Admin/Teamer/TrainingAttendance/DeleteController.php b/src/Controller/Admin/Teamer/TrainingAttendance/DeleteController.php index a1a10ec..c2f8201 100644 --- a/src/Controller/Admin/Teamer/TrainingAttendance/DeleteController.php +++ b/src/Controller/Admin/Teamer/TrainingAttendance/DeleteController.php @@ -4,6 +4,7 @@ namespace App\Controller\Admin\Teamer\TrainingAttendance; use App\Controller\Traits\ReturnUrlTrait; use App\Entity\TrainingAttendance; +use App\Event\TrainingAttendanceDeletedEvent; use App\Htmx\HxRedirectResponse; use Doctrine\ORM\EntityManagerInterface; use Psr\Log\LoggerInterface; @@ -12,6 +13,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; class DeleteController extends AbstractController { @@ -19,11 +21,15 @@ class DeleteController extends AbstractController public function __construct( private readonly EntityManagerInterface $entityManager, + private readonly EventDispatcherInterface $eventDispatcher, private readonly LoggerInterface $logger ) { } - #[Route('/admin/teamer/skills/training-attendance/delete/{uuid}', name: 'app_admin_teamer_skills_training_attendance_delete')] + #[Route( + path: '/admin/teamer/skills/training-attendance/delete/{uuid}', + name: 'app_admin_teamer_skills_training_attendance_delete' + )] #[IsGranted('ROLE_ADMIN')] public function index(TrainingAttendance $attendance, Request $request): Response { @@ -33,6 +39,11 @@ class DeleteController extends AbstractController $this->entityManager->remove($attendance); $this->entityManager->flush(); + $this->eventDispatcher->dispatch( + new TrainingAttendanceDeletedEvent($attendance), + TrainingAttendanceDeletedEvent::NAME + ); + $this->addFlash('success', 'Die Teilnahme an der Fortbilundg wurde gelöscht'); $this->logger->info('Delete training attendance', [ 'attendance_id' => $attendance->getId(), diff --git a/src/Controller/Teamer/Profile/License/AddController.php b/src/Controller/Teamer/Profile/License/AddController.php index 439e7fe..fb81c55 100644 --- a/src/Controller/Teamer/Profile/License/AddController.php +++ b/src/Controller/Teamer/Profile/License/AddController.php @@ -2,11 +2,10 @@ namespace App\Controller\Teamer\Profile\License; -use App\Entity\JobProfile; use App\Entity\License; -use App\Entity\Teamer; use App\Entity\Upload; use App\Entity\User; +use App\Event\LicenseAddedEvent; use App\Form\LicenseType; use App\Htmx\HxRedirectResponse; use App\Model\UploadSessionDto; @@ -18,12 +17,14 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; class AddController extends AbstractController { public function __construct( private readonly EntityManagerInterface $entityManager, private readonly UploadHandler $uploadHandler, + private readonly EventDispatcherInterface $eventDispatcher, private readonly LoggerInterface $logger ) { } @@ -49,11 +50,12 @@ class AddController extends AbstractController $teamer = $user->getTeamer(); $teamer->addLicense($license); - $this->assignJobProfiles($license, $teamer); $this->entityManager->persist($license); $this->entityManager->flush(); + $this->eventDispatcher->dispatch(new LicenseAddedEvent($license), LicenseAddedEvent::NAME); + $this->addFlash('success', 'Die Lizenz wurde hinzugefügt.'); $this->logger->info('Add license', [ 'teamer_id' => $teamer->getId(), @@ -69,23 +71,6 @@ class AddController extends AbstractController ]); } - private function assignJobProfiles(License $license, Teamer $teamer): void - { - if (false === in_array($license->getType(), [License::TYPE_SKI, License::TYPE_SNOWBOARD])) { - return; - } - - $matchingJobProfiles = $this - ->entityManager - ->getRepository(JobProfile::class) - ->findByLicenseType($license->getType()) - ; - - foreach ($matchingJobProfiles as $matchingJobProfile) { - $teamer->addJobProfile($matchingJobProfile); - } - } - private function updateCertificate(User $user, License $license, UploadSessionDto $uploadSession): void { $upload = Upload::fromUploadDto($uploadSession->getUploads()->first(), $user, Upload::TYPE_CERTIFICATE); diff --git a/src/Controller/Teamer/Profile/License/DeleteController.php b/src/Controller/Teamer/Profile/License/DeleteController.php index 3a82d29..a01dcfa 100644 --- a/src/Controller/Teamer/Profile/License/DeleteController.php +++ b/src/Controller/Teamer/Profile/License/DeleteController.php @@ -3,6 +3,7 @@ namespace App\Controller\Teamer\Profile\License; use App\Entity\License; +use App\Event\LicenseDeletedEvent; use App\Htmx\HxRedirectResponse; use Doctrine\ORM\EntityManagerInterface; use Psr\Log\LoggerInterface; @@ -11,11 +12,13 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; class DeleteController extends AbstractController { public function __construct( private readonly EntityManagerInterface $entityManager, + private readonly EventDispatcherInterface $eventDispatcher, private readonly LoggerInterface $logger ) { } @@ -30,6 +33,8 @@ class DeleteController extends AbstractController $this->entityManager->remove($license); $this->entityManager->flush(); + $this->eventDispatcher->dispatch(new LicenseDeletedEvent($license), LicenseDeletedEvent::NAME); + $this->addFlash('success', 'Die Lizenz wurde gelöscht.'); $this->logger->info('Delete license', [ 'teamer_id' => $teamer->getId(), diff --git a/src/Controller/Teamer/Profile/SkillsController.php b/src/Controller/Teamer/Profile/SkillsController.php index ef1353f..075f427 100644 --- a/src/Controller/Teamer/Profile/SkillsController.php +++ b/src/Controller/Teamer/Profile/SkillsController.php @@ -2,7 +2,6 @@ namespace App\Controller\Teamer\Profile; -use App\Entity\JobProfile; use App\Entity\User; use App\Form\TeamerJobProfileType; use App\Repository\JobProfileRepository; @@ -35,28 +34,7 @@ class SkillsController extends AbstractController $selectableJobProfiles = []; foreach ($jobProfiles as $profile) { - /** @var JobProfile $profile */ - $requiredTrainingsSatisfied = false; - if (0 === $profile->getRequiredTrainings()->count()) { - $requiredTrainingsSatisfied = true; - } - foreach ($profile->getRequiredTrainings() as $training) { - if (null !== $teamer->getTrainingAttendance($training)) { - $requiredTrainingsSatisfied = true; - break; - } - } - $requiredLicensesSatisfied = false; - if (0 === count($profile->getRequiredLicenses())) { - $requiredLicensesSatisfied = true; - } - foreach ($profile->getRequiredLicenses() as $license) { - if (null !== $teamer->getLicenseOfType($license)) { - $requiredLicensesSatisfied = true; - break; - } - } - $selectableJobProfiles[$profile->getId()] = $requiredTrainingsSatisfied && $requiredLicensesSatisfied; + $selectableJobProfiles[$profile->getId()] = $profile->isQualified($teamer); } $form = $this->createForm(TeamerJobProfileType::class, $teamer, [ diff --git a/src/Entity/JobProfile.php b/src/Entity/JobProfile.php index 0346003..1415103 100644 --- a/src/Entity/JobProfile.php +++ b/src/Entity/JobProfile.php @@ -118,4 +118,37 @@ class JobProfile implements BlameableEntityInterface, TimestampableEntityInterfa return $this; } + + public function isQualified(Teamer $teamer): bool + { + // Check licenses + $qualifiedByLicenses = true; + if (0 < $this->requiredTrainings->count()) { + $teamerLicenseTypes = $teamer->getLicenses()->map(function (License $license) { + return $license->getType(); + })->toArray(); + + foreach ($this->requiredLicenses as $requiredLicense) { + if (false === in_array($requiredLicense, $teamerLicenseTypes)) { + $qualifiedByLicenses = false; + } + } + } + + // Check trainings + $qualifiedByTrainings = true; + if (0 < $this->requiredTrainings->count()) { + $teamerTrainings = $teamer->getTrainingAttendances()->map(function (TrainingAttendance $trainingAttendance) { + return $trainingAttendance->getTraining(); + }); + + foreach ($this->requiredTrainings as $requiredTraining) { + if (false === $teamerTrainings->contains($requiredTraining)) { + $qualifiedByTrainings = false; + } + } + } + + return $qualifiedByLicenses && $qualifiedByTrainings; + } } diff --git a/src/Event/LicenseAddedEvent.php b/src/Event/LicenseAddedEvent.php new file mode 100644 index 0000000..b833840 --- /dev/null +++ b/src/Event/LicenseAddedEvent.php @@ -0,0 +1,20 @@ +license; + } +} \ No newline at end of file diff --git a/src/Event/LicenseDeletedEvent.php b/src/Event/LicenseDeletedEvent.php new file mode 100644 index 0000000..ca88e11 --- /dev/null +++ b/src/Event/LicenseDeletedEvent.php @@ -0,0 +1,20 @@ +license; + } +} \ No newline at end of file diff --git a/src/Event/TrainingAttendanceCreatedEvent.php b/src/Event/TrainingAttendanceCreatedEvent.php new file mode 100644 index 0000000..3678fd2 --- /dev/null +++ b/src/Event/TrainingAttendanceCreatedEvent.php @@ -0,0 +1,20 @@ +trainingAttendance; + } +} \ No newline at end of file diff --git a/src/Event/TrainingAttendanceDeletedEvent.php b/src/Event/TrainingAttendanceDeletedEvent.php new file mode 100644 index 0000000..8e8a246 --- /dev/null +++ b/src/Event/TrainingAttendanceDeletedEvent.php @@ -0,0 +1,20 @@ +trainingAttendance; + } +} \ No newline at end of file diff --git a/src/EventListener/JobProfileAssignmentSubscriber.php b/src/EventListener/JobProfileAssignmentSubscriber.php new file mode 100644 index 0000000..29daffc --- /dev/null +++ b/src/EventListener/JobProfileAssignmentSubscriber.php @@ -0,0 +1,72 @@ + 'onTrainingAttendanceCreated', + TrainingAttendanceDeletedEvent::NAME => 'onTrainingAttendanceDeleted', + LicenseAddedEvent::NAME => 'onLicenseAdded', + LicenseDeletedEvent::NAME => 'onLicenseDeleted', + ]; + } + + public function onTrainingAttendanceCreated(TrainingAttendanceCreatedEvent $event): void + { + $teamer = $event->getTrainingAttendance()->getTeamer(); + $this->updateJobProfiles($teamer); + } + + public function onTrainingAttendanceDeleted(TrainingAttendanceDeletedEvent $event): void + { + $teamer = $event->getTrainingAttendance()->getTeamer(); + $this->updateJobProfiles($teamer); + } + + public function onLicenseAdded(LicenseAddedEvent $event): void + { + $teamer = $event->getLicense()->getTeamer(); + $this->updateJobProfiles($teamer); + } + + public function onLicenseDeleted(LicenseDeletedEvent $event): void + { + $teamer = $event->getLicense()->getTeamer(); + $this->updateJobProfiles($teamer); + } + + private function updateJobProfiles(Teamer $teamer): void + { + $jobProfiles = $this + ->entityManager + ->getRepository(JobProfile::class) + ->findAll() + ; + + foreach ($jobProfiles as $jobProfile) { + if ($jobProfile->isQualified($teamer)) { + $teamer->addJobProfile($jobProfile); + } else { + $teamer->removeJobProfile($jobProfile); + } + } + + $this->entityManager->flush(); + } +} \ No newline at end of file