vendor/easycorp/easyadmin-bundle/src/Controller/AdminControllerTrait.php line 63

Open in your IDE?
  1. <?php
  2. namespace EasyCorp\Bundle\EasyAdminBundle\Controller;
  3. use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
  4. use Doctrine\ORM\EntityManager;
  5. use Doctrine\ORM\QueryBuilder;
  6. use EasyCorp\Bundle\EasyAdminBundle\Event\EasyAdminEvents;
  7. use EasyCorp\Bundle\EasyAdminBundle\Exception\EntityRemoveException;
  8. use EasyCorp\Bundle\EasyAdminBundle\Exception\ForbiddenActionException;
  9. use EasyCorp\Bundle\EasyAdminBundle\Exception\NoEntitiesConfiguredException;
  10. use EasyCorp\Bundle\EasyAdminBundle\Exception\NoPermissionException;
  11. use EasyCorp\Bundle\EasyAdminBundle\Exception\UndefinedEntityException;
  12. use EasyCorp\Bundle\EasyAdminBundle\Form\Filter\FilterRegistry;
  13. use EasyCorp\Bundle\EasyAdminBundle\Form\Type\EasyAdminBatchFormType;
  14. use EasyCorp\Bundle\EasyAdminBundle\Form\Type\EasyAdminFiltersFormType;
  15. use EasyCorp\Bundle\EasyAdminBundle\Form\Type\EasyAdminFormType;
  16. use EasyCorp\Bundle\EasyAdminBundle\Form\Type\FileUploadType;
  17. use EasyCorp\Bundle\EasyAdminBundle\Form\Type\Model\FileUploadState;
  18. use Pagerfanta\Pagerfanta;
  19. use Symfony\Component\EventDispatcher\GenericEvent;
  20. use Symfony\Component\Form\Extension\Core\Type\HiddenType;
  21. use Symfony\Component\Form\Extension\Core\Type\SubmitType;
  22. use Symfony\Component\Form\Form;
  23. use Symfony\Component\Form\FormBuilder;
  24. use Symfony\Component\Form\FormBuilderInterface;
  25. use Symfony\Component\Form\FormInterface;
  26. use Symfony\Component\HttpFoundation\JsonResponse;
  27. use Symfony\Component\HttpFoundation\RedirectResponse;
  28. use Symfony\Component\HttpFoundation\Request;
  29. use Symfony\Component\HttpFoundation\Response;
  30. use Symfony\Component\HttpKernel\Kernel;
  31. use Symfony\Component\PropertyAccess\PropertyAccess;
  32. use Symfony\Component\Routing\Annotation\Route;
  33. /**
  34.  * Common features needed in admin controllers.
  35.  *
  36.  * @internal
  37.  *
  38.  * @author Javier Eguiluz <javier.eguiluz@gmail.com>
  39.  */
  40. trait AdminControllerTrait
  41. {
  42.     /** @var array The full configuration of the entire backend */
  43.     protected $config;
  44.     /** @var array The full configuration of the current entity */
  45.     protected $entity = [];
  46.     /** @var Request The instance of the current Symfony request */
  47.     protected $request;
  48.     /** @var EntityManager|null The Doctrine entity manager for the current entity */
  49.     protected $em;
  50.     /**
  51.      * @Route("/", name="easyadmin")
  52.      *
  53.      * @param Request $request
  54.      *
  55.      * @return RedirectResponse|Response
  56.      *
  57.      * @throws ForbiddenActionException
  58.      */
  59.     public function indexAction(Request $request)
  60.     {
  61.         $this->initialize($request);
  62.         if (null === $request->query->get('entity')) {
  63.             return $this->redirectToBackendHomepage();
  64.         }
  65.         $action $request->query->get('action''list');
  66.         if (!$this->isActionAllowed($action)) {
  67.             throw new ForbiddenActionException(['action' => $action'entity_name' => $this->entity['name']]);
  68.         }
  69.         if (\in_array($action, ['show''edit''new'])) {
  70.             $id $this->request->query->get('id');
  71.             $entity $this->request->attributes->get('easyadmin')['item'];
  72.             $requiredPermission $this->entity[$action]['item_permission'];
  73.             $userHasPermission $this->get('easyadmin.security.authorization_checker')->isGranted($requiredPermission$entity);
  74.             if (false === $userHasPermission) {
  75.                 throw new NoPermissionException(['action' => $action'entity_name' => $this->entity['name'], 'entity_id' => $id]);
  76.             }
  77.         }
  78.         return $this->executeDynamicMethod($action.'<EntityName>Action');
  79.     }
  80.     /**
  81.      * Utility method which initializes the configuration of the entity on which
  82.      * the user is performing the action.
  83.      *
  84.      * @param Request $request
  85.      *
  86.      * @throws NoEntitiesConfiguredException
  87.      * @throws UndefinedEntityException
  88.      */
  89.     protected function initialize(Request $request)
  90.     {
  91.         $this->dispatch(EasyAdminEvents::PRE_INITIALIZE);
  92.         $this->config $this->get('easyadmin.config.manager')->getBackendConfig();
  93.         if (=== \count($this->config['entities'])) {
  94.             throw new NoEntitiesConfiguredException();
  95.         }
  96.         // this condition happens when accessing the backend homepage and before
  97.         // redirecting to the default page set as the homepage
  98.         if (null === $entityName $request->query->get('entity')) {
  99.             return;
  100.         }
  101.         if (!\array_key_exists($entityName$this->config['entities'])) {
  102.             throw new UndefinedEntityException(['entity_name' => $entityName]);
  103.         }
  104.         $this->entity $this->get('easyadmin.config.manager')->getEntityConfig($entityName);
  105.         $action $request->query->get('action''list');
  106.         if (!$request->query->has('sortField')) {
  107.             $sortField $this->entity[$action]['sort']['field'] ?? $this->entity['primary_key_field_name'];
  108.             $request->query->set('sortField'$sortField);
  109.         }
  110.         if (!$request->query->has('sortDirection')) {
  111.             $sortDirection $this->entity[$action]['sort']['direction'] ?? 'DESC';
  112.             $request->query->set('sortDirection'$sortDirection);
  113.         }
  114.         $this->em $this->getDoctrine()->getManagerForClass($this->entity['class']);
  115.         $this->request $request;
  116.         $this->dispatch(EasyAdminEvents::POST_INITIALIZE);
  117.     }
  118.     protected function dispatch($eventName, array $arguments = [])
  119.     {
  120.         $arguments array_replace([
  121.             'config' => $this->config,
  122.             'em' => $this->em,
  123.             'entity' => $this->entity,
  124.             'request' => $this->request,
  125.         ], $arguments);
  126.         $subject $arguments['paginator'] ?? $arguments['entity'];
  127.         $event = new GenericEvent($subject$arguments);
  128.         if (Kernel::VERSION_ID >= 40300) {
  129.             $this->get('event_dispatcher')->dispatch($event$eventName);
  130.         } else {
  131.             $this->get('event_dispatcher')->dispatch($eventName$event);
  132.         }
  133.     }
  134.     /**
  135.      * The method that returns the values displayed by an autocomplete field
  136.      * based on the user's input.
  137.      *
  138.      * @return JsonResponse
  139.      */
  140.     protected function autocompleteAction()
  141.     {
  142.         $results $this->get('easyadmin.autocomplete')->find(
  143.             $this->request->query->get('entity'),
  144.             $this->request->query->get('query'),
  145.             $this->request->query->get('page'1)
  146.         );
  147.         return new JsonResponse($results);
  148.     }
  149.     /**
  150.      * The method that is executed when the user performs a 'list' action on an entity.
  151.      *
  152.      * @return Response
  153.      */
  154.     protected function listAction()
  155.     {
  156.         $this->dispatch(EasyAdminEvents::PRE_LIST);
  157.         $fields $this->entity['list']['fields'];
  158.         $paginator $this->findAll($this->entity['class'], $this->request->query->get('page'1), $this->entity['list']['max_results'], $this->request->query->get('sortField'), $this->request->query->get('sortDirection'), $this->entity['list']['dql_filter']);
  159.         $this->dispatch(EasyAdminEvents::POST_LIST, ['paginator' => $paginator]);
  160.         $parameters = [
  161.             'paginator' => $paginator,
  162.             'fields' => $fields,
  163.             'batch_form' => $this->createBatchForm($this->entity['name'])->createView(),
  164.             'delete_form_template' => $this->createDeleteForm($this->entity['name'], '__id__')->createView(),
  165.         ];
  166.         return $this->executeDynamicMethod('render<EntityName>Template', ['list'$this->entity['templates']['list'], $parameters]);
  167.     }
  168.     /**
  169.      * The method that is executed when the user performs a 'edit' action on an entity.
  170.      *
  171.      * @return Response|RedirectResponse
  172.      *
  173.      * @throws \RuntimeException
  174.      */
  175.     protected function editAction()
  176.     {
  177.         $this->dispatch(EasyAdminEvents::PRE_EDIT);
  178.         $id $this->request->query->get('id');
  179.         $easyadmin $this->request->attributes->get('easyadmin');
  180.         $entity $easyadmin['item'];
  181.         if ($this->request->isXmlHttpRequest() && $property $this->request->query->get('property')) {
  182.             $newValue 'true' === mb_strtolower($this->request->query->get('newValue'));
  183.             $fieldsMetadata $this->entity['list']['fields'];
  184.             if (!isset($fieldsMetadata[$property]) || 'toggle' !== $fieldsMetadata[$property]['dataType']) {
  185.                 throw new \RuntimeException(sprintf('The type of the "%s" property is not "toggle".'$property));
  186.             }
  187.             $this->updateEntityProperty($entity$property$newValue);
  188.             // cast to integer instead of string to avoid sending empty responses for 'false'
  189.             return new Response((int) $newValue);
  190.         }
  191.         $fields $this->entity['edit']['fields'];
  192.         $editForm $this->executeDynamicMethod('create<EntityName>EditForm', [$entity$fields]);
  193.         $deleteForm $this->createDeleteForm($this->entity['name'], $id);
  194.         $editForm->handleRequest($this->request);
  195.         if ($editForm->isSubmitted() && $editForm->isValid()) {
  196.             $this->processUploadedFiles($editForm);
  197.             $this->dispatch(EasyAdminEvents::PRE_UPDATE, ['entity' => $entity]);
  198.             $this->executeDynamicMethod('update<EntityName>Entity', [$entity$editForm]);
  199.             $this->dispatch(EasyAdminEvents::POST_UPDATE, ['entity' => $entity]);
  200.             return $this->redirectToReferrer();
  201.         }
  202.         $this->dispatch(EasyAdminEvents::POST_EDIT);
  203.         $parameters = [
  204.             'form' => $editForm->createView(),
  205.             'entity_fields' => $fields,
  206.             'entity' => $entity,
  207.             'delete_form' => $deleteForm->createView(),
  208.         ];
  209.         return $this->executeDynamicMethod('render<EntityName>Template', ['edit'$this->entity['templates']['edit'], $parameters]);
  210.     }
  211.     /**
  212.      * The method that is executed when the user performs a 'show' action on an entity.
  213.      *
  214.      * @return Response
  215.      */
  216.     protected function showAction()
  217.     {
  218.         $this->dispatch(EasyAdminEvents::PRE_SHOW);
  219.         $id $this->request->query->get('id');
  220.         $easyadmin $this->request->attributes->get('easyadmin');
  221.         $entity $easyadmin['item'];
  222.         $fields $this->entity['show']['fields'];
  223.         $deleteForm $this->createDeleteForm($this->entity['name'], $id);
  224.         $this->dispatch(EasyAdminEvents::POST_SHOW, [
  225.             'deleteForm' => $deleteForm,
  226.             'fields' => $fields,
  227.             'entity' => $entity,
  228.         ]);
  229.         $parameters = [
  230.             'entity' => $entity,
  231.             'fields' => $fields,
  232.             'delete_form' => $deleteForm->createView(),
  233.         ];
  234.         return $this->executeDynamicMethod('render<EntityName>Template', ['show'$this->entity['templates']['show'], $parameters]);
  235.     }
  236.     /**
  237.      * The method that is executed when the user performs a 'new' action on an entity.
  238.      *
  239.      * @return Response|RedirectResponse
  240.      */
  241.     protected function newAction()
  242.     {
  243.         $this->dispatch(EasyAdminEvents::PRE_NEW);
  244.         $entity $this->executeDynamicMethod('createNew<EntityName>Entity');
  245.         $easyadmin $this->request->attributes->get('easyadmin');
  246.         $easyadmin['item'] = $entity;
  247.         $this->request->attributes->set('easyadmin'$easyadmin);
  248.         $fields $this->entity['new']['fields'];
  249.         $newForm $this->executeDynamicMethod('create<EntityName>NewForm', [$entity$fields]);
  250.         $newForm->handleRequest($this->request);
  251.         if ($newForm->isSubmitted() && $newForm->isValid()) {
  252.             $this->processUploadedFiles($newForm);
  253.             $this->dispatch(EasyAdminEvents::PRE_PERSIST, ['entity' => $entity]);
  254.             $this->executeDynamicMethod('persist<EntityName>Entity', [$entity$newForm]);
  255.             $this->dispatch(EasyAdminEvents::POST_PERSIST, ['entity' => $entity]);
  256.             return $this->redirectToReferrer();
  257.         }
  258.         $this->dispatch(EasyAdminEvents::POST_NEW, [
  259.             'entity_fields' => $fields,
  260.             'form' => $newForm,
  261.             'entity' => $entity,
  262.         ]);
  263.         $parameters = [
  264.             'form' => $newForm->createView(),
  265.             'entity_fields' => $fields,
  266.             'entity' => $entity,
  267.         ];
  268.         return $this->executeDynamicMethod('render<EntityName>Template', ['new'$this->entity['templates']['new'], $parameters]);
  269.     }
  270.     /**
  271.      * The method that is executed when the user performs a 'delete' action to
  272.      * remove any entity.
  273.      *
  274.      * @return RedirectResponse
  275.      *
  276.      * @throws EntityRemoveException
  277.      */
  278.     protected function deleteAction()
  279.     {
  280.         $this->dispatch(EasyAdminEvents::PRE_DELETE);
  281.         if (!Request::getHttpMethodParameterOverride() && 'POST' === $this->request->getMethod() && 'DELETE' === $this->request->request->get('_method')) {
  282.             $this->request->setMethod('DELETE');
  283.         }
  284.         if ('DELETE' !== $this->request->getMethod()) {
  285.             return $this->redirect($this->generateUrl('easyadmin', ['action' => 'list''entity' => $this->entity['name']]));
  286.         }
  287.         $id $this->request->query->get('id');
  288.         $form $this->createDeleteForm($this->entity['name'], $id);
  289.         $form->handleRequest($this->request);
  290.         if ($form->isSubmitted() && $form->isValid()) {
  291.             $easyadmin $this->request->attributes->get('easyadmin');
  292.             $entity $easyadmin['item'];
  293.             $this->dispatch(EasyAdminEvents::PRE_REMOVE, ['entity' => $entity]);
  294.             try {
  295.                 $this->executeDynamicMethod('remove<EntityName>Entity', [$entity$form]);
  296.             } catch (ForeignKeyConstraintViolationException $e) {
  297.                 throw new EntityRemoveException(['entity_name' => $this->entity['name'], 'message' => $e->getMessage()]);
  298.             }
  299.             $this->dispatch(EasyAdminEvents::POST_REMOVE, ['entity' => $entity]);
  300.         }
  301.         $this->dispatch(EasyAdminEvents::POST_DELETE);
  302.         return $this->redirectToReferrer();
  303.     }
  304.     /**
  305.      * The method that is executed when the user performs a query on an entity.
  306.      *
  307.      * @return Response
  308.      */
  309.     protected function searchAction()
  310.     {
  311.         $this->dispatch(EasyAdminEvents::PRE_SEARCH);
  312.         $query trim($this->request->query->get('query'));
  313.         // if the search query is empty, redirect to the 'list' action
  314.         if ('' === $query) {
  315.             $queryParameters array_replace($this->request->query->all(), ['action' => 'list']);
  316.             unset($queryParameters['query']);
  317.             return $this->redirect($this->get('router')->generate('easyadmin'$queryParameters));
  318.         }
  319.         $searchableFields $this->entity['search']['fields'];
  320.         $paginator $this->findBy(
  321.             $this->entity['class'],
  322.             $query,
  323.             $searchableFields,
  324.             $this->request->query->get('page'1),
  325.             $this->entity['list']['max_results'],
  326.             $this->request->query->get('sortField'),
  327.             $this->request->query->get('sortDirection'),
  328.             $this->entity['search']['dql_filter']
  329.         );
  330.         $fields $this->entity['list']['fields'];
  331.         $this->dispatch(EasyAdminEvents::POST_SEARCH, [
  332.             'fields' => $fields,
  333.             'paginator' => $paginator,
  334.         ]);
  335.         $parameters = [
  336.             'paginator' => $paginator,
  337.             'fields' => $fields,
  338.             'batch_form' => $this->createBatchForm($this->entity['name'])->createView(),
  339.             'delete_form_template' => $this->createDeleteForm($this->entity['name'], '__id__')->createView(),
  340.         ];
  341.         return $this->executeDynamicMethod('render<EntityName>Template', ['search'$this->entity['templates']['list'], $parameters]);
  342.     }
  343.     /**
  344.      * The method that is executed when the user performs a 'batch' action to any entity.
  345.      */
  346.     protected function batchAction(): Response
  347.     {
  348.         $batchForm $this->createBatchForm($this->entity['name']);
  349.         $batchForm->handleRequest($this->request);
  350.         if ($batchForm->isSubmitted() && $batchForm->isValid()) {
  351.             $actionName $batchForm->get('name')->getData();
  352.             $actionIds $batchForm->get('ids')->getData();
  353.             $batchActionResult $this->executeDynamicMethod($actionName.'<EntityName>BatchAction', [$actionIds$batchForm]);
  354.             if ($batchActionResult instanceof Response) {
  355.                 return $batchActionResult;
  356.             }
  357.         }
  358.         return $this->redirectToReferrer();
  359.     }
  360.     protected function createBatchForm(string $entityName): FormInterface
  361.     {
  362.         return $this->get('form.factory')->createNamed('batch_form'EasyAdminBatchFormType::class, null, [
  363.             'action' => $this->generateUrl('easyadmin', ['action' => 'batch''entity' => $entityName]),
  364.             'entity' => $entityName,
  365.         ]);
  366.     }
  367.     protected function deleteBatchAction(array $ids): void
  368.     {
  369.         $class $this->entity['class'];
  370.         $primaryKey $this->entity['primary_key_field_name'];
  371.         $entities $this->em->getRepository($class)
  372.             ->findBy([$primaryKey => $ids]);
  373.         foreach ($entities as $entity) {
  374.             $this->em->remove($entity);
  375.         }
  376.         $this->em->flush();
  377.     }
  378.     /**
  379.      * The method that is executed when the user open the filters modal on an entity.
  380.      *
  381.      * @return Response
  382.      */
  383.     protected function filtersAction()
  384.     {
  385.         $filtersForm $this->createFiltersForm($this->entity['name']);
  386.         $filtersForm->handleRequest($this->request);
  387.         $easyadmin $this->request->attributes->get('easyadmin');
  388.         $easyadmin['filters']['applied'] = array_keys($this->request->get('filters', []));
  389.         $this->request->attributes->set('easyadmin'$easyadmin);
  390.         $parameters = [
  391.             'filters_form' => $filtersForm->createView(),
  392.             'referer_action' => $this->request->get('referer_action''list'),
  393.         ];
  394.         return $this->executeDynamicMethod('render<EntityName>Template', ['filters'$this->entity['templates']['filters'], $parameters]);
  395.     }
  396.     /**
  397.      * The method that apply all configured filter to the list QueryBuilder.
  398.      */
  399.     protected function filterQueryBuilder(QueryBuilder $queryBuilder): void
  400.     {
  401.         if (!$requestData $this->request->get('filters')) {
  402.             // Don't create the filters form if there is no filter applied
  403.             return;
  404.         }
  405.         /** @var Form $filtersForm */
  406.         $filtersForm $this->createFiltersForm($this->entity['name']);
  407.         $filtersForm->handleRequest($this->request);
  408.         if (!$filtersForm->isSubmitted()) {
  409.             return;
  410.         }
  411.         /** @var FilterRegistry $filterRegistry */
  412.         $filterRegistry $this->get('easyadmin.filter.registry');
  413.         $appliedFilters = [];
  414.         foreach ($filtersForm as $filterForm) {
  415.             $name $filterForm->getName();
  416.             if (!isset($requestData[$name])) {
  417.                 // this filter is not applied
  418.                 continue;
  419.             }
  420.             // if the form filter is not valid then
  421.             // we should not apply the filter
  422.             if (!$filterForm->isValid()) {
  423.                 continue;
  424.             }
  425.             // resolve the filter type related to this form field
  426.             $filterType $filterRegistry->resolveType($filterForm);
  427.             $metadata $this->entity['list']['filters'][$name] ?? [];
  428.             if (false !== $filterType->filter($queryBuilder$filterForm$metadata)) {
  429.                 $appliedFilters[] = $name;
  430.             }
  431.         }
  432.         $easyadmin $this->request->attributes->get('easyadmin');
  433.         $easyadmin['filters']['applied'] = $appliedFilters;
  434.         $this->request->attributes->set('easyadmin'$easyadmin);
  435.     }
  436.     protected function createFiltersForm(string $entityName): FormInterface
  437.     {
  438.         return $this->get('form.factory')->createNamed('filters'EasyAdminFiltersFormType::class, null, [
  439.             'method' => 'GET',
  440.             'entity' => $entityName,
  441.         ]);
  442.     }
  443.     /**
  444.      * Process all uploaded files in the current form if available.
  445.      */
  446.     protected function processUploadedFiles(FormInterface $form): void
  447.     {
  448.         /** @var FormInterface $child */
  449.         foreach ($form as $child) {
  450.             $config $child->getConfig();
  451.             if (!$config->getType()->getInnerType() instanceof FileUploadType) {
  452.                 if ($config->getCompound()) {
  453.                     $this->processUploadedFiles($child);
  454.                 }
  455.                 continue;
  456.             }
  457.             /** @var FileUploadState $state */
  458.             $state $config->getAttribute('state');
  459.             if (!$state->isModified()) {
  460.                 continue;
  461.             }
  462.             $uploadDelete $config->getOption('upload_delete');
  463.             if ($state->hasCurrentFiles() && ($state->isDelete() || (!$state->isAddAllowed() && $state->hasUploadedFiles()))) {
  464.                 foreach ($state->getCurrentFiles() as $file) {
  465.                     $uploadDelete($file);
  466.                 }
  467.                 $state->setCurrentFiles([]);
  468.             }
  469.             $filePaths = (array) $child->getData();
  470.             $uploadDir $config->getOption('upload_dir');
  471.             $uploadNew $config->getOption('upload_new');
  472.             foreach ($state->getUploadedFiles() as $index => $file) {
  473.                 $fileName mb_substr($filePaths[$index], mb_strlen($uploadDir));
  474.                 $uploadNew($file$uploadDir$fileName);
  475.             }
  476.         }
  477.     }
  478.     /**
  479.      * It updates the value of some property of some entity to the new given value.
  480.      *
  481.      * @param mixed  $entity   The instance of the entity to modify
  482.      * @param string $property The name of the property to change
  483.      * @param bool   $value    The new value of the property
  484.      *
  485.      * @throws \RuntimeException
  486.      */
  487.     protected function updateEntityProperty($entity$property$value)
  488.     {
  489.         $entityConfig $this->entity;
  490.         if (!$this->get('easyadmin.property_accessor')->isWritable($entity$property)) {
  491.             throw new \RuntimeException(sprintf('The "%s" property of the "%s" entity is not writable.'$property$entityConfig['name']));
  492.         }
  493.         $this->get('easyadmin.property_accessor')->setValue($entity$property$value);
  494.         $this->dispatch(EasyAdminEvents::PRE_UPDATE, ['entity' => $entity'property' => $property'newValue' => $value]);
  495.         $this->executeDynamicMethod('update<EntityName>Entity', [$entity]);
  496.         $this->dispatch(EasyAdminEvents::POST_UPDATE, ['entity' => $entity'property' => $property'newValue' => $value]);
  497.         $this->dispatch(EasyAdminEvents::POST_EDIT);
  498.     }
  499.     /**
  500.      * Creates a new object of the current managed entity.
  501.      * This method is mostly here for override convenience, because it allows
  502.      * the user to use his own method to customize the entity instantiation.
  503.      *
  504.      * @return object
  505.      */
  506.     protected function createNewEntity()
  507.     {
  508.         $entityFullyQualifiedClassName $this->entity['class'];
  509.         return new $entityFullyQualifiedClassName();
  510.     }
  511.     /**
  512.      * Allows applications to modify the entity associated with the item being
  513.      * created while persisting it.
  514.      *
  515.      * @param object $entity
  516.      */
  517.     protected function persistEntity($entity)
  518.     {
  519.         $this->em->persist($entity);
  520.         $this->em->flush();
  521.     }
  522.     /**
  523.      * Allows applications to modify the entity associated with the item being
  524.      * edited before updating it.
  525.      *
  526.      * @param object $entity
  527.      */
  528.     protected function updateEntity($entity)
  529.     {
  530.         $this->em->persist($entity);
  531.         $this->em->flush();
  532.     }
  533.     /**
  534.      * Allows applications to modify the entity associated with the item being
  535.      * deleted before removing it.
  536.      *
  537.      * @param object $entity
  538.      */
  539.     protected function removeEntity($entity)
  540.     {
  541.         $this->em->remove($entity);
  542.         $this->em->flush();
  543.     }
  544.     /**
  545.      * Performs a database query to get all the records related to the given
  546.      * entity. It supports pagination and field sorting.
  547.      *
  548.      * @param string      $entityClass
  549.      * @param int         $page
  550.      * @param int         $maxPerPage
  551.      * @param string|null $sortField
  552.      * @param string|null $sortDirection
  553.      * @param string|null $dqlFilter
  554.      *
  555.      * @return Pagerfanta The paginated query results
  556.      */
  557.     protected function findAll($entityClass$page 1$maxPerPage 15$sortField null$sortDirection null$dqlFilter null)
  558.     {
  559.         if (null === $sortDirection || !\in_array(strtoupper($sortDirection), ['ASC''DESC'])) {
  560.             $sortDirection 'DESC';
  561.         }
  562.         $queryBuilder $this->executeDynamicMethod('create<EntityName>ListQueryBuilder', [$entityClass$sortDirection$sortField$dqlFilter]);
  563.         $this->filterQueryBuilder($queryBuilder);
  564.         $this->dispatch(EasyAdminEvents::POST_LIST_QUERY_BUILDER, [
  565.             'query_builder' => $queryBuilder,
  566.             'sort_field' => $sortField,
  567.             'sort_direction' => $sortDirection,
  568.         ]);
  569.         return $this->get('easyadmin.paginator')->createOrmPaginator($queryBuilder$page$maxPerPage);
  570.     }
  571.     /**
  572.      * Creates Query Builder instance for all the records.
  573.      *
  574.      * @param string      $entityClass
  575.      * @param string      $sortDirection
  576.      * @param string|null $sortField
  577.      * @param string|null $dqlFilter
  578.      *
  579.      * @return QueryBuilder The Query Builder instance
  580.      */
  581.     protected function createListQueryBuilder($entityClass$sortDirection$sortField null$dqlFilter null)
  582.     {
  583.         return $this->get('easyadmin.query_builder')->createListQueryBuilder($this->entity$sortField$sortDirection$dqlFilter);
  584.     }
  585.     /**
  586.      * Performs a database query based on the search query provided by the user.
  587.      * It supports pagination and field sorting.
  588.      *
  589.      * @param string      $entityClass
  590.      * @param string      $searchQuery
  591.      * @param array       $searchableFields
  592.      * @param int         $page
  593.      * @param int         $maxPerPage
  594.      * @param string|null $sortField
  595.      * @param string|null $sortDirection
  596.      * @param string|null $dqlFilter
  597.      *
  598.      * @return Pagerfanta The paginated query results
  599.      */
  600.     protected function findBy($entityClass$searchQuery, array $searchableFields$page 1$maxPerPage 15$sortField null$sortDirection null$dqlFilter null)
  601.     {
  602.         if (empty($sortDirection) || !\in_array(strtoupper($sortDirection), ['ASC''DESC'])) {
  603.             $sortDirection 'DESC';
  604.         }
  605.         $queryBuilder $this->executeDynamicMethod('create<EntityName>SearchQueryBuilder', [$entityClass$searchQuery$searchableFields$sortField$sortDirection$dqlFilter]);
  606.         $this->filterQueryBuilder($queryBuilder);
  607.         $this->dispatch(EasyAdminEvents::POST_SEARCH_QUERY_BUILDER, [
  608.             'query_builder' => $queryBuilder,
  609.             'search_query' => $searchQuery,
  610.             'searchable_fields' => $searchableFields,
  611.         ]);
  612.         return $this->get('easyadmin.paginator')->createOrmPaginator($queryBuilder$page$maxPerPage);
  613.     }
  614.     /**
  615.      * Creates Query Builder instance for search query.
  616.      *
  617.      * @param string      $entityClass
  618.      * @param string      $searchQuery
  619.      * @param array       $searchableFields
  620.      * @param string|null $sortField
  621.      * @param string|null $sortDirection
  622.      * @param string|null $dqlFilter
  623.      *
  624.      * @return QueryBuilder The Query Builder instance
  625.      */
  626.     protected function createSearchQueryBuilder($entityClass$searchQuery, array $searchableFields$sortField null$sortDirection null$dqlFilter null)
  627.     {
  628.         return $this->get('easyadmin.query_builder')->createSearchQueryBuilder($this->entity$searchQuery$sortField$sortDirection$dqlFilter);
  629.     }
  630.     /**
  631.      * Creates the form used to edit an entity.
  632.      *
  633.      * @param object $entity
  634.      * @param array  $entityProperties
  635.      *
  636.      * @return Form|FormInterface
  637.      */
  638.     protected function createEditForm($entity, array $entityProperties)
  639.     {
  640.         return $this->createEntityForm($entity$entityProperties'edit');
  641.     }
  642.     /**
  643.      * Creates the form used to create an entity.
  644.      *
  645.      * @param object $entity
  646.      * @param array  $entityProperties
  647.      *
  648.      * @return Form|FormInterface
  649.      */
  650.     protected function createNewForm($entity, array $entityProperties)
  651.     {
  652.         return $this->createEntityForm($entity$entityProperties'new');
  653.     }
  654.     /**
  655.      * Creates the form builder of the form used to create or edit the given entity.
  656.      *
  657.      * @param object $entity
  658.      * @param string $view   The name of the view where this form is used ('new' or 'edit')
  659.      *
  660.      * @return FormBuilder
  661.      */
  662.     protected function createEntityFormBuilder($entity$view)
  663.     {
  664.         $formOptions $this->executeDynamicMethod('get<EntityName>EntityFormOptions', [$entity$view]);
  665.         return $this->get('form.factory')->createNamedBuilder(mb_strtolower($this->entity['name']), EasyAdminFormType::class, $entity$formOptions);
  666.     }
  667.     /**
  668.      * Retrieves the list of form options before sending them to the form builder.
  669.      * This allows adding dynamic logic to the default form options.
  670.      *
  671.      * @param object $entity
  672.      * @param string $view
  673.      *
  674.      * @return array
  675.      */
  676.     protected function getEntityFormOptions($entity$view)
  677.     {
  678.         $formOptions $this->entity[$view]['form_options'];
  679.         $formOptions['entity'] = $this->entity['name'];
  680.         $formOptions['view'] = $view;
  681.         return $formOptions;
  682.     }
  683.     /**
  684.      * Creates the form object used to create or edit the given entity.
  685.      *
  686.      * @param object $entity
  687.      * @param array  $entityProperties
  688.      * @param string $view
  689.      *
  690.      * @return FormInterface
  691.      *
  692.      * @throws \Exception
  693.      */
  694.     protected function createEntityForm($entity, array $entityProperties$view)
  695.     {
  696.         if (method_exists($this$customMethodName 'create'.$this->entity['name'].'EntityForm')) {
  697.             $form $this->{$customMethodName}($entity$entityProperties$view);
  698.             if (!$form instanceof FormInterface) {
  699.                 throw new \UnexpectedValueException(sprintf('The "%s" method must return a FormInterface, "%s" given.'$customMethodName, \is_object($form) ? \get_class($form) : \gettype($form)));
  700.             }
  701.             return $form;
  702.         }
  703.         $formBuilder $this->executeDynamicMethod('create<EntityName>EntityFormBuilder', [$entity$view]);
  704.         if (!$formBuilder instanceof FormBuilderInterface) {
  705.             throw new \UnexpectedValueException(sprintf('The "%s" method must return a FormBuilderInterface, "%s" given.''createEntityForm', \is_object($formBuilder) ? \get_class($formBuilder) : \gettype($formBuilder)));
  706.         }
  707.         return $formBuilder->getForm();
  708.     }
  709.     /**
  710.      * Creates the form used to delete an entity. It must be a form because
  711.      * the deletion of the entity are always performed with the 'DELETE' HTTP method,
  712.      * which requires a form to work in the current browsers.
  713.      *
  714.      * @param string     $entityName
  715.      * @param int|string $entityId   When reusing the delete form for multiple entities, a pattern string is passed instead of an integer
  716.      *
  717.      * @return Form|FormInterface
  718.      */
  719.     protected function createDeleteForm($entityName$entityId)
  720.     {
  721.         /** @var FormBuilder $formBuilder */
  722.         $formBuilder $this->get('form.factory')->createNamedBuilder('delete_form')
  723.             ->setAction($this->generateUrl('easyadmin', ['action' => 'delete''entity' => $entityName'id' => $entityId]))
  724.             ->setMethod('DELETE')
  725.         ;
  726.         $formBuilder->add('submit'SubmitType::class, ['label' => 'delete_modal.action''translation_domain' => 'EasyAdminBundle']);
  727.         // needed to avoid submitting empty delete forms (see issue #1409)
  728.         $formBuilder->add('_easyadmin_delete_flag'HiddenType::class, ['data' => '1']);
  729.         return $formBuilder->getForm();
  730.     }
  731.     /**
  732.      * Utility method that checks if the given action is allowed for
  733.      * the current entity.
  734.      *
  735.      * @param string $actionName
  736.      *
  737.      * @return bool
  738.      */
  739.     protected function isActionAllowed($actionName)
  740.     {
  741.         return false === \in_array($actionName$this->entity['disabled_actions'], true);
  742.     }
  743.     /**
  744.      * Given a method name pattern, it looks for the customized version of that
  745.      * method (based on the entity name) and executes it. If the custom method
  746.      * does not exist, it executes the regular method.
  747.      *
  748.      * For example:
  749.      *   executeDynamicMethod('create<EntityName>Entity') and the entity name is 'User'
  750.      *   if 'createUserEntity()' exists, execute it; otherwise execute 'createEntity()'
  751.      *
  752.      * @param string $methodNamePattern The pattern of the method name (dynamic parts are enclosed with <> angle brackets)
  753.      * @param array  $arguments         The arguments passed to the executed method
  754.      *
  755.      * @return mixed
  756.      */
  757.     protected function executeDynamicMethod($methodNamePattern, array $arguments = [])
  758.     {
  759.         $methodName str_replace('<EntityName>'$this->entity['name'], $methodNamePattern);
  760.         if (!\is_callable([$this$methodName])) {
  761.             $methodName str_replace('<EntityName>'''$methodNamePattern);
  762.         }
  763.         if (!method_exists($this$methodName)) {
  764.             throw new \BadMethodCallException(sprintf('The "%s()" method does not exist in the %s class'$methodName, static::class));
  765.         }
  766.         return \call_user_func_array([$this$methodName], $arguments);
  767.     }
  768.     /**
  769.      * Generates the backend homepage and redirects to it.
  770.      */
  771.     protected function redirectToBackendHomepage()
  772.     {
  773.         $homepageConfig $this->config['homepage'];
  774.         $url $homepageConfig['url'] ?? $this->get('router')->generate($homepageConfig['route'], $homepageConfig['params']);
  775.         return $this->redirect($url);
  776.     }
  777.     /**
  778.      * @return RedirectResponse
  779.      */
  780.     protected function redirectToReferrer()
  781.     {
  782.         $refererUrl $this->request->query->get('referer''');
  783.         $refererAction $this->request->query->get('action');
  784.         // 1. redirect to list if possible
  785.         if ($this->isActionAllowed('list')) {
  786.             if (!empty($refererUrl)) {
  787.                 return $this->redirect(urldecode($refererUrl));
  788.             }
  789.             return $this->redirectToRoute('easyadmin', [
  790.                 'action' => 'list',
  791.                 'entity' => $this->entity['name'],
  792.                 'menuIndex' => $this->request->query->get('menuIndex'),
  793.                 'submenuIndex' => $this->request->query->get('submenuIndex'),
  794.             ]);
  795.         }
  796.         // 2. from new|edit action, redirect to edit if possible
  797.         if (\in_array($refererAction, ['new''edit']) && $this->isActionAllowed('edit')) {
  798.             return $this->redirectToRoute('easyadmin', [
  799.                 'action' => 'edit',
  800.                 'entity' => $this->entity['name'],
  801.                 'menuIndex' => $this->request->query->get('menuIndex'),
  802.                 'submenuIndex' => $this->request->query->get('submenuIndex'),
  803.                 'id' => ('new' === $refererAction)
  804.                     ? PropertyAccess::createPropertyAccessor()->getValue($this->request->attributes->get('easyadmin')['item'], $this->entity['primary_key_field_name'])
  805.                     : $this->request->query->get('id'),
  806.             ]);
  807.         }
  808.         // 3. from new action, redirect to new if possible
  809.         if ('new' === $refererAction && $this->isActionAllowed('new')) {
  810.             return $this->redirectToRoute('easyadmin', [
  811.                 'action' => 'new',
  812.                 'entity' => $this->entity['name'],
  813.                 'menuIndex' => $this->request->query->get('menuIndex'),
  814.                 'submenuIndex' => $this->request->query->get('submenuIndex'),
  815.             ]);
  816.         }
  817.         return $this->redirectToBackendHomepage();
  818.     }
  819.     /**
  820.      * Used to add/modify/remove parameters before passing them to the Twig template.
  821.      * Instead of defining a render method per action (list, show, search, etc.) use
  822.      * the $actionName argument to discriminate between actions.
  823.      *
  824.      * @param string $actionName   The name of the current action (list, show, new, etc.)
  825.      * @param string $templatePath The path of the Twig template to render
  826.      * @param array  $parameters   The parameters passed to the template
  827.      *
  828.      * @return Response
  829.      */
  830.     protected function renderTemplate($actionName$templatePath, array $parameters = [])
  831.     {
  832.         return $this->render($templatePath$parameters);
  833.     }
  834. }