From 3fd4c6ca54474deeb7c3b40aa65f8d8d62ffb496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Mon, 16 Oct 2023 12:05:18 +0200 Subject: [PATCH] Feat: Add contacts --- migrations/Version20231016090702.php | 39 ++++++ .../Admin/System/Contact/CreateController.php | 79 ++++++++++++ .../Admin/System/Contact/DeleteController.php | 35 ++++++ .../Admin/System/Contact/EditController.php | 76 ++++++++++++ .../Admin/System/Contact/IndexController.php | 29 +++++ src/Controller/Teamer/ContactController.php | 19 ++- src/Entity/Contact.php | 115 ++++++++++++++++++ src/Entity/Teamer.php | 2 +- src/Entity/User.php | 15 +++ src/Form/ContactType.php | 42 +++++++ src/Menu/AdminMenuBuilder.php | 4 + src/Repository/ContactRepository.php | 50 ++++++++ templates/admin/application/index.html.twig | 1 + .../admin/system/contact/_form.html.twig | 23 ++++ .../admin/system/contact/create.html.twig | 1 + templates/admin/system/contact/edit.html.twig | 1 + .../admin/system/contact/index.html.twig | 81 ++++++++++++ .../admin/system/document/index.html.twig | 1 + templates/teamer/contact/index.html.twig | 47 +++++++ 19 files changed, 658 insertions(+), 2 deletions(-) create mode 100644 migrations/Version20231016090702.php create mode 100644 src/Controller/Admin/System/Contact/CreateController.php create mode 100644 src/Controller/Admin/System/Contact/DeleteController.php create mode 100644 src/Controller/Admin/System/Contact/EditController.php create mode 100644 src/Controller/Admin/System/Contact/IndexController.php create mode 100644 src/Entity/Contact.php create mode 100644 src/Form/ContactType.php create mode 100644 src/Repository/ContactRepository.php create mode 100644 templates/admin/system/contact/_form.html.twig create mode 100644 templates/admin/system/contact/create.html.twig create mode 100644 templates/admin/system/contact/edit.html.twig create mode 100644 templates/admin/system/contact/index.html.twig diff --git a/migrations/Version20231016090702.php b/migrations/Version20231016090702.php new file mode 100644 index 0000000..0248921 --- /dev/null +++ b/migrations/Version20231016090702.php @@ -0,0 +1,39 @@ +addSql('CREATE TABLE contact (id INT AUTO_INCREMENT NOT NULL, photo_id INT DEFAULT NULL, name VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL, destination VARCHAR(255) DEFAULT NULL, UNIQUE INDEX UNIQ_4C62E6387E9E4C8C (photo_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE contact ADD CONSTRAINT FK_4C62E6387E9E4C8C FOREIGN KEY (photo_id) REFERENCES upload (id)'); + $this->addSql('ALTER TABLE user ADD contact_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE user ADD CONSTRAINT FK_8D93D649E7A1254A FOREIGN KEY (contact_id) REFERENCES contact (id)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_8D93D649E7A1254A ON user (contact_id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE user DROP FOREIGN KEY FK_8D93D649E7A1254A'); + $this->addSql('ALTER TABLE contact DROP FOREIGN KEY FK_4C62E6387E9E4C8C'); + $this->addSql('DROP TABLE contact'); + $this->addSql('DROP INDEX UNIQ_8D93D649E7A1254A ON user'); + $this->addSql('ALTER TABLE user DROP contact_id'); + } +} diff --git a/src/Controller/Admin/System/Contact/CreateController.php b/src/Controller/Admin/System/Contact/CreateController.php new file mode 100644 index 0000000..c701a15 --- /dev/null +++ b/src/Controller/Admin/System/Contact/CreateController.php @@ -0,0 +1,79 @@ +generateUrl('app_admin_system_contact_create'); + + // Handle upload independently from form submission to avoid issues with failing validation + $uploadSession = $this->uploadHandler->getUploadSession(); + if (true === $request->isMethod('POST') && 0 < $uploadSession->getCount()) { + /** @var User $user */ + $user = $this->getUser(); + + $photo = Upload::fromUploadDto($uploadSession->getUploads()->first(), $user, Upload::TYPE_PHOTO); + $contact->setPhoto($photo); + + $this->entityManager->flush(); + + $this->uploadHandler->moveUploadSessionFilesFromOrphanage(Upload::TYPE_PHOTO, $uploadSession); + $this->uploadHandler->destroyUploadSession(); + } + + $form = $this->createForm( + ContactType::class, + $contact, + [ + 'action' => $formAction, + 'ajax_submit' => true, + 'upload_session' => $uploadSession, + ] + ); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $this->entityManager->persist($contact); + $this->entityManager->flush(); + $this->addFlash('success', 'Der Ansprechpartner wurde hinzugefügt'); + $this->logger->info('Create contact', [ + 'contact' => $contact->getName(), + ]); + + $response->setCloseAndRedirect($this->generateUrl('app_admin_system_contact_index')); + } else { + $response->setContent($this->renderView('admin/system/contact/create.html.twig', [ + 'form' => $form, + ])); + } + + return $this->json($response); + } +} \ No newline at end of file diff --git a/src/Controller/Admin/System/Contact/DeleteController.php b/src/Controller/Admin/System/Contact/DeleteController.php new file mode 100644 index 0000000..4557228 --- /dev/null +++ b/src/Controller/Admin/System/Contact/DeleteController.php @@ -0,0 +1,35 @@ +entityManager->remove($contact); + $this->entityManager->flush(); + + $this->addFlash('success', 'Der Ansprechpartner wurde gelöscht'); + $this->logger->info('Delete contact', [ + 'contact' => $contact->getName(), + ]); + + return $this->redirectToRoute('app_admin_system_contact_index'); + } +} \ No newline at end of file diff --git a/src/Controller/Admin/System/Contact/EditController.php b/src/Controller/Admin/System/Contact/EditController.php new file mode 100644 index 0000000..9426478 --- /dev/null +++ b/src/Controller/Admin/System/Contact/EditController.php @@ -0,0 +1,76 @@ +generateUrl('app_admin_system_contact_edit', ['id' => $contact->getId()]); + // Handle upload independently from form submission to avoid issues with failing validation + $uploadSession = $this->uploadHandler->getUploadSession(); + if (true === $request->isMethod('POST') && 0 < $uploadSession->getCount()) { + /** @var User $user */ + $user = $this->getUser(); + $photo = Upload::fromUploadDto($uploadSession->getUploads()->first(), $user, Upload::TYPE_PHOTO); + if (null !== $existingPhoto = $contact->getPhoto()) { + $this->entityManager->remove($existingPhoto); + } + $contact->setPhoto($photo); + $this->entityManager->flush(); + $this->uploadHandler->moveUploadSessionFilesFromOrphanage(Upload::TYPE_PHOTO, $uploadSession); + $this->uploadHandler->destroyUploadSession(); + } + + $form = $this->createForm( + ContactType::class, + $contact, + [ + 'action' => $formAction, + 'ajax_submit' => true, + 'upload_session' => $uploadSession, + ] + ); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $this->entityManager->flush(); + $this->addFlash('success', 'Der Ansprechpartner wurde aktualisiert'); + $this->logger->info('Edit contact', [ + 'contact' => $contact->getName(), + ]); + + $response->setCloseAndRedirect($this->generateUrl('app_admin_system_contact_index')); + } else { + $response->setContent($this->renderView('admin/system/contact/edit.html.twig', [ + 'form' => $form, + ])); + } + + return $this->json($response); + } +} \ No newline at end of file diff --git a/src/Controller/Admin/System/Contact/IndexController.php b/src/Controller/Admin/System/Contact/IndexController.php new file mode 100644 index 0000000..e6f29a0 --- /dev/null +++ b/src/Controller/Admin/System/Contact/IndexController.php @@ -0,0 +1,29 @@ +contactRepository + ->findBy([], ['name' => 'ASC']) + ; + + return $this->render('admin/system/contact/index.html.twig', [ + 'contacts' => $contacts, + ]); + } +} \ No newline at end of file diff --git a/src/Controller/Teamer/ContactController.php b/src/Controller/Teamer/ContactController.php index 7cc2d96..e3e9994 100644 --- a/src/Controller/Teamer/ContactController.php +++ b/src/Controller/Teamer/ContactController.php @@ -2,15 +2,32 @@ namespace App\Controller\Teamer; +use App\Repository\ContactRepository; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; class ContactController extends AbstractController { + public function __construct(private readonly ContactRepository $contactRepository) + {} + #[Route('/teamer/contact', name: 'app_teamer_contact')] public function index(): Response { - return $this->render('teamer/contact/index.html.twig'); + $generalContacts = $this + ->contactRepository + ->getGeneralContacts() + ; + + $destinationContacts = $this + ->contactRepository + ->getDestinationContacts() + ; + + return $this->render('teamer/contact/index.html.twig', [ + 'generalContacts' => $generalContacts, + 'destinationContacts' => $destinationContacts, + ]); } } \ No newline at end of file diff --git a/src/Entity/Contact.php b/src/Entity/Contact.php new file mode 100644 index 0000000..d56870d --- /dev/null +++ b/src/Entity/Contact.php @@ -0,0 +1,115 @@ +getName(); + } + + public function getId(): ?int + { + return $this->id; + } + + public function getName(): ?string + { + return $this->name; + } + + public function setName(string $name): static + { + $this->name = $name; + + return $this; + } + + public function getEmail(): ?string + { + return $this->email; + } + + public function setEmail(string $email): static + { + $this->email = $email; + + return $this; + } + + public function getPhoto(): ?Upload + { + return $this->photo; + } + + public function setPhoto(?Upload $photo): static + { + $this->photo = $photo; + + return $this; + } + + public function getDestination(): ?string + { + return $this->destination; + } + + public function setDestination(?string $destination): static + { + $this->destination = $destination; + + return $this; + } + + public function getUser(): ?User + { + return $this->user; + } + + public function setUser(?User $user): static + { + // unset the owning side of the relation if necessary + if ($user === null && $this->user !== null) { + $this->user->setContact(null); + } + + // set the owning side of the relation if necessary + if ($user !== null && $user->getContact() !== $this) { + $user->setContact($this); + } + + $this->user = $user; + + return $this; + } +} diff --git a/src/Entity/Teamer.php b/src/Entity/Teamer.php index 32e0b15..24d1eee 100644 --- a/src/Entity/Teamer.php +++ b/src/Entity/Teamer.php @@ -115,7 +115,7 @@ class Teamer implements TimestampableEntityInterface #[ORM\OneToMany(mappedBy: 'teamer', targetEntity: Disposition::class, cascade: ['remove'])] private Collection $dispositions; - #[ORM\OneToOne(mappedBy: 'teamer')] + #[ORM\OneToOne(mappedBy: 'teamer', fetch: 'EXTRA_LAZY')] private ?User $user = null; #[ORM\Column] diff --git a/src/Entity/User.php b/src/Entity/User.php index 4724275..2f71d2c 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -45,6 +45,9 @@ class User implements UserInterface, TimestampableEntityInterface #[ORM\OneToOne(inversedBy: 'user', cascade: ['persist', 'remove'])] private ?Teamer $teamer = null; + #[ORM\OneToOne(inversedBy: 'user', cascade: ['persist', 'remove'])] + private ?Contact $contact = null; + public function __construct() { $this->uuid = Uuid::v4(); @@ -200,4 +203,16 @@ class User implements UserInterface, TimestampableEntityInterface return $this; } + + public function getContact(): ?Contact + { + return $this->contact; + } + + public function setContact(?Contact $contact): static + { + $this->contact = $contact; + + return $this; + } } diff --git a/src/Form/ContactType.php b/src/Form/ContactType.php new file mode 100644 index 0000000..ad7bfb4 --- /dev/null +++ b/src/Form/ContactType.php @@ -0,0 +1,42 @@ +add('name', TextType::class, [ + 'label' => 'Name', + ]) + ->add('email', EmailType::class, [ + 'label' => 'E-Mail', + ]) + ->add('destination', TextType::class, [ + 'label' => 'Destination', + 'required' => false, + ]) + ; + } + + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver + ->setDefaults([ + 'data_class' => Contact::class, + ]) + ->setRequired(['upload_session']) + ->setAllowedTypes('upload_session', UploadSessionDto::class) + ; + } +} \ No newline at end of file diff --git a/src/Menu/AdminMenuBuilder.php b/src/Menu/AdminMenuBuilder.php index 5f37c0a..2107293 100644 --- a/src/Menu/AdminMenuBuilder.php +++ b/src/Menu/AdminMenuBuilder.php @@ -87,6 +87,10 @@ class AdminMenuBuilder extends AbstractMenuBuilder 'route' => 'app_admin_system_document_index', 'title' => 'Dokumente', ], + [ + 'route' => 'app_admin_system_contact_index', + 'title' => 'Ansprechpartner', + ], ], ], ]; diff --git a/src/Repository/ContactRepository.php b/src/Repository/ContactRepository.php new file mode 100644 index 0000000..363d44c --- /dev/null +++ b/src/Repository/ContactRepository.php @@ -0,0 +1,50 @@ + + * + * @method Contact|null find($id, $lockMode = null, $lockVersion = null) + * @method Contact|null findOneBy(array $criteria, array $orderBy = null) + * @method Contact[] findAll() + * @method Contact[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class ContactRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, Contact::class); + } + + public function getGeneralContacts(): array + { + $qb = $this->createQueryBuilder('contact'); + + return $qb + ->leftJoin('contact.photo', 'photo') + ->where($qb->expr()->isNull('contact.destination')) + ->orderBy('contact.name', 'ASC') + ->getQuery() + ->getResult() + ; + } + + public function getDestinationContacts(): array + { + $qb = $this->createQueryBuilder('contact'); + + return $qb + ->leftJoin('contact.photo', 'photo') + ->where($qb->expr()->isNotNull('contact.destination')) + ->orderBy('contact.name', 'ASC') + ->addOrderBy('contact.destination', 'ASC') + ->getQuery() + ->getResult() + ; + } +} diff --git a/templates/admin/application/index.html.twig b/templates/admin/application/index.html.twig index d595c05..3b08800 100644 --- a/templates/admin/application/index.html.twig +++ b/templates/admin/application/index.html.twig @@ -71,6 +71,7 @@ {% endfor %} + {{ knp_pagination_render(pagination) }} {% endblock %} \ No newline at end of file diff --git a/templates/admin/system/contact/_form.html.twig b/templates/admin/system/contact/_form.html.twig new file mode 100644 index 0000000..641600d --- /dev/null +++ b/templates/admin/system/contact/_form.html.twig @@ -0,0 +1,23 @@ +{{ form_start(form) }} +
+ {{ form_row(form.name) }} + {{ form_row(form.email) }} + {{ form_row(form.destination) }} +
+

+ Foto +

+ {% include '_partials/_upload_collection_form.html.twig' with { + 'endpoint_upload': path('_uploader_upload_photo'), + 'max_filesize': 5, + 'max_files': 1, + 'accepted_files': 'image/jpg,image/jpeg', + } %} + {{ form_errors(form) }} +
+
+ +{{ form_rest(form) }} +{{ form_end(form) }} diff --git a/templates/admin/system/contact/create.html.twig b/templates/admin/system/contact/create.html.twig new file mode 100644 index 0000000..6923fd8 --- /dev/null +++ b/templates/admin/system/contact/create.html.twig @@ -0,0 +1 @@ +{% include 'admin/system/contact/_form.html.twig' %} \ No newline at end of file diff --git a/templates/admin/system/contact/edit.html.twig b/templates/admin/system/contact/edit.html.twig new file mode 100644 index 0000000..6923fd8 --- /dev/null +++ b/templates/admin/system/contact/edit.html.twig @@ -0,0 +1 @@ +{% include 'admin/system/contact/_form.html.twig' %} \ No newline at end of file diff --git a/templates/admin/system/contact/index.html.twig b/templates/admin/system/contact/index.html.twig new file mode 100644 index 0000000..69db299 --- /dev/null +++ b/templates/admin/system/contact/index.html.twig @@ -0,0 +1,81 @@ +{% extends 'admin/layout.html.twig' %} + +{% block title %}Ansprechpartner{% endblock %} + +{% block content %} +

+ Ansprechpartner +

+
+
+ + + + + + + + + + + {% for contact in contacts %} + + + + + + + {% else %} + + + + {% endfor %} + +
+ Name + + E-Mail + + Destination +
+ {{ contact.name }} + + {{ contact.email }} + + {{ contact.destination|default('-') }} + +
+ + +
+
+ Keine Daten... +
+
+
+ +{% endblock %} \ No newline at end of file diff --git a/templates/admin/system/document/index.html.twig b/templates/admin/system/document/index.html.twig index c8e47f1..841ba3a 100644 --- a/templates/admin/system/document/index.html.twig +++ b/templates/admin/system/document/index.html.twig @@ -85,6 +85,7 @@ {% endfor %} + {{ knp_pagination_render(pagination) }}