mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 10:34:09 +00:00
1022 lines
33 KiB
PHP
1022 lines
33 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/*
|
|
* Chill is a software for social workers
|
|
*
|
|
* For the full copyright and license information, please view
|
|
* the LICENSE file that was distributed with this source code.
|
|
*/
|
|
|
|
namespace Chill\MainBundle\CRUD\Controller;
|
|
|
|
use Chill\MainBundle\CRUD\Form\CRUDDeleteEntityForm;
|
|
use Chill\MainBundle\CRUD\Resolver\Resolver;
|
|
use Chill\MainBundle\Pagination\PaginatorFactory;
|
|
use Chill\MainBundle\Pagination\PaginatorInterface;
|
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
|
use Chill\MainBundle\Templating\Listing\FilterOrderHelper;
|
|
use Chill\MainBundle\Templating\Listing\FilterOrderHelperFactoryInterface;
|
|
use Doctrine\ORM\QueryBuilder;
|
|
use Doctrine\Persistence\ManagerRegistry;
|
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
|
use Symfony\Component\Form\FormInterface;
|
|
use Symfony\Component\HttpFoundation\Request;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
|
use Symfony\Component\Serializer\SerializerInterface;
|
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
|
|
|
/**
|
|
* The CRUDController class is an abstract class that provides basic CRUD (Create, Read, Update, Delete) functionality for entities in a Symfony application. It extends the Abstract
|
|
*Controller class.
|
|
*
|
|
* For the class \Chill\MainBundle\CRUD\Controller\CRUDController, the dependency injection system does not work as in
|
|
* the usual way, where dependencies are provided via the constructor method. Instead, it utilizes the capabilities
|
|
* provided by the \Symfony\Contracts\Service\ServiceSubscriberInterface.
|
|
*
|
|
* The \Symfony\Contracts\Service\ServiceSubscriberInterface is an interface that is used by objects needing explicit
|
|
* service references. It means this class has a direct dependency on the Symfony service container, allowing it to
|
|
* request specific services it needs for execution.
|
|
*
|
|
* By implementing the ServiceSubscriberInterface, this class effectively defines a method called getSubscribedServices,
|
|
* which returns an array mapping service IDs to their classes or interfaces. Symfony's service container uses this
|
|
* method to preconfigure the services required, optimizing the dependency injection process.
|
|
*
|
|
* This model can be used to keep the class itself concise by preventing the need to define a constructor and inject
|
|
* services through method calls. This approach also allows for more flexible service usage, as services can be fetched
|
|
* as and when required, rather than all services being loaded whether they are used or not. This makes for a more
|
|
* efficient and maintainable solution especially when dealing with larger services.
|
|
*
|
|
* For more details about how this works, you can refer to Symfony's Documentation
|
|
* on Service Subscribers & Service Locators: https://symfony.com/doc/current/service_container/service_subscribers_locators.html
|
|
*/
|
|
class CRUDController extends AbstractController
|
|
{
|
|
/**
|
|
* The crud configuration.
|
|
*
|
|
* This configuration si defined by `chill_main['crud']`.
|
|
*/
|
|
protected array $crudConfig;
|
|
|
|
public function CRUD(?string $parameter): Response
|
|
{
|
|
return new Response($parameter);
|
|
}
|
|
|
|
public function delete(Request $request, $id): Response
|
|
{
|
|
return $this->deleteAction('delete', $request, $id);
|
|
}
|
|
|
|
/**
|
|
* BAse method for edit action.
|
|
*
|
|
* IMplemented by the method formEditAction, with action as 'edit'
|
|
*/
|
|
public function edit(Request $request, mixed $id): Response
|
|
{
|
|
return $this->formEditAction('edit', $request, $id);
|
|
}
|
|
|
|
/**
|
|
* Get the context for the serialization.
|
|
*/
|
|
public function getContextForSerialization(string $action, Request $request, mixed $entity, string $_format): array
|
|
{
|
|
return [];
|
|
}
|
|
|
|
public static function getSubscribedServices(): array
|
|
{
|
|
return \array_merge(
|
|
parent::getSubscribedServices(),
|
|
[
|
|
'chill_main.paginator_factory' => PaginatorFactory::class,
|
|
'translator' => TranslatorInterface::class,
|
|
AuthorizationHelper::class => AuthorizationHelper::class,
|
|
EventDispatcherInterface::class => EventDispatcherInterface::class,
|
|
Resolver::class => Resolver::class,
|
|
SerializerInterface::class => SerializerInterface::class,
|
|
FilterOrderHelperFactoryInterface::class => FilterOrderHelperFactoryInterface::class,
|
|
ManagerRegistry::class => ManagerRegistry::class,
|
|
]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Base method called by index action.
|
|
*
|
|
* @return type
|
|
*/
|
|
public function index(Request $request)
|
|
{
|
|
return $this->indexEntityAction('index', $request);
|
|
}
|
|
|
|
/**
|
|
* Base method for new action.
|
|
*
|
|
* Implemented by the method formNewAction, with action as 'new'
|
|
*/
|
|
public function new(Request $request): Response
|
|
{
|
|
return $this->formCreateAction('new', $request);
|
|
}
|
|
|
|
public function setCrudConfig(array $config): void
|
|
{
|
|
$this->crudConfig = $config;
|
|
}
|
|
|
|
/**
|
|
* Base method for the view action.
|
|
*
|
|
* Implemented by the method viewAction, with action as 'view'
|
|
*/
|
|
public function view(Request $request, mixed $id): Response
|
|
{
|
|
return $this->viewAction('view', $request, $id);
|
|
}
|
|
|
|
/**
|
|
* build a default role name, using the crud resolver.
|
|
*
|
|
* This method should not be overriden. Override `getRoleFor` instead.
|
|
*
|
|
* @param string $action
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function buildDefaultRole($action)
|
|
{
|
|
return $this->getCrudResolver()->buildDefaultRole(
|
|
$this->getCrudName(),
|
|
$action
|
|
);
|
|
}
|
|
|
|
protected function buildFilterOrderHelper(string $action, Request $request): ?FilterOrderHelper
|
|
{
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Build the base query for listing all entities, normally use in a listing
|
|
* page.
|
|
*
|
|
* This base query does not contains any `WHERE` or `SELECT` clauses. Those
|
|
* are added by other methods, like `queryEntities` and `countQueries`.
|
|
*
|
|
* @return QueryBuilder
|
|
*/
|
|
protected function buildQueryEntities(string $action, Request $request)
|
|
{
|
|
$query = $this->getManagerRegistry()
|
|
->getManager()
|
|
->createQueryBuilder()
|
|
->select('e')
|
|
->from($this->getEntityClass(), 'e');
|
|
|
|
$this->customizeQuery($action, $request, $query);
|
|
|
|
return $query;
|
|
}
|
|
|
|
/**
|
|
* check the acl. Called by every action.
|
|
*
|
|
* By default, check the role given by `getRoleFor` for the value given in
|
|
* $entity.
|
|
*
|
|
* Throw an \Symfony\Component\Security\Core\Exception\AccessDeniedHttpException
|
|
* if not accessible.
|
|
*
|
|
* @throws \Symfony\Component\Security\Core\Exception\AccessDeniedHttpException
|
|
*/
|
|
protected function checkACL(string $action, mixed $entity)
|
|
{
|
|
$this->denyAccessUnlessGranted($this->getRoleFor($action), $entity);
|
|
}
|
|
|
|
/**
|
|
* Count the number of entities.
|
|
*/
|
|
protected function countEntities(string $action, Request $request, ?FilterOrderHelper $filterOrder = null): int
|
|
{
|
|
return $this->buildQueryEntities($action, $request)
|
|
->select('COUNT(e)')
|
|
->getQuery()
|
|
->getSingleScalarResult();
|
|
}
|
|
|
|
/**
|
|
* Create an entity.
|
|
*/
|
|
protected function createEntity(string $action, Request $request): object
|
|
{
|
|
$type = $this->getEntityClass();
|
|
|
|
return new $type();
|
|
}
|
|
|
|
/**
|
|
* Create a form.
|
|
*
|
|
* use the method `getFormClassFor`
|
|
*
|
|
* A hook is available: `customizeForm` allow you to customize the form
|
|
* if needed.
|
|
*
|
|
* It is preferable to override customizeForm instead of overriding
|
|
* this method.
|
|
*/
|
|
protected function createFormFor(string $action, mixed $entity, ?string $formClass = null, array $formOptions = []): FormInterface
|
|
{
|
|
$formClass ??= $this->getFormClassFor($action);
|
|
|
|
$form = $this->createForm($formClass, $entity, $formOptions);
|
|
|
|
$this->customizeForm($action, $form);
|
|
|
|
return $form;
|
|
}
|
|
|
|
/**
|
|
* Customize the form created by createFormFor.
|
|
*/
|
|
protected function customizeForm(string $action, FormInterface $form) {}
|
|
|
|
protected function customizeQuery(string $action, Request $request, $query): void {}
|
|
|
|
/**
|
|
* @param null $formClass
|
|
*/
|
|
protected function deleteAction(string $action, Request $request, $id, $formClass = null): Response
|
|
{
|
|
$this->onPreDelete($action, $request);
|
|
|
|
$entity = $this->getEntity($action, $id, $request);
|
|
|
|
$postFetch = $this->onPostFetchEntity($action, $request, $entity);
|
|
|
|
if ($postFetch instanceof Response) {
|
|
return $postFetch;
|
|
}
|
|
|
|
if (null === $entity) {
|
|
throw $this->createNotFoundException(sprintf('The %s with id %s is not found', $this->getCrudName(), $id));
|
|
}
|
|
|
|
$response = $this->checkACL($action, $entity);
|
|
|
|
if ($response instanceof Response) {
|
|
return $response;
|
|
}
|
|
|
|
$response = $this->onPostCheckACL($action, $request, $entity);
|
|
|
|
if ($response instanceof Response) {
|
|
return $response;
|
|
}
|
|
|
|
$form = $this->createFormFor($action, $entity, $formClass);
|
|
|
|
$form->handleRequest($request);
|
|
|
|
if ($form->isSubmitted() && $form->isValid()) {
|
|
$this->onFormValid($action, $entity, $form, $request);
|
|
$em = $this->getManagerRegistry()->getManager();
|
|
|
|
$this->onPreRemove($action, $entity, $form, $request);
|
|
$this->removeEntity($action, $entity, $form, $request);
|
|
$this->onPostRemove($action, $entity, $form, $request);
|
|
|
|
$this->onPreFlush($action, $entity, $form, $request);
|
|
$em->flush();
|
|
$this->onPostFlush($action, $entity, $form, $request);
|
|
|
|
$this->addFlash('success', $this->generateFormSuccessMessage($action, $entity));
|
|
|
|
$result = $this->onBeforeRedirectAfterSubmission($action, $entity, $form, $request);
|
|
|
|
if ($result instanceof Response) {
|
|
return $result;
|
|
}
|
|
|
|
return $this->redirectToRoute('chill_crud_'.$this->getCrudName().'_view', ['id' => $entity->getId()]);
|
|
}
|
|
|
|
if ($form->isSubmitted()) {
|
|
$this->addFlash('error', $this->generateFormErrorMessage($action, $form));
|
|
}
|
|
|
|
$defaultTemplateParameters = [
|
|
'form' => $form->createView(),
|
|
'entity' => $entity,
|
|
'crud_name' => $this->getCrudName(),
|
|
];
|
|
|
|
return $this->render(
|
|
$this->getTemplateFor($action, $entity, $request),
|
|
$this->generateTemplateParameter($action, $entity, $request, $defaultTemplateParameters)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Duplicate an entity.
|
|
*/
|
|
protected function duplicateEntity(string $action, Request $request)
|
|
{
|
|
$id = $request->query->get('duplicate_id', 0);
|
|
$originalEntity = $this->getEntity($action, $id, $request);
|
|
|
|
$this->getManagerRegistry()->getManager()
|
|
->detach($originalEntity);
|
|
|
|
return $originalEntity;
|
|
}
|
|
|
|
/**
|
|
* The new (or creation) action.
|
|
*
|
|
* Some steps may be overriden during this process of rendering:
|
|
*
|
|
* This method:
|
|
*
|
|
* 1. Create or duplicate an entity:
|
|
*
|
|
* If the `duplicate` parameter is present, the entity is duplicated
|
|
* using the `duplicate` method.
|
|
*
|
|
* If not, the entity is created using the `create` method.
|
|
* 3. check ACL using `checkACL` ;
|
|
* 4. launch `onPostCheckACL`. If the result is an instance of Response,
|
|
* this response is returned ;
|
|
* 5. generate a form using `createFormFor`, and handle request on this form;
|
|
*
|
|
* If the form is valid, the entity is stored and flushed, and a redirection
|
|
* is returned.
|
|
*
|
|
* In this case, those hooks are available:
|
|
*
|
|
* * onFormValid
|
|
* * onPreFlush
|
|
* * onPostFlush
|
|
* * onBeforeRedirectAfterSubmission. If this method return an instance of
|
|
* Response, this response is returned.
|
|
*
|
|
* 5. generate default template parameters:
|
|
*
|
|
* * `entity`: the fetched entity ;
|
|
* * `crud_name`: the crud name ;
|
|
* * `form`: the formview instance.
|
|
*
|
|
* 6. Launch rendering, the parameter is fetch using `getTemplateFor`
|
|
* The parameters may be personnalized using `generateTemplateParameter`.
|
|
*
|
|
* @param type $formClass
|
|
*/
|
|
protected function formCreateAction(string $action, Request $request, $formClass = null): Response
|
|
{
|
|
if ($request->query->has('duplicate')) {
|
|
$entity = $this->duplicateEntity($action, $request);
|
|
} else {
|
|
$entity = $this->createEntity($action, $request);
|
|
}
|
|
|
|
$response = $this->checkACL($action, $entity);
|
|
|
|
if ($response instanceof Response) {
|
|
return $response;
|
|
}
|
|
|
|
$response = $this->onPostCheckACL($action, $request, $entity);
|
|
|
|
if ($response instanceof Response) {
|
|
return $response;
|
|
}
|
|
|
|
$form = $this->createFormFor($action, $entity, $formClass);
|
|
|
|
$form->handleRequest($request);
|
|
|
|
if ($form->isSubmitted() && $form->isValid()) {
|
|
$this->onFormValid($action, $entity, $form, $request);
|
|
$em = $this->getManagerRegistry()->getManager();
|
|
|
|
$this->onPrePersist($action, $entity, $form, $request);
|
|
$em->persist($entity);
|
|
$this->onPostPersist($action, $entity, $form, $request);
|
|
|
|
$this->onPreFlush($action, $entity, $form, $request);
|
|
$em->flush();
|
|
$this->onPostFlush($action, $entity, $form, $request);
|
|
|
|
$this->addFlash('success', $this->generateFormSuccessMessage($action, $entity));
|
|
|
|
$result = $this->onBeforeRedirectAfterSubmission($action, $entity, $form, $request);
|
|
|
|
if ($result instanceof Response) {
|
|
return $result;
|
|
}
|
|
|
|
return $this->redirectToRoute('chill_crud_'.$this->getCrudName().'_view', ['id' => $entity->getId()]);
|
|
}
|
|
|
|
if ($form->isSubmitted()) {
|
|
$this->addFlash('error', $this->generateFormErrorMessage($action, $form));
|
|
}
|
|
|
|
$defaultTemplateParameters = [
|
|
'form' => $form->createView(),
|
|
'entity' => $entity,
|
|
'crud_name' => $this->getCrudName(),
|
|
];
|
|
|
|
return $this->render(
|
|
$this->getTemplateFor($action, $entity, $request),
|
|
$this->generateTemplateParameter($action, $entity, $request, $defaultTemplateParameters)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* The edit action.
|
|
*
|
|
* Some steps may be overriden during this process of rendering:
|
|
*
|
|
* This method:
|
|
*
|
|
* 1. fetch the entity, using `getEntity`
|
|
* 2. launch `onPostFetchEntity`. If postfetch is an instance of Response,
|
|
* this response is returned.
|
|
* 2. throw an HttpNotFoundException if entity is null
|
|
* 3. check ACL using `checkACL` ;
|
|
* 4. launch `onPostCheckACL`. If the result is an instance of Response,
|
|
* this response is returned ;
|
|
* 5. generate a form using `createFormFor`, and handle request on this form;
|
|
*
|
|
* If the form is valid, the entity is stored and flushed, and a redirection
|
|
* is returned.
|
|
*
|
|
* In this case, those hooks are available:
|
|
*
|
|
* * onFormValid
|
|
* * onPreFlush
|
|
* * onPostFlush
|
|
* * onBeforeRedirectAfterSubmission. If this method return an instance of
|
|
* Response, this response is returned.
|
|
*
|
|
* 5. generate default template parameters:
|
|
*
|
|
* * `entity`: the fetched entity ;
|
|
* * `crud_name`: the crud name ;
|
|
* * `form`: the formview instance.
|
|
*
|
|
* 6. Launch rendering, the parameter is fetch using `getTemplateFor`
|
|
* The parameters may be personnalized using `generateTemplateParameter`.
|
|
*
|
|
* @param class-string $formClass
|
|
*
|
|
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
|
|
*/
|
|
protected function formEditAction(string $action, Request $request, mixed $id, ?string $formClass = null, array $formOptions = []): Response
|
|
{
|
|
$entity = $this->getEntity($action, $id, $request);
|
|
|
|
if (null === $entity) {
|
|
throw $this->createNotFoundException(sprintf('The %s with id %s is not found', $this->getCrudName(), $id));
|
|
}
|
|
|
|
$response = $this->checkACL($action, $entity);
|
|
|
|
if ($response instanceof Response) {
|
|
return $response;
|
|
}
|
|
|
|
$response = $this->onPostCheckACL($action, $request, $entity);
|
|
|
|
if ($response instanceof Response) {
|
|
return $response;
|
|
}
|
|
|
|
$form = $this->createFormFor($action, $entity, $formClass, $formOptions);
|
|
|
|
$form->handleRequest($request);
|
|
|
|
if ($form->isSubmitted() && $form->isValid()) {
|
|
$this->onFormValid($action, $entity, $form, $request);
|
|
$em = $this->getManagerRegistry()->getManager();
|
|
|
|
$this->onPreFlush($action, $entity, $form, $request);
|
|
$em->flush();
|
|
$this->onPostFlush($action, $entity, $form, $request);
|
|
|
|
$this->addFlash('success', $this->generateFormSuccessMessage($action, $entity));
|
|
|
|
$result = $this->onBeforeRedirectAfterSubmission($action, $entity, $form, $request);
|
|
|
|
if ($result instanceof Response) {
|
|
return $result;
|
|
}
|
|
|
|
return $this->redirectToRoute('chill_crud_'.$this->getCrudName().'_index');
|
|
}
|
|
|
|
if ($form->isSubmitted()) {
|
|
$this->addFlash('error', $this->generateFormErrorMessage($action, $form));
|
|
}
|
|
|
|
$defaultTemplateParameters = [
|
|
'form' => $form->createView(),
|
|
'entity' => $entity,
|
|
'crud_name' => $this->getCrudName(),
|
|
];
|
|
|
|
return $this->render(
|
|
$this->getTemplateFor($action, $entity, $request),
|
|
$this->generateTemplateParameter($action, $entity, $request, $defaultTemplateParameters)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Generate a message which explains an error about the form.
|
|
*
|
|
* Used in form actions
|
|
*/
|
|
protected function generateFormErrorMessage(string $action, FormInterface $form): string
|
|
{
|
|
$msg = 'This form contains errors';
|
|
|
|
return $this->getTranslator()->trans($msg);
|
|
}
|
|
|
|
/**
|
|
* Generate a success message when a form could be flushed successfully.
|
|
*
|
|
* @param string $action
|
|
*/
|
|
protected function generateFormSuccessMessage($action, mixed $entity): string
|
|
{
|
|
$msg = match ($action) {
|
|
'edit' => 'crud.edit.success',
|
|
'new' => 'crud.new.success',
|
|
'delete' => 'crud.delete.success',
|
|
default => 'crud.default.success',
|
|
};
|
|
|
|
return $this->getTranslator()->trans($msg);
|
|
}
|
|
|
|
/**
|
|
* Customize template parameters.
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function generateTemplateParameter(
|
|
string $action,
|
|
mixed $entity,
|
|
Request $request,
|
|
array $defaultTemplateParameters = []
|
|
) {
|
|
return $defaultTemplateParameters;
|
|
}
|
|
|
|
/**
|
|
* Include services.
|
|
*/
|
|
protected function getActionConfig(string $action)
|
|
{
|
|
return $this->crudConfig['actions'][$action];
|
|
}
|
|
|
|
protected function getAuthorizationHelper(): AuthorizationHelper
|
|
{
|
|
return $this->container->get(AuthorizationHelper::class);
|
|
}
|
|
|
|
/**
|
|
* @return string the crud name
|
|
*/
|
|
protected function getCrudName(): string
|
|
{
|
|
return $this->crudConfig['name'];
|
|
}
|
|
|
|
protected function getCrudResolver(): Resolver
|
|
{
|
|
return $this->get(Resolver::class);
|
|
}
|
|
|
|
protected function getDefaultDeleteFormClass($action)
|
|
{
|
|
return CRUDDeleteEntityForm::class;
|
|
}
|
|
|
|
/**
|
|
* get the instance of the entity with the given id.
|
|
*
|
|
* @param string $id
|
|
*/
|
|
protected function getEntity(mixed $action, $id, Request $request): ?object
|
|
{
|
|
return $this->getManagerRegistry()
|
|
->getRepository($this->getEntityClass())
|
|
->find($id);
|
|
}
|
|
|
|
/**
|
|
* @return string the complete fqdn of the class
|
|
*/
|
|
protected function getEntityClass(): string
|
|
{
|
|
return $this->crudConfig['class'];
|
|
}
|
|
|
|
protected function getEventDispatcher(): EventDispatcherInterface
|
|
{
|
|
return $this->get(EventDispatcherInterface::class);
|
|
}
|
|
|
|
protected function getFilterOrderHelperFactory(): FilterOrderHelperFactoryInterface
|
|
{
|
|
return $this->get(FilterOrderHelperFactoryInterface::class);
|
|
}
|
|
|
|
/**
|
|
* get the default form class from config.
|
|
*
|
|
* @param string $action
|
|
*
|
|
* @return string the FQDN of the form class
|
|
*/
|
|
protected function getFormClassFor($action)
|
|
{
|
|
if ('delete' === $action) {
|
|
return $this->crudConfig[$action]['form_class']
|
|
?? $this->getDefaultDeleteFormClass($action);
|
|
}
|
|
|
|
return $this->crudConfig[$action]['form_class']
|
|
?? $this->crudConfig['form_class'];
|
|
}
|
|
|
|
/**
|
|
* @todo (check how to do this with dependency injection and make changes...)
|
|
*/
|
|
protected function getPaginatorFactory(): PaginatorFactory
|
|
{
|
|
return $this->container->get('chill_main.paginator_factory');
|
|
}
|
|
|
|
protected function getManagerRegistry(): ManagerRegistry
|
|
{
|
|
return $this->container->get(ManagerRegistry::class);
|
|
}
|
|
|
|
/**
|
|
* Get the result of the query.
|
|
*/
|
|
protected function getQueryResult(
|
|
string $action,
|
|
Request $request,
|
|
int $totalItems,
|
|
PaginatorInterface $paginator,
|
|
?FilterOrderHelper $filterOrder = null
|
|
) {
|
|
$query = $this->queryEntities($action, $request, $paginator, $filterOrder);
|
|
|
|
return $query->getQuery()->getResult();
|
|
}
|
|
|
|
/**
|
|
* @return \Chill\MainBundle\Entity\Center[]
|
|
*/
|
|
protected function getReachableCenters(string $role, ?Scope $scope = null)
|
|
{
|
|
return $this->getAuthorizationHelper()
|
|
->getReachableCenters($this->getUser(), $role, $scope);
|
|
}
|
|
|
|
/**
|
|
* get the role given from the config.
|
|
*
|
|
* @param string $action
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function getRoleFor($action)
|
|
{
|
|
if (\array_key_exists('role', $this->getActionConfig($action))) {
|
|
return $this->getActionConfig($action)['role'];
|
|
}
|
|
|
|
return $this->buildDefaultRole($action);
|
|
}
|
|
|
|
/**
|
|
* Get the template for the current crud.
|
|
*
|
|
* This template may be defined in configuration. If any template are
|
|
* defined, return the default template for the actions new, edit, index,
|
|
* and view.
|
|
*
|
|
* @param string $action
|
|
*
|
|
* @return string the path to the template
|
|
*
|
|
* @throws \LogicException if no template are available
|
|
*/
|
|
protected function getTemplateFor($action, mixed $entity, Request $request)
|
|
{
|
|
if ($this->hasCustomTemplate($action, $entity, $request)) {
|
|
return $this->getActionConfig($action)['template'];
|
|
}
|
|
|
|
return match ($action) {
|
|
'new' => '@ChillMain/CRUD/new.html.twig',
|
|
'edit' => '@ChillMain/CRUD/edit.html.twig',
|
|
'index' => '@ChillMain/CRUD/index.html.twig',
|
|
'view' => '@ChillMain/CRUD/view.html.twig',
|
|
'delete' => '@ChillMain/CRUD/delete.html.twig',
|
|
default => throw new \LogicException("the view for action {$action} is not ".'defined. You should override '.__METHOD__.' to add this action'),
|
|
};
|
|
}
|
|
|
|
protected function getTranslator(): TranslatorInterface
|
|
{
|
|
return $this->container->get('translator');
|
|
}
|
|
|
|
protected function hasCustomTemplate($action, $entity, Request $request): bool
|
|
{
|
|
return !empty($this->getActionConfig($action)['template']);
|
|
}
|
|
|
|
/**
|
|
* Build an index page.
|
|
*
|
|
* Some steps may be overriden during this process of rendering.
|
|
*
|
|
* This method:
|
|
*
|
|
* 1. Launch `onPreIndex`
|
|
* 2. check acl. If it does return a response instance, return it
|
|
* 3. launch `onPostCheckACL`. If it does return a response instance, return it
|
|
* 4. count the items, using `countEntities`
|
|
* 5. build a paginator element from the the number of entities ;
|
|
* 6. Launch `onPreIndexQuery`. If it does return a response instance, return it
|
|
* 7. fetch the results, using `getQueryResult`
|
|
*
|
|
* Internally, this build a query, using `queryEntities`
|
|
*
|
|
* 8. Launch `onPostIndexFetchQuery`. If it does return a response instance, return it
|
|
* 9. create default parameters:
|
|
*
|
|
* The default parameters are:
|
|
*
|
|
* * entities: the list en entities ;
|
|
* * crud_name: the name of the crud ;
|
|
* * paginator: a paginator element ;
|
|
* 10. Launch rendering, the parameter is fetch using `getTemplateFor`
|
|
* The parameters may be personnalized using `generateTemplateParameter`.
|
|
*
|
|
* @param string $action
|
|
*/
|
|
protected function indexEntityAction($action, Request $request)
|
|
{
|
|
$this->onPreIndex($action, $request);
|
|
|
|
$response = $this->checkACL($action, null);
|
|
|
|
if ($response instanceof Response) {
|
|
return $response;
|
|
}
|
|
|
|
$entity = '';
|
|
|
|
$response = $this->onPostCheckACL($action, $request, $entity);
|
|
|
|
if ($response instanceof Response) {
|
|
return $response;
|
|
}
|
|
|
|
$filterOrder = $this->buildFilterOrderHelper($action, $request);
|
|
$totalItems = $this->countEntities($action, $request, $filterOrder);
|
|
$paginator = $this->getPaginatorFactory()->create($totalItems);
|
|
|
|
$response = $this->onPreIndexBuildQuery(
|
|
$action,
|
|
$request,
|
|
$totalItems,
|
|
$paginator
|
|
);
|
|
|
|
if ($response instanceof Response) {
|
|
return $response;
|
|
}
|
|
|
|
$entities = $this->getQueryResult($action, $request, $totalItems, $paginator, $filterOrder);
|
|
|
|
$response = $this->onPostIndexFetchQuery(
|
|
$action,
|
|
$request,
|
|
$totalItems,
|
|
$paginator,
|
|
$entities
|
|
);
|
|
|
|
if ($response instanceof Response) {
|
|
return $response;
|
|
}
|
|
|
|
$defaultTemplateParameters = [
|
|
'entities' => $entities,
|
|
'crud_name' => $this->getCrudName(),
|
|
'paginator' => $paginator,
|
|
'filter_order' => $filterOrder,
|
|
];
|
|
|
|
return $this->render(
|
|
$this->getTemplateFor($action, $entities, $request),
|
|
$this->generateTemplateParameter($action, $entities, $request, $defaultTemplateParameters)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Return a redirect response depending on the value of submit button.
|
|
*
|
|
* The handled values are :
|
|
*
|
|
* * save-and-close: return to index of current crud ;
|
|
* * save-and-new: return to new page of current crud ;
|
|
* * save-and-view: return to view page of current crud ;
|
|
*/
|
|
protected function onBeforeRedirectAfterSubmission(string $action, mixed $entity, FormInterface $form, Request $request): ?Response
|
|
{
|
|
$next = $request->request->get('submit', 'save-and-close');
|
|
|
|
return match ($next) {
|
|
'save-and-close' => $this->redirectToRoute('chill_crud_'.$this->getCrudName().'_index'),
|
|
'save-and-new' => $this->redirectToRoute('chill_crud_'.$this->getCrudName().'_new', $request->query->all()),
|
|
default => $this->redirectToRoute('chill_crud_'.$this->getCrudName().'_view', [
|
|
'id' => $entity->getId(),
|
|
]),
|
|
};
|
|
}
|
|
|
|
protected function onFormValid(string $action, object $entity, FormInterface $form, Request $request) {}
|
|
|
|
protected function onPostCheckACL($action, Request $request, $entity): ?Response
|
|
{
|
|
return null;
|
|
}
|
|
|
|
protected function onPostFetchEntity($action, Request $request, $entity): ?Response
|
|
{
|
|
return null;
|
|
}
|
|
|
|
protected function onPostFlush(string $action, $entity, FormInterface $form, Request $request) {}
|
|
|
|
/**
|
|
* method used by indexAction.
|
|
*/
|
|
protected function onPostIndexBuildQuery(string $action, Request $request, int $totalItems, PaginatorInterface $paginator, mixed $query) {}
|
|
|
|
/**
|
|
* method used by indexAction.
|
|
*/
|
|
protected function onPostIndexFetchQuery(string $action, Request $request, int $totalItems, PaginatorInterface $paginator, mixed $entities) {}
|
|
|
|
protected function onPostPersist(string $action, $entity, FormInterface $form, Request $request) {}
|
|
|
|
protected function onPostRemove(string $action, $entity, FormInterface $form, Request $request) {}
|
|
|
|
protected function onPreDelete(string $action, Request $request) {}
|
|
|
|
protected function onPreFlush(string $action, $entity, FormInterface $form, Request $request) {}
|
|
|
|
protected function onPreIndex(string $action, Request $request) {}
|
|
|
|
/**
|
|
* method used by indexAction.
|
|
*/
|
|
protected function onPreIndexBuildQuery(string $action, Request $request, int $totalItems, PaginatorInterface $paginator) {}
|
|
|
|
protected function onPrePersist(string $action, $entity, FormInterface $form, Request $request) {}
|
|
|
|
protected function onPreRemove(string $action, $entity, FormInterface $form, Request $request) {}
|
|
|
|
/**
|
|
* Add ordering fields in the query build by self::queryEntities.
|
|
*
|
|
* @param mixed|QueryBuilder $query by default, an instance of QueryBuilder
|
|
*
|
|
* @return mixed|QueryBuilder
|
|
*/
|
|
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator)
|
|
{
|
|
return $query;
|
|
}
|
|
|
|
/**
|
|
* Query the entity.
|
|
*
|
|
* By default, get all entities.
|
|
*
|
|
* The method `orderEntity` is called internally to order entities.
|
|
*
|
|
* It returns, by default, a query builder.
|
|
*
|
|
* @return type
|
|
*/
|
|
protected function queryEntities(string $action, Request $request, PaginatorInterface $paginator, ?FilterOrderHelper $filterOrder = null)
|
|
{
|
|
$query = $this->buildQueryEntities($action, $request)
|
|
->setFirstResult($paginator->getCurrentPage()->getFirstItemNumber())
|
|
->setMaxResults($paginator->getItemsPerPage());
|
|
|
|
// allow to order queries and return the new query
|
|
return $this->orderQuery($action, $query, $request, $paginator);
|
|
}
|
|
|
|
protected function removeEntity(string $action, $entity, FormInterface $form, Request $request)
|
|
{
|
|
$this->getManagerRegistry()
|
|
->getManager()
|
|
->remove($entity);
|
|
}
|
|
|
|
/**
|
|
* The view action.
|
|
*
|
|
* Some steps may be overriden during this process of rendering:
|
|
*
|
|
* This method:
|
|
*
|
|
* 1. fetch the entity, using `getEntity`
|
|
* 2. launch `onPostFetchEntity`. If postfetch is an instance of Response,
|
|
* this response is returned.
|
|
* 2. throw an HttpNotFoundException if entity is null
|
|
* 3. check ACL using `checkACL` ;
|
|
* 4. launch `onPostCheckACL`. If the result is an instance of Response,
|
|
* this response is returned ;
|
|
* 5. generate default template parameters:
|
|
*
|
|
* * `entity`: the fetched entity ;
|
|
* * `crud_name`: the crud name
|
|
* 6. Launch rendering, the parameter is fetch using `getTemplateFor`
|
|
* The parameters may be personnalized using `generateTemplateParameter`.
|
|
*/
|
|
protected function viewAction(string $action, Request $request, mixed $id, mixed $_format = 'html'): Response
|
|
{
|
|
$entity = $this->getEntity($action, $id, $request);
|
|
|
|
$postFetch = $this->onPostFetchEntity($action, $request, $entity);
|
|
|
|
if ($postFetch instanceof Response) {
|
|
return $postFetch;
|
|
}
|
|
|
|
if (null === $entity) {
|
|
throw $this->createNotFoundException(sprintf('The %s with id %s is not found', $this->getCrudName(), $id));
|
|
}
|
|
|
|
$response = $this->checkACL($action, $entity);
|
|
|
|
if ($response instanceof Response) {
|
|
return $response;
|
|
}
|
|
|
|
$response = $this->onPostCheckACL($action, $request, $entity);
|
|
|
|
if ($response instanceof Response) {
|
|
return $response;
|
|
}
|
|
|
|
if ('html' === $_format) {
|
|
$defaultTemplateParameters = [
|
|
'entity' => $entity,
|
|
'crud_name' => $this->getCrudName(),
|
|
];
|
|
|
|
return $this->render(
|
|
$this->getTemplateFor($action, $entity, $request),
|
|
$this->generateTemplateParameter($action, $entity, $request, $defaultTemplateParameters)
|
|
);
|
|
}
|
|
|
|
if ('json' === $_format) {
|
|
$context = $this->getContextForSerialization($action, $request, $entity, $_format);
|
|
|
|
return $this->json($entity, Response::HTTP_OK, [], $context);
|
|
}
|
|
|
|
throw new BadRequestHttpException('This format is not implemented');
|
|
}
|
|
}
|