diff --git a/migrations/Version20231002134820.php b/migrations/Version20231002134820.php new file mode 100644 index 0000000..0086df2 --- /dev/null +++ b/migrations/Version20231002134820.php @@ -0,0 +1,35 @@ +addSql('CREATE TABLE teamer_availability (teamer_id INT NOT NULL, availability_id INT NOT NULL, INDEX IDX_9CF7958B4302FF75 (teamer_id), INDEX IDX_9CF7958B61778466 (availability_id), PRIMARY KEY(teamer_id, availability_id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE teamer_availability ADD CONSTRAINT FK_9CF7958B4302FF75 FOREIGN KEY (teamer_id) REFERENCES teamer (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE teamer_availability ADD CONSTRAINT FK_9CF7958B61778466 FOREIGN KEY (availability_id) REFERENCES availability (id) ON DELETE CASCADE'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE teamer_availability DROP FOREIGN KEY FK_9CF7958B4302FF75'); + $this->addSql('ALTER TABLE teamer_availability DROP FOREIGN KEY FK_9CF7958B61778466'); + $this->addSql('DROP TABLE teamer_availability'); + } +} diff --git a/src/Controller/Teamer/Profile/AvailabilitiesController.php b/src/Controller/Teamer/Profile/AvailabilitiesController.php index fc03401..b55e51e 100644 --- a/src/Controller/Teamer/Profile/AvailabilitiesController.php +++ b/src/Controller/Teamer/Profile/AvailabilitiesController.php @@ -2,17 +2,59 @@ namespace App\Controller\Teamer\Profile; +use App\Entity\Availability; +use App\Entity\User; +use App\Repository\AvailabilityRepository; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; class AvailabilitiesController extends AbstractController { + public function __construct( + private readonly AvailabilityRepository $availabilityRepository, + private readonly EntityManagerInterface $entityManager + ) { + } + #[Route('/teamer/profile/availabilities', name: 'app_teamer_profile_availabilities')] #[IsGranted('ROLE_TEAMER')] - public function index(): Response + public function index(Request $request): Response { - return $this->render('teamer/profile/availabilities.html.twig'); + /** @var User $user */ + $user = $this->getUser(); + $teamer = $user->getTeamer(); + + $selectableAvailabilities = $this->availabilityRepository->getSelectable($teamer); + $form = $this->createFormBuilder($teamer) + ->add('availabilities', EntityType::class, [ + 'label' => false, + 'class' => Availability::class, + 'choice_label' => function($choice) { + return sprintf('%s - %s', $choice->getDateFrom()->format('d.m.Y'), $choice->getDateTo()->format('d.m.Y')); + }, + 'choices' => $selectableAvailabilities, + 'multiple' => true, + 'expanded' => true, + ]) + ->getForm() + ; + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $this->entityManager->flush(); + + return $this->redirectToRoute('app_teamer_profile_availabilities'); + } + + return $this->render('teamer/profile/availabilities.html.twig', [ + 'availabilities' => $teamer->getAvailabilities(), + 'selectableAvailabilities' => $selectableAvailabilities, + 'form' => $form, + ]); } } \ No newline at end of file diff --git a/src/Entity/Availability.php b/src/Entity/Availability.php index e65d236..f9da5fa 100644 --- a/src/Entity/Availability.php +++ b/src/Entity/Availability.php @@ -5,6 +5,8 @@ namespace App\Entity; use App\Entity\Traits\BlameableEntity; use App\Entity\Traits\TimestampableEntity; use App\Repository\AvailabilityRepository; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; @@ -25,9 +27,17 @@ class Availability implements BlameableEntityInterface, TimestampableEntityInter #[ORM\Column(type: Types::DATE_IMMUTABLE)] private ?\DateTimeImmutable $dateTo = null; - #[ORM\ManyToOne(inversedBy: 'availabilities')] + #[ORM\ManyToOne(inversedBy: 'customAvailabilities')] private ?Teamer $owner = null; + #[ORM\ManyToMany(targetEntity: Teamer::class, mappedBy: 'availabilities')] + private Collection $teamers; + + public function __construct() + { + $this->teamers = new ArrayCollection(); + } + public function getId(): ?int { return $this->id; @@ -68,4 +78,31 @@ class Availability implements BlameableEntityInterface, TimestampableEntityInter return $this; } + + /** + * @return Collection + */ + public function getTeamers(): Collection + { + return $this->teamers; + } + + public function addTeamer(Teamer $teamer): static + { + if (!$this->teamers->contains($teamer)) { + $this->teamers->add($teamer); + $teamer->addAvailability($this); + } + + return $this; + } + + public function removeTeamer(Teamer $teamer): static + { + if ($this->teamers->removeElement($teamer)) { + $teamer->removeAvailability($this); + } + + return $this; + } } diff --git a/src/Entity/Teamer.php b/src/Entity/Teamer.php index 0260a2a..5eefcab 100644 --- a/src/Entity/Teamer.php +++ b/src/Entity/Teamer.php @@ -83,9 +83,12 @@ class Teamer implements TimestampableEntityInterface #[ORM\Column(type: Types::TEXT, nullable: true)] private ?string $remarks = null; - #[ORM\OneToMany(mappedBy: 'owner', targetEntity: Availability::class)] + #[ORM\ManyToMany(targetEntity: Availability::class, inversedBy: 'teamers')] private Collection $availabilities; + #[ORM\OneToMany(mappedBy: 'owner', targetEntity: Availability::class)] + private Collection $customAvailabilities; + #[ORM\OneToOne(cascade: ['persist', 'remove'], fetch: 'EAGER')] #[Assert\NotNull(message: 'Bitte lade ein Foto von dir hoch', groups: ['profile', 'profile_preflight'])] private ?Upload $photo = null; @@ -136,6 +139,7 @@ class Teamer implements TimestampableEntityInterface $this->bookmarks = new ArrayCollection(); $this->applications = new ArrayCollection(); $this->dispositions = new ArrayCollection(); + $this->customAvailabilities = new ArrayCollection(); } public function toPayload(): array @@ -375,7 +379,6 @@ class Teamer implements TimestampableEntityInterface { if (!$this->availabilities->contains($availability)) { $this->availabilities->add($availability); - $availability->setOwner($this); } return $this; @@ -383,10 +386,35 @@ class Teamer implements TimestampableEntityInterface public function removeAvailability(Availability $availability): static { - if ($this->availabilities->removeElement($availability)) { + $this->availabilities->removeElement($availability); + + return $this; + } + + /** + * @return Collection + */ + public function getCustomAvailabilities(): Collection + { + return $this->customAvailabilities; + } + + public function addCustomAvailability(Availability $customAvailability): static + { + if (!$this->customAvailabilities->contains($customAvailability)) { + $this->customAvailabilities->add($customAvailability); + $customAvailability->setOwner($this); + } + + return $this; + } + + public function removeCustomAvailability(Availability $customAvailability): static + { + if ($this->customAvailabilities->removeElement($customAvailability)) { // set the owning side to null (unless already changed) - if ($availability->getOwner() === $this) { - $availability->setOwner(null); + if ($customAvailability->getOwner() === $this) { + $customAvailability->setOwner(null); } } diff --git a/src/Repository/ApplicationRepository.php b/src/Repository/ApplicationRepository.php index 943595b..cd222d1 100644 --- a/src/Repository/ApplicationRepository.php +++ b/src/Repository/ApplicationRepository.php @@ -20,47 +20,4 @@ class ApplicationRepository extends ServiceEntityRepository { parent::__construct($registry, Application::class); } - - public function save(Application $entity, bool $flush = false): void - { - $this->getEntityManager()->persist($entity); - - if ($flush) { - $this->getEntityManager()->flush(); - } - } - - public function remove(Application $entity, bool $flush = false): void - { - $this->getEntityManager()->remove($entity); - - if ($flush) { - $this->getEntityManager()->flush(); - } - } - -// /** -// * @return Application[] Returns an array of Application objects -// */ -// public function findByExampleField($value): array -// { -// return $this->createQueryBuilder('a') -// ->andWhere('a.exampleField = :val') -// ->setParameter('val', $value) -// ->orderBy('a.id', 'ASC') -// ->setMaxResults(10) -// ->getQuery() -// ->getResult() -// ; -// } - -// public function findOneBySomeField($value): ?Application -// { -// return $this->createQueryBuilder('a') -// ->andWhere('a.exampleField = :val') -// ->setParameter('val', $value) -// ->getQuery() -// ->getOneOrNullResult() -// ; -// } } diff --git a/src/Repository/AvailabilityRepository.php b/src/Repository/AvailabilityRepository.php index 21d6cba..ab9b94f 100644 --- a/src/Repository/AvailabilityRepository.php +++ b/src/Repository/AvailabilityRepository.php @@ -3,6 +3,7 @@ namespace App\Repository; use App\Entity\Availability; +use App\Entity\Teamer; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Persistence\ManagerRegistry; @@ -21,46 +22,29 @@ class AvailabilityRepository extends ServiceEntityRepository parent::__construct($registry, Availability::class); } - public function save(Availability $entity, bool $flush = false): void + public function getSelectable(Teamer $teamer): array { - $this->getEntityManager()->persist($entity); + $qb = $this->createQueryBuilder('availability'); - if ($flush) { - $this->getEntityManager()->flush(); - } + return $qb + ->leftJoin('availability.teamers', 'teamer') + ->where($qb->expr()->andX( + $qb->expr()->gt('availability.dateFrom', ':today'), + $qb->expr()->orX( + $qb->expr()->isNull('teamer'), + $qb->expr()->neq('teamer', ':teamer') + ), + $qb->expr()->orX( + $qb->expr()->isNull('availability.owner'), + $qb->expr()->eq('availability.owner', ':owner') + ) + )) + ->orderBy('availability.dateFrom', 'ASC') + ->setParameter('teamer', $teamer) + ->setParameter('owner', $teamer) + ->setParameter('today', new \DateTimeImmutable()) + ->getQuery() + ->getResult() + ; } - - public function remove(Availability $entity, bool $flush = false): void - { - $this->getEntityManager()->remove($entity); - - if ($flush) { - $this->getEntityManager()->flush(); - } - } - -// /** -// * @return Availability[] Returns an array of Availability objects -// */ -// public function findByExampleField($value): array -// { -// return $this->createQueryBuilder('a') -// ->andWhere('a.exampleField = :val') -// ->setParameter('val', $value) -// ->orderBy('a.id', 'ASC') -// ->setMaxResults(10) -// ->getQuery() -// ->getResult() -// ; -// } - -// public function findOneBySomeField($value): ?Availability -// { -// return $this->createQueryBuilder('a') -// ->andWhere('a.exampleField = :val') -// ->setParameter('val', $value) -// ->getQuery() -// ->getOneOrNullResult() -// ; -// } } diff --git a/templates/teamer/profile/availabilities.html.twig b/templates/teamer/profile/availabilities.html.twig index 9306952..e46076d 100644 --- a/templates/teamer/profile/availabilities.html.twig +++ b/templates/teamer/profile/availabilities.html.twig @@ -3,4 +3,37 @@ {% block title %}Meine Verfügbarkeit{% endblock %} {% block content %} +

+ Meine Vergfügbarkeit +

+

+ Hier kannst du angeben, wann du allgemein Zeit und Lust hättest, Einsätze zu absolvieren. Wenn wir in diesem + Zeitraum einen Einsatz zu vergeben haben, dann nehmen wir telefonisch oder per E-Mail Kontakt zu dir auf. +

+ +

+ Ich bin verfügbar +

+ + +

+ Weitere Verfügbarkeiten +

+ {{ form_start(form) }} + {{ form_row(form.availabilities) }} + + {{ form_rest(form) }} + {{ form_end(form) }} {% endblock %} \ No newline at end of file