-
Notifications
You must be signed in to change notification settings - Fork 42
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] Integrate custom entity into the administration interface #84
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
<?xml version="1.0" ?> | ||
<form xmlns="http://schemas.sulu.io/template/template" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.sulu.io/template/form-1.0.xsd" | ||
> | ||
<key>event_details</key> | ||
|
||
<properties> | ||
<property name="name" type="text_line" mandatory="true" colspan="12"> | ||
<meta> | ||
<title>sulu_admin.name</title> | ||
</meta> | ||
|
||
<params> | ||
<param name="headline" value="true"/> | ||
</params> | ||
</property> | ||
|
||
<property name="image" type="single_media_selection" colspan="12"> | ||
<meta> | ||
<title>app.image</title> | ||
</meta> | ||
</property> | ||
|
||
<property name="startDate" type="date" colspan="6"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Available values for the |
||
<meta> | ||
<title>app.start_date</title> | ||
</meta> | ||
</property> | ||
|
||
<property name="endDate" type="date" colspan="6"> | ||
<meta> | ||
<title>app.end_date</title> | ||
</meta> | ||
</property> | ||
</properties> | ||
</form> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
<?xml version="1.0" ?> | ||
<list xmlns="http://schemas.sulu.io/list-builder/list"> | ||
<key>events</key> | ||
|
||
<properties> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. List configurations can include fields of associated entities by defining |
||
<property name="id" visibility="no" translation="sulu_admin.id"> | ||
<field-name>id</field-name> | ||
<entity-name>App\Entity\Event</entity-name> | ||
</property> | ||
|
||
<property name="name" visibility="always" searchability="yes" translation="sulu_admin.name"> | ||
<field-name>name</field-name> | ||
<entity-name>App\Entity\Event</entity-name> | ||
</property> | ||
|
||
<property name="startDate" visibility="yes" translation="app.start_date" type="date"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Available values for the |
||
<field-name>startDate</field-name> | ||
<entity-name>App\Entity\Event</entity-name> | ||
|
||
<filter type="datetime" /> | ||
</property> | ||
|
||
<property name="endDate" visibility="yes" translation="app.end_date" type="date"> | ||
<field-name>endDate</field-name> | ||
<entity-name>App\Entity\Event</entity-name> | ||
|
||
<filter type="datetime" /> | ||
</property> | ||
</properties> | ||
</list> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
sulu_admin: | ||
resources: | ||
events: | ||
routes: | ||
list: 'app.get_event_list' | ||
detail: 'app.get_event' |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Admin; | ||
|
||
use App\Entity\Event; | ||
use Sulu\Bundle\AdminBundle\Admin\Admin; | ||
use Sulu\Bundle\AdminBundle\Admin\Navigation\NavigationItem; | ||
use Sulu\Bundle\AdminBundle\Admin\Navigation\NavigationItemCollection; | ||
use Sulu\Bundle\AdminBundle\Admin\View\ToolbarAction; | ||
use Sulu\Bundle\AdminBundle\Admin\View\ViewBuilderFactoryInterface; | ||
use Sulu\Bundle\AdminBundle\Admin\View\ViewCollection; | ||
use Sulu\Component\Security\Authorization\PermissionTypes; | ||
use Sulu\Component\Security\Authorization\SecurityCheckerInterface; | ||
|
||
class EventAdmin extends Admin | ||
{ | ||
public const LIST_VIEW = 'app.event.list'; | ||
public const ADD_FORM_VIEW = 'app.event.add_form'; | ||
public const ADD_FORM_DETAILS_VIEW = 'app.event.add_form.details'; | ||
public const EDIT_FORM_VIEW = 'app.event.edit_form'; | ||
public const EDIT_FORM_DETAILS_VIEW = 'app.event.edit_form.details'; | ||
|
||
private ViewBuilderFactoryInterface $viewBuilderFactory; | ||
private SecurityCheckerInterface $securityChecker; | ||
|
||
public function __construct( | ||
ViewBuilderFactoryInterface $viewBuilderFactory, | ||
SecurityCheckerInterface $securityChecker | ||
) { | ||
$this->viewBuilderFactory = $viewBuilderFactory; | ||
$this->securityChecker = $securityChecker; | ||
} | ||
|
||
public function configureNavigationItems(NavigationItemCollection $navigationItemCollection): void | ||
{ | ||
if ($this->securityChecker->hasPermission(Event::SECURITY_CONTEXT, PermissionTypes::EDIT)) { | ||
$rootNavigationItem = new NavigationItem('app.events'); | ||
$rootNavigationItem->setIcon('su-calendar'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Available icons are documented in the Sulu Javascript Docs. |
||
$rootNavigationItem->setPosition(30); | ||
$rootNavigationItem->setView(static::LIST_VIEW); | ||
|
||
$navigationItemCollection->add($rootNavigationItem); | ||
} | ||
} | ||
|
||
public function configureViews(ViewCollection $viewCollection): void | ||
{ | ||
$formToolbarActions = []; | ||
$listToolbarActions = []; | ||
|
||
if ($this->securityChecker->hasPermission(Event::SECURITY_CONTEXT, PermissionTypes::ADD)) { | ||
$listToolbarActions[] = new ToolbarAction('sulu_admin.add'); | ||
} | ||
|
||
if ($this->securityChecker->hasPermission(Event::SECURITY_CONTEXT, PermissionTypes::EDIT)) { | ||
$formToolbarActions[] = new ToolbarAction('sulu_admin.save'); | ||
} | ||
|
||
if ($this->securityChecker->hasPermission(Event::SECURITY_CONTEXT, PermissionTypes::DELETE)) { | ||
$formToolbarActions[] = new ToolbarAction('sulu_admin.delete'); | ||
$listToolbarActions[] = new ToolbarAction('sulu_admin.delete'); | ||
} | ||
|
||
if ($this->securityChecker->hasPermission(Event::SECURITY_CONTEXT, PermissionTypes::VIEW)) { | ||
$listToolbarActions[] = new ToolbarAction('sulu_admin.export'); | ||
} | ||
|
||
if ($this->securityChecker->hasPermission(Event::SECURITY_CONTEXT, PermissionTypes::EDIT)) { | ||
$viewCollection->add( | ||
$this->viewBuilderFactory->createListViewBuilder(static::LIST_VIEW, '/events') | ||
->setResourceKey(Event::RESOURCE_KEY) | ||
->setListKey(Event::LIST_KEY) | ||
->setTitle('app.events') | ||
->addListAdapters(['table']) | ||
->setAddView(static::ADD_FORM_VIEW) | ||
->setEditView(static::EDIT_FORM_VIEW) | ||
->addToolbarActions($listToolbarActions) | ||
); | ||
|
||
$viewCollection->add( | ||
$this->viewBuilderFactory->createResourceTabViewBuilder(static::ADD_FORM_VIEW, '/events/add') | ||
->setResourceKey(Event::RESOURCE_KEY) | ||
->setBackView(static::LIST_VIEW) | ||
); | ||
|
||
$viewCollection->add( | ||
$this->viewBuilderFactory->createFormViewBuilder(static::ADD_FORM_DETAILS_VIEW, '/details') | ||
->setResourceKey(Event::RESOURCE_KEY) | ||
->setFormKey(Event::FORM_KEY) | ||
->setTabTitle('sulu_admin.details') | ||
->setEditView(static::EDIT_FORM_VIEW) | ||
->addToolbarActions($formToolbarActions) | ||
->setParent(static::ADD_FORM_VIEW) | ||
); | ||
|
||
$viewCollection->add( | ||
$this->viewBuilderFactory->createResourceTabViewBuilder(static::EDIT_FORM_VIEW, '/events/:id') | ||
->setResourceKey(Event::RESOURCE_KEY) | ||
->setBackView(static::LIST_VIEW) | ||
); | ||
|
||
$viewCollection->add( | ||
$this->viewBuilderFactory->createFormViewBuilder(static::EDIT_FORM_DETAILS_VIEW, '/details') | ||
->setResourceKey(Event::RESOURCE_KEY) | ||
->setFormKey(Event::FORM_KEY) | ||
->setTabTitle('sulu_admin.details') | ||
->addToolbarActions($formToolbarActions) | ||
->setParent(static::EDIT_FORM_VIEW) | ||
); | ||
} | ||
} | ||
|
||
/** | ||
* @return mixed[] | ||
*/ | ||
public function getSecurityContexts(): array | ||
{ | ||
return [ | ||
self::SULU_ADMIN_SECURITY_SYSTEM => [ | ||
'Events' => [ | ||
Event::SECURITY_CONTEXT => [ | ||
PermissionTypes::VIEW, | ||
PermissionTypes::ADD, | ||
PermissionTypes::EDIT, | ||
PermissionTypes::DELETE, | ||
], | ||
], | ||
], | ||
]; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Controller\Admin; | ||
|
||
use App\Common\DoctrineListRepresentationFactory; | ||
use App\Entity\Event; | ||
use Doctrine\ORM\EntityManagerInterface; | ||
use Sulu\Bundle\MediaBundle\Media\Manager\MediaManagerInterface; | ||
use Sulu\Component\Security\SecuredControllerInterface; | ||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpFoundation\Response; | ||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; | ||
use Symfony\Component\Routing\Annotation\Route; | ||
|
||
/** | ||
* @phpstan-type EventData array{ | ||
* id: int|null, | ||
* name: string, | ||
* image: array{id: int}|null, | ||
* startDate: string|null, | ||
* endDate: string|null, | ||
* } | ||
*/ | ||
class EventController extends AbstractController implements SecuredControllerInterface | ||
{ | ||
private DoctrineListRepresentationFactory $doctrineListRepresentationFactory; | ||
private EntityManagerInterface $entityManager; | ||
private MediaManagerInterface $mediaManager; | ||
|
||
public function __construct( | ||
DoctrineListRepresentationFactory $doctrineListRepresentationFactory, | ||
EntityManagerInterface $entityManager, | ||
MediaManagerInterface $mediaManager | ||
) { | ||
$this->doctrineListRepresentationFactory = $doctrineListRepresentationFactory; | ||
$this->entityManager = $entityManager; | ||
$this->mediaManager = $mediaManager; | ||
} | ||
|
||
/** | ||
* @Route("/admin/api/events/{id}", methods={"GET"}, name="app.get_event") | ||
*/ | ||
public function getAction(int $id): Response | ||
{ | ||
$event = $this->entityManager->getRepository(Event::class)->find($id); | ||
if (!$event) { | ||
throw new NotFoundHttpException(); | ||
} | ||
|
||
return $this->json($this->getDataForEntity($event)); | ||
} | ||
|
||
/** | ||
* @Route("/admin/api/events/{id}", methods={"PUT"}, name="app.put_event") | ||
*/ | ||
public function putAction(Request $request, int $id): Response | ||
{ | ||
$event = $this->entityManager->getRepository(Event::class)->find($id); | ||
if (!$event) { | ||
throw new NotFoundHttpException(); | ||
} | ||
|
||
/** @var EventData $data */ | ||
$data = $request->toArray(); | ||
$this->mapDataToEntity($data, $event); | ||
$this->entityManager->flush(); | ||
|
||
return $this->json($this->getDataForEntity($event)); | ||
} | ||
|
||
/** | ||
* @Route("/admin/api/events", methods={"POST"}, name="app.post_event") | ||
*/ | ||
public function postAction(Request $request): Response | ||
{ | ||
$event = new Event(); | ||
|
||
/** @var EventData $data */ | ||
$data = $request->toArray(); | ||
$this->mapDataToEntity($data, $event); | ||
$this->entityManager->persist($event); | ||
$this->entityManager->flush(); | ||
|
||
return $this->json($this->getDataForEntity($event), 201); | ||
} | ||
|
||
/** | ||
* @Route("/admin/api/events/{id}", methods={"DELETE"}, name="app.delete_event") | ||
*/ | ||
public function deleteAction(int $id): Response | ||
{ | ||
/** @var Event $event */ | ||
$event = $this->entityManager->getReference(Event::class, $id); | ||
$this->entityManager->remove($event); | ||
$this->entityManager->flush(); | ||
|
||
return $this->json(null, 204); | ||
} | ||
|
||
/** | ||
* @Route("/admin/api/events", methods={"GET"}, name="app.get_event_list") | ||
*/ | ||
public function getListAction(): Response | ||
{ | ||
$listRepresentation = $this->doctrineListRepresentationFactory->createDoctrineListRepresentation( | ||
Event::RESOURCE_KEY | ||
); | ||
|
||
return $this->json($listRepresentation->toArray()); | ||
} | ||
|
||
/** | ||
* @return EventData $data | ||
*/ | ||
protected function getDataForEntity(Event $entity): array | ||
{ | ||
$image = $entity->getImage(); | ||
$startDate = $entity->getStartDate(); | ||
$endDate = $entity->getEndDate(); | ||
|
||
return [ | ||
'id' => $entity->getId(), | ||
'name' => $entity->getName(), | ||
'image' => $image | ||
? ['id' => $image->getId()] | ||
: null, | ||
'startDate' => $startDate ? $startDate->format('c') : null, | ||
'endDate' => $endDate ? $endDate->format('c') : null, | ||
]; | ||
} | ||
|
||
/** | ||
* @param EventData $data | ||
*/ | ||
protected function mapDataToEntity(array $data, Event $entity): void | ||
{ | ||
$imageId = $data['image']['id'] ?? null; | ||
|
||
$entity->setName($data['name']); | ||
$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); | ||
} | ||
|
||
public function getSecurityContext(): string | ||
{ | ||
return Event::SECURITY_CONTEXT; | ||
} | ||
|
||
public function getLocale(Request $request): ?string | ||
{ | ||
return $request->query->get('locale'); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The form view can be structured by using
<section>
tags in the form configuration. An example can be found in the form configuration of the built-in contact entity.