WIP: Implement availabilities
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20231002134820 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->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');
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -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<int, Teamer>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
+33
-5
@@ -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<int, Availability>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
// ;
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
// ;
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -3,4 +3,37 @@
|
||||
{% block title %}Meine Verfügbarkeit{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="text-2xl font-bold pb-4">
|
||||
Meine Vergfügbarkeit
|
||||
</h1>
|
||||
<p class="pb-8">
|
||||
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.
|
||||
</p>
|
||||
|
||||
<h2 class="text-xl font-bold pb-2">
|
||||
Ich bin verfügbar
|
||||
</h2>
|
||||
<ul class="list-disc pl-4 pb-8">
|
||||
{% for availability in availabilities %}
|
||||
<li>
|
||||
{{ availability.dateFrom|date('d.m.Y') }} - {{ availability.dateTo|date('d.m.Y') }}
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="text-sm">
|
||||
Du hast bisher keine Verfügbarkeiten hinterlegt.
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
<h2 class="text-xl font-bold pb-2">
|
||||
Weitere Verfügbarkeiten
|
||||
</h2>
|
||||
{{ form_start(form) }}
|
||||
{{ form_row(form.availabilities) }}
|
||||
<button type="submit" class="btn">
|
||||
Aktualisieren
|
||||
</button>
|
||||
{{ form_rest(form) }}
|
||||
{{ form_end(form) }}
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user