diff --git a/config/forms/event_details.xml b/config/forms/event_details.xml index 7c624dcc..bd4e4871 100644 --- a/config/forms/event_details.xml +++ b/config/forms/event_details.xml @@ -14,6 +14,19 @@ + + + + + + + sulu_admin.url + + + + + + diff --git a/config/services.yaml b/config/services.yaml index 3877d370..831db81b 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -44,3 +44,6 @@ services: App\Content\Type\AlbumSelection: tags: [{name: 'sulu.content.type', alias: 'album_selection'}] + + App\Routing\EventRouteDefaultsProvider: + tags: [{ name: 'sulu_route.defaults_provider' }] diff --git a/src/Controller/Admin/EventController.php b/src/Controller/Admin/EventController.php index c0c4077d..345a81aa 100644 --- a/src/Controller/Admin/EventController.php +++ b/src/Controller/Admin/EventController.php @@ -8,7 +8,10 @@ use App\Entity\Event; use Doctrine\ORM\EntityManagerInterface; use Sulu\Bundle\MediaBundle\Media\Manager\MediaManagerInterface; +use Sulu\Bundle\RouteBundle\Entity\RouteRepositoryInterface; +use Sulu\Bundle\RouteBundle\Manager\RouteManagerInterface; use Sulu\Component\Security\SecuredControllerInterface; +use Sulu\Component\Webspace\Manager\WebspaceManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -19,6 +22,7 @@ * @phpstan-type EventData array{ * id: int|null, * name: string, + * routePath: string, * image: array{id: int}|null, * startDate: string|null, * endDate: string|null, @@ -29,15 +33,24 @@ class EventController extends AbstractController implements SecuredControllerInt private DoctrineListRepresentationFactory $doctrineListRepresentationFactory; private EntityManagerInterface $entityManager; private MediaManagerInterface $mediaManager; + private WebspaceManagerInterface $webspaceManager; + private RouteManagerInterface $routeManager; + private RouteRepositoryInterface $routeRepository; public function __construct( DoctrineListRepresentationFactory $doctrineListRepresentationFactory, EntityManagerInterface $entityManager, - MediaManagerInterface $mediaManager + MediaManagerInterface $mediaManager, + WebspaceManagerInterface $webspaceManager, + RouteManagerInterface $routeManager, + RouteRepositoryInterface $routeRepository ) { $this->doctrineListRepresentationFactory = $doctrineListRepresentationFactory; $this->entityManager = $entityManager; $this->mediaManager = $mediaManager; + $this->webspaceManager = $webspaceManager; + $this->routeManager = $routeManager; + $this->routeRepository = $routeRepository; } /** @@ -66,6 +79,7 @@ public function putAction(Request $request, int $id): Response /** @var EventData $data */ $data = $request->toArray(); $this->mapDataToEntity($data, $event); + $this->updateRoutesForEntity($event); $this->entityManager->flush(); return $this->json($this->getDataForEntity($event)); @@ -84,6 +98,9 @@ public function postAction(Request $request): Response $this->entityManager->persist($event); $this->entityManager->flush(); + $this->updateRoutesForEntity($event); + $this->entityManager->flush(); + return $this->json($this->getDataForEntity($event), 201); } @@ -94,6 +111,7 @@ public function deleteAction(int $id): Response { /** @var Event $event */ $event = $this->entityManager->getReference(Event::class, $id); + $this->removeRoutesForEntity($event); $this->entityManager->remove($event); $this->entityManager->flush(); @@ -124,6 +142,7 @@ protected function getDataForEntity(Event $entity): array return [ 'id' => $entity->getId(), 'name' => $entity->getName(), + 'routePath' => $entity->getRoutePath(), 'image' => $image ? ['id' => $image->getId()] : null, @@ -140,11 +159,41 @@ protected function mapDataToEntity(array $data, Event $entity): void $imageId = $data['image']['id'] ?? null; $entity->setName($data['name']); + $entity->setRoutePath($data['routePath']); $entity->setImage($imageId ? $this->mediaManager->getEntityById($imageId) : null); $entity->setStartDate($data['startDate'] ? new \DateTimeImmutable($data['startDate']) : null); $entity->setEndDate($data['endDate'] ? new \DateTimeImmutable($data['endDate']) : null); } + protected function updateRoutesForEntity(Event $entity): void + { + // create route for all locales of the application because event entity is not localized + foreach ($this->webspaceManager->getAllLocales() as $locale) { + $this->routeManager->createOrUpdateByAttributes( + Event::class, + (string) $entity->getId(), + $locale, + $entity->getRoutePath(), + ); + } + } + + protected function removeRoutesForEntity(Event $entity): void + { + // remove route for all locales of the application because event entity is not localized + foreach ($this->webspaceManager->getAllLocales() as $locale) { + $routes = $this->routeRepository->findAllByEntity( + Event::class, + (string) $entity->getId(), + $locale + ); + + foreach ($routes as $route) { + $this->routeRepository->remove($route); + } + } + } + public function getSecurityContext(): string { return Event::SECURITY_CONTEXT; diff --git a/src/Controller/Website/EventController.php b/src/Controller/Website/EventController.php new file mode 100644 index 00000000..d47b071f --- /dev/null +++ b/src/Controller/Website/EventController.php @@ -0,0 +1,60 @@ +webspaceManager = $webspaceManager; + $this->routeRepository = $routeRepository; + $this->templateAttributeResolver = $templateAttributeResolver; + } + + public function indexAction(Event $event): Response + { + $parameters = $this->templateAttributeResolver->resolve([ + 'event' => $event, + 'localizations' => $this->getLocalizationsArrayForEntity($event), + ]); + + return $this->render('events/event.html.twig', $parameters); + } + + /** + * @return array + */ + protected function getLocalizationsArrayForEntity(Event $entity): array + { + $routes = $this->routeRepository->findAllByEntity(Event::class, (string) $entity->getId()); + + $localizations = []; + foreach ($routes as $route) { + $url = $this->webspaceManager->findUrlByResourceLocator( + $route->getPath(), + null, + $route->getLocale() + ); + + $localizations[$route->getLocale()] = ['locale' => $route->getLocale(), 'url' => $url]; + } + + return $localizations; + } +} diff --git a/src/Entity/Event.php b/src/Entity/Event.php index b85c5117..6e15343d 100644 --- a/src/Entity/Event.php +++ b/src/Entity/Event.php @@ -28,6 +28,11 @@ class Event */ private string $name; + /** + * @ORM\Column(type="string", length=255) + */ + private string $routePath; + /** * @ORM\ManyToOne(targetEntity=MediaInterface::class) * @ORM\JoinColumn(onDelete="SET NULL") @@ -59,6 +64,16 @@ public function setName(string $name): void $this->name = $name; } + public function getRoutePath(): string + { + return $this->routePath ?? ''; + } + + public function setRoutePath(string $routePath): void + { + $this->routePath = $routePath; + } + public function getImage(): ?MediaInterface { return $this->image; diff --git a/src/Routing/EventRouteDefaultsProvider.php b/src/Routing/EventRouteDefaultsProvider.php new file mode 100644 index 00000000..0fc06c6e --- /dev/null +++ b/src/Routing/EventRouteDefaultsProvider.php @@ -0,0 +1,40 @@ +entityManager = $entityManager; + } + + /** + * @return mixed[] + */ + public function getByEntity($entityClass, $id, $locale, $object = null) + { + return [ + '_controller' => EventController::class . '::indexAction', + 'event' => $object ?: $this->entityManager->getRepository(Event::class)->find($id), + ]; + } + + public function isPublished($entityClass, $id, $locale) + { + return true; + } + + public function supports($entityClass) + { + return Event::class === $entityClass; + } +} diff --git a/templates/events/event.html.twig b/templates/events/event.html.twig new file mode 100644 index 00000000..429821b0 --- /dev/null +++ b/templates/events/event.html.twig @@ -0,0 +1,5 @@ +{% extends 'base.html.twig' %} + +{% block contentBody %} +

{{ event.name }}

+{% endblock %}