Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Example] Assign route to custom entity #87

Draft
wants to merge 1 commit into
base: example/custom-entity
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions config/forms/event_details.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@
<params>
<param name="headline" value="true"/>
</params>

<tag name="sulu.rlp.part"/>
</property>

<property name="routePath" type="route" mandatory="true" colspan="12">
<meta>
<title>sulu_admin.url</title>
</meta>

<params>
<param name="entity_class" value="App\Entity\Event"/>
<param name="route_schema" value="/events/{implode('-', object)}"/>
</params>
</property>

<property name="image" type="single_media_selection" colspan="12">
Expand Down
3 changes: 3 additions & 0 deletions config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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' }]
51 changes: 50 additions & 1 deletion src/Controller/Admin/EventController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
Expand All @@ -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;
}

/**
Expand Down Expand Up @@ -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));
Expand All @@ -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);
}

Expand All @@ -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();

Expand Down Expand Up @@ -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,
Expand All @@ -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) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason of going through all locales, when updating a route for specific locale? This usually would throw an error with NULL for the rest of the locales that are not currently edited/created

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As stated in the comment in the previous line, the event entity of the example is not localized. If your entity is localized, you want to update only the route of the current 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;
Expand Down
60 changes: 60 additions & 0 deletions src/Controller/Website/EventController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

declare(strict_types=1);

namespace App\Controller\Website;

use App\Entity\Event;
use Sulu\Bundle\RouteBundle\Entity\RouteRepositoryInterface;
use Sulu\Bundle\WebsiteBundle\Resolver\TemplateAttributeResolverInterface;
use Sulu\Component\Webspace\Manager\WebspaceManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;

class EventController extends AbstractController
{
private WebspaceManagerInterface $webspaceManager;
private RouteRepositoryInterface $routeRepository;
private TemplateAttributeResolverInterface $templateAttributeResolver;

public function __construct(
WebspaceManagerInterface $webspaceManager,
RouteRepositoryInterface $routeRepository,
TemplateAttributeResolverInterface $templateAttributeResolver
) {
$this->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<string, array{locale: string, url:string|null}>
*/
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;
}
}
15 changes: 15 additions & 0 deletions src/Entity/Event.php
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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;
Expand Down
40 changes: 40 additions & 0 deletions src/Routing/EventRouteDefaultsProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace App\Routing;

use App\Controller\Website\EventController;
use App\Entity\Event;
use Doctrine\ORM\EntityManagerInterface;
use Sulu\Bundle\RouteBundle\Routing\Defaults\RouteDefaultsProviderInterface;

class EventRouteDefaultsProvider implements RouteDefaultsProviderInterface
{
private EntityManagerInterface $entityManager;

public function __construct(
EntityManagerInterface $entityManager
) {
$this->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;
}
}
5 changes: 5 additions & 0 deletions templates/events/event.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{% extends 'base.html.twig' %}

{% block contentBody %}
<h1>{{ event.name }}</h1>
{% endblock %}