vendor/easycorp/easyadmin-bundle/src/Controller/AbstractCrudController.php line 193

Open in your IDE?
  1. <?php
  2. namespace EasyCorp\Bundle\EasyAdminBundle\Controller;
  3. use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
  4. use Doctrine\ORM\EntityManagerInterface;
  5. use Doctrine\ORM\QueryBuilder;
  6. use EasyCorp\Bundle\EasyAdminBundle\Collection\FieldCollection;
  7. use EasyCorp\Bundle\EasyAdminBundle\Collection\FilterCollection;
  8. use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
  9. use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
  10. use EasyCorp\Bundle\EasyAdminBundle\Config\Assets;
  11. use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
  12. use EasyCorp\Bundle\EasyAdminBundle\Config\Filters;
  13. use EasyCorp\Bundle\EasyAdminBundle\Config\KeyValueStore;
  14. use EasyCorp\Bundle\EasyAdminBundle\Config\Option\EA;
  15. use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext;
  16. use EasyCorp\Bundle\EasyAdminBundle\Contracts\Controller\CrudControllerInterface;
  17. use EasyCorp\Bundle\EasyAdminBundle\Dto\BatchActionDto;
  18. use EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto;
  19. use EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto;
  20. use EasyCorp\Bundle\EasyAdminBundle\Dto\SearchDto;
  21. use EasyCorp\Bundle\EasyAdminBundle\Event\AfterCrudActionEvent;
  22. use EasyCorp\Bundle\EasyAdminBundle\Event\AfterEntityDeletedEvent;
  23. use EasyCorp\Bundle\EasyAdminBundle\Event\AfterEntityPersistedEvent;
  24. use EasyCorp\Bundle\EasyAdminBundle\Event\AfterEntityUpdatedEvent;
  25. use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeCrudActionEvent;
  26. use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeEntityDeletedEvent;
  27. use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeEntityPersistedEvent;
  28. use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeEntityUpdatedEvent;
  29. use EasyCorp\Bundle\EasyAdminBundle\Exception\EntityRemoveException;
  30. use EasyCorp\Bundle\EasyAdminBundle\Exception\ForbiddenActionException;
  31. use EasyCorp\Bundle\EasyAdminBundle\Exception\InsufficientEntityPermissionException;
  32. use EasyCorp\Bundle\EasyAdminBundle\Factory\ActionFactory;
  33. use EasyCorp\Bundle\EasyAdminBundle\Factory\ControllerFactory;
  34. use EasyCorp\Bundle\EasyAdminBundle\Factory\EntityFactory;
  35. use EasyCorp\Bundle\EasyAdminBundle\Factory\FilterFactory;
  36. use EasyCorp\Bundle\EasyAdminBundle\Factory\FormFactory;
  37. use EasyCorp\Bundle\EasyAdminBundle\Factory\PaginatorFactory;
  38. use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField;
  39. use EasyCorp\Bundle\EasyAdminBundle\Form\Type\FileUploadType;
  40. use EasyCorp\Bundle\EasyAdminBundle\Form\Type\FiltersFormType;
  41. use EasyCorp\Bundle\EasyAdminBundle\Form\Type\Model\FileUploadState;
  42. use EasyCorp\Bundle\EasyAdminBundle\Orm\EntityRepository;
  43. use EasyCorp\Bundle\EasyAdminBundle\Orm\EntityUpdater;
  44. use EasyCorp\Bundle\EasyAdminBundle\Provider\AdminContextProvider;
  45. use EasyCorp\Bundle\EasyAdminBundle\Provider\FieldProvider;
  46. use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
  47. use EasyCorp\Bundle\EasyAdminBundle\Router\CrudUrlGenerator;
  48. use EasyCorp\Bundle\EasyAdminBundle\Security\Permission;
  49. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  50. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  51. use Symfony\Component\Form\FormBuilderInterface;
  52. use Symfony\Component\Form\FormInterface;
  53. use Symfony\Component\HttpFoundation\JsonResponse;
  54. use Symfony\Component\HttpFoundation\RedirectResponse;
  55. use Symfony\Component\HttpFoundation\Response;
  56. use function Symfony\Component\String\u;
  57. /**
  58.  * @author Javier Eguiluz <javier.eguiluz@gmail.com>
  59.  */
  60. abstract class AbstractCrudController extends AbstractController implements CrudControllerInterface
  61. {
  62.     abstract public static function getEntityFqcn(): string;
  63.     public function configureCrud(Crud $crud): Crud
  64.     {
  65.         return $crud;
  66.     }
  67.     public function configureAssets(Assets $assets): Assets
  68.     {
  69.         return $assets;
  70.     }
  71.     public function configureActions(Actions $actions): Actions
  72.     {
  73.         return $actions;
  74.     }
  75.     public function configureFilters(Filters $filters): Filters
  76.     {
  77.         return $filters;
  78.     }
  79.     public function configureFields(string $pageName): iterable
  80.     {
  81.         return $this->get(FieldProvider::class)->getDefaultFields($pageName);
  82.     }
  83.     public static function getSubscribedServices()
  84.     {
  85.         return array_merge(parent::getSubscribedServices(), [
  86.             'event_dispatcher' => '?'.EventDispatcherInterface::class,
  87.             ActionFactory::class => '?'.ActionFactory::class,
  88.             AdminContextProvider::class => '?'.AdminContextProvider::class,
  89.             AdminUrlGenerator::class => '?'.AdminUrlGenerator::class,
  90.             ControllerFactory::class => '?'.ControllerFactory::class,
  91.             CrudUrlGenerator::class => '?'.CrudUrlGenerator::class,
  92.             EntityFactory::class => '?'.EntityFactory::class,
  93.             EntityRepository::class => '?'.EntityRepository::class,
  94.             EntityUpdater::class => '?'.EntityUpdater::class,
  95.             FieldProvider::class => '?'.FieldProvider::class,
  96.             FilterFactory::class => '?'.FilterFactory::class,
  97.             FormFactory::class => '?'.FormFactory::class,
  98.             PaginatorFactory::class => '?'.PaginatorFactory::class,
  99.         ]);
  100.     }
  101.     public function index(AdminContext $context)
  102.     {
  103.         $event = new BeforeCrudActionEvent($context);
  104.         $this->get('event_dispatcher')->dispatch($event);
  105.         if ($event->isPropagationStopped()) {
  106.             return $event->getResponse();
  107.         }
  108.         if (!$this->isGranted(Permission::EA_EXECUTE_ACTION, ['action' => Action::INDEX'entity' => null])) {
  109.             throw new ForbiddenActionException($context);
  110.         }
  111.         $fields FieldCollection::new($this->configureFields(Crud::PAGE_INDEX));
  112.         $filters $this->get(FilterFactory::class)->create($context->getCrud()->getFiltersConfig(), $fields$context->getEntity());
  113.         $queryBuilder $this->createIndexQueryBuilder($context->getSearch(), $context->getEntity(), $fields$filters);
  114.         $paginator $this->get(PaginatorFactory::class)->create($queryBuilder);
  115.         // this can happen after deleting some items and trying to return
  116.         // to a 'index' page that no longer exists. Redirect to the last page instead
  117.         if ($paginator->isOutOfRange()) {
  118.             return $this->redirect($this->get(AdminUrlGenerator::class)
  119.                 ->set(EA::PAGE$paginator->getLastPage())
  120.                 ->generateUrl());
  121.         }
  122.         $entities $this->get(EntityFactory::class)->createCollection($context->getEntity(), $paginator->getResults());
  123.         $this->get(EntityFactory::class)->processFieldsForAll($entities$fields);
  124.         $actions $this->get(EntityFactory::class)->processActionsForAll($entities$context->getCrud()->getActionsConfig());
  125.         $responseParameters $this->configureResponseParameters(KeyValueStore::new([
  126.             'pageName' => Crud::PAGE_INDEX,
  127.             'templateName' => 'crud/index',
  128.             'entities' => $entities,
  129.             'paginator' => $paginator,
  130.             'global_actions' => $actions->getGlobalActions(),
  131.             'batch_actions' => $actions->getBatchActions(),
  132.             'filters' => $filters,
  133.         ]));
  134.         $event = new AfterCrudActionEvent($context$responseParameters);
  135.         $this->get('event_dispatcher')->dispatch($event);
  136.         if ($event->isPropagationStopped()) {
  137.             return $event->getResponse();
  138.         }
  139.         return $responseParameters;
  140.     }
  141.     public function detail(AdminContext $context)
  142.     {
  143.         $event = new BeforeCrudActionEvent($context);
  144.         $this->get('event_dispatcher')->dispatch($event);
  145.         if ($event->isPropagationStopped()) {
  146.             return $event->getResponse();
  147.         }
  148.         if (!$this->isGranted(Permission::EA_EXECUTE_ACTION, ['action' => Action::DETAIL'entity' => $context->getEntity()])) {
  149.             throw new ForbiddenActionException($context);
  150.         }
  151.         if (!$context->getEntity()->isAccessible()) {
  152.             throw new InsufficientEntityPermissionException($context);
  153.         }
  154.         $this->get(EntityFactory::class)->processFields($context->getEntity(), FieldCollection::new($this->configureFields(Crud::PAGE_DETAIL)));
  155.         $this->get(EntityFactory::class)->processActions($context->getEntity(), $context->getCrud()->getActionsConfig());
  156.         $responseParameters $this->configureResponseParameters(KeyValueStore::new([
  157.             'pageName' => Crud::PAGE_DETAIL,
  158.             'templateName' => 'crud/detail',
  159.             'entity' => $context->getEntity(),
  160.         ]));
  161.         $event = new AfterCrudActionEvent($context$responseParameters);
  162.         $this->get('event_dispatcher')->dispatch($event);
  163.         if ($event->isPropagationStopped()) {
  164.             return $event->getResponse();
  165.         }
  166.         return $responseParameters;
  167.     }
  168.     public function edit(AdminContext $context)
  169.     {
  170.         $event = new BeforeCrudActionEvent($context);
  171.         $this->get('event_dispatcher')->dispatch($event);
  172.         if ($event->isPropagationStopped()) {
  173.             return $event->getResponse();
  174.         }
  175.         if (!$this->isGranted(Permission::EA_EXECUTE_ACTION, ['action' => Action::EDIT'entity' => $context->getEntity()])) {
  176.             throw new ForbiddenActionException($context);
  177.         }
  178.         if (!$context->getEntity()->isAccessible()) {
  179.             throw new InsufficientEntityPermissionException($context);
  180.         }
  181.         $this->get(EntityFactory::class)->processFields($context->getEntity(), FieldCollection::new($this->configureFields(Crud::PAGE_EDIT)));
  182.         $this->get(EntityFactory::class)->processActions($context->getEntity(), $context->getCrud()->getActionsConfig());
  183.         $entityInstance $context->getEntity()->getInstance();
  184.         if ($context->getRequest()->isXmlHttpRequest()) {
  185.             $fieldName $context->getRequest()->query->get('fieldName');
  186.             $newValue 'true' === mb_strtolower($context->getRequest()->query->get('newValue'));
  187.             $event $this->ajaxEdit($context->getEntity(), $fieldName$newValue);
  188.             if ($event->isPropagationStopped()) {
  189.                 return $event->getResponse();
  190.             }
  191.             // cast to integer instead of string to avoid sending empty responses for 'false'
  192.             return new Response((int) $newValue);
  193.         }
  194.         $editForm $this->createEditForm($context->getEntity(), $context->getCrud()->getEditFormOptions(), $context);
  195.         $editForm->handleRequest($context->getRequest());
  196.         if ($editForm->isSubmitted() && $editForm->isValid()) {
  197.             $this->processUploadedFiles($editForm);
  198.             $event = new BeforeEntityUpdatedEvent($entityInstance);
  199.             $this->get('event_dispatcher')->dispatch($event);
  200.             $entityInstance $event->getEntityInstance();
  201.             $this->updateEntity($this->get('doctrine')->getManagerForClass($context->getEntity()->getFqcn()), $entityInstance);
  202.             $this->get('event_dispatcher')->dispatch(new AfterEntityUpdatedEvent($entityInstance));
  203.             return $this->getRedirectResponseAfterSave($contextAction::EDIT);
  204.         }
  205.         $responseParameters $this->configureResponseParameters(KeyValueStore::new([
  206.             'pageName' => Crud::PAGE_EDIT,
  207.             'templateName' => 'crud/edit',
  208.             'edit_form' => $editForm,
  209.             'entity' => $context->getEntity(),
  210.         ]));
  211.         $event = new AfterCrudActionEvent($context$responseParameters);
  212.         $this->get('event_dispatcher')->dispatch($event);
  213.         if ($event->isPropagationStopped()) {
  214.             return $event->getResponse();
  215.         }
  216.         return $responseParameters;
  217.     }
  218.     public function new(AdminContext $context)
  219.     {
  220.         $event = new BeforeCrudActionEvent($context);
  221.         $this->get('event_dispatcher')->dispatch($event);
  222.         if ($event->isPropagationStopped()) {
  223.             return $event->getResponse();
  224.         }
  225.         if (!$this->isGranted(Permission::EA_EXECUTE_ACTION, ['action' => Action::NEW, 'entity' => null])) {
  226.             throw new ForbiddenActionException($context);
  227.         }
  228.         if (!$context->getEntity()->isAccessible()) {
  229.             throw new InsufficientEntityPermissionException($context);
  230.         }
  231.         $context->getEntity()->setInstance($this->createEntity($context->getEntity()->getFqcn()));
  232.         $this->get(EntityFactory::class)->processFields($context->getEntity(), FieldCollection::new($this->configureFields(Crud::PAGE_NEW)));
  233.         $this->get(EntityFactory::class)->processActions($context->getEntity(), $context->getCrud()->getActionsConfig());
  234.         $newForm $this->createNewForm($context->getEntity(), $context->getCrud()->getNewFormOptions(), $context);
  235.         $newForm->handleRequest($context->getRequest());
  236.         $entityInstance $newForm->getData();
  237.         $context->getEntity()->setInstance($entityInstance);
  238.         if ($newForm->isSubmitted() && $newForm->isValid()) {
  239.             $this->processUploadedFiles($newForm);
  240.             $event = new BeforeEntityPersistedEvent($entityInstance);
  241.             $this->get('event_dispatcher')->dispatch($event);
  242.             $entityInstance $event->getEntityInstance();
  243.             $this->persistEntity($this->get('doctrine')->getManagerForClass($context->getEntity()->getFqcn()), $entityInstance);
  244.             $this->get('event_dispatcher')->dispatch(new AfterEntityPersistedEvent($entityInstance));
  245.             $context->getEntity()->setInstance($entityInstance);
  246.             return $this->getRedirectResponseAfterSave($contextAction::NEW);
  247.         }
  248.         $responseParameters $this->configureResponseParameters(KeyValueStore::new([
  249.             'pageName' => Crud::PAGE_NEW,
  250.             'templateName' => 'crud/new',
  251.             'entity' => $context->getEntity(),
  252.             'new_form' => $newForm,
  253.         ]));
  254.         $event = new AfterCrudActionEvent($context$responseParameters);
  255.         $this->get('event_dispatcher')->dispatch($event);
  256.         if ($event->isPropagationStopped()) {
  257.             return $event->getResponse();
  258.         }
  259.         return $responseParameters;
  260.     }
  261.     public function delete(AdminContext $context)
  262.     {
  263.         $event = new BeforeCrudActionEvent($context);
  264.         $this->get('event_dispatcher')->dispatch($event);
  265.         if ($event->isPropagationStopped()) {
  266.             return $event->getResponse();
  267.         }
  268.         if (!$this->isGranted(Permission::EA_EXECUTE_ACTION, ['action' => Action::DELETE'entity' => $context->getEntity()])) {
  269.             throw new ForbiddenActionException($context);
  270.         }
  271.         if (!$context->getEntity()->isAccessible()) {
  272.             throw new InsufficientEntityPermissionException($context);
  273.         }
  274.         $csrfToken $context->getRequest()->request->get('token');
  275.         if (!$this->isCsrfTokenValid('ea-delete'$csrfToken)) {
  276.             return $this->redirectToRoute($context->getDashboardRouteName());
  277.         }
  278.         $entityInstance $context->getEntity()->getInstance();
  279.         $event = new BeforeEntityDeletedEvent($entityInstance);
  280.         $this->get('event_dispatcher')->dispatch($event);
  281.         if ($event->isPropagationStopped()) {
  282.             return $event->getResponse();
  283.         }
  284.         $entityInstance $event->getEntityInstance();
  285.         try {
  286.             $this->deleteEntity($this->get('doctrine')->getManagerForClass($context->getEntity()->getFqcn()), $entityInstance);
  287.         } catch (ForeignKeyConstraintViolationException $e) {
  288.             throw new EntityRemoveException(['entity_name' => $context->getEntity()->getName(), 'message' => $e->getMessage()]);
  289.         }
  290.         $this->get('event_dispatcher')->dispatch(new AfterEntityDeletedEvent($entityInstance));
  291.         $responseParameters $this->configureResponseParameters(KeyValueStore::new([
  292.             'entity' => $context->getEntity(),
  293.         ]));
  294.         $event = new AfterCrudActionEvent($context$responseParameters);
  295.         $this->get('event_dispatcher')->dispatch($event);
  296.         if ($event->isPropagationStopped()) {
  297.             return $event->getResponse();
  298.         }
  299.         if (null !== $referrer $context->getReferrer()) {
  300.             return $this->redirect($referrer);
  301.         }
  302.         return $this->redirect($this->get(AdminUrlGenerator::class)->setAction(Action::INDEX)->unset(EA::ENTITY_ID)->generateUrl());
  303.     }
  304.     public function batchDelete(AdminContext $contextBatchActionDto $batchActionDto): Response
  305.     {
  306.         $event = new BeforeCrudActionEvent($context);
  307.         $this->get('event_dispatcher')->dispatch($event);
  308.         if ($event->isPropagationStopped()) {
  309.             return $event->getResponse();
  310.         }
  311.         if (!$this->isCsrfTokenValid('ea-batch-action-'.Action::BATCH_DELETE$batchActionDto->getCsrfToken())) {
  312.             return $this->redirectToRoute($context->getDashboardRouteName());
  313.         }
  314.         $entityManager $this->getDoctrine()->getManagerForClass($batchActionDto->getEntityFqcn());
  315.         $repository $entityManager->getRepository($batchActionDto->getEntityFqcn());
  316.         foreach ($batchActionDto->getEntityIds() as $entityId) {
  317.             $entityInstance $repository->find($entityId);
  318.             if (!$entityInstance) {
  319.                 continue;
  320.             }
  321.             $entityDto $context->getEntity()->newWithInstance($entityInstance);
  322.             if (!$this->isGranted(Permission::EA_EXECUTE_ACTION, ['action' => Action::DELETE'entity' => $entityDto])) {
  323.                 throw new ForbiddenActionException($context);
  324.             }
  325.             if (!$entityDto->isAccessible()) {
  326.                 throw new InsufficientEntityPermissionException($context);
  327.             }
  328.             $event = new BeforeEntityDeletedEvent($entityInstance);
  329.             $this->get('event_dispatcher')->dispatch($event);
  330.             $entityInstance $event->getEntityInstance();
  331.             try {
  332.                 $this->deleteEntity($entityManager$entityInstance);
  333.             } catch (ForeignKeyConstraintViolationException $e) {
  334.                 throw new EntityRemoveException(['entity_name' => $entityDto->toString(), 'message' => $e->getMessage()]);
  335.             }
  336.             $this->get('event_dispatcher')->dispatch(new AfterEntityDeletedEvent($entityInstance));
  337.         }
  338.         $responseParameters $this->configureResponseParameters(KeyValueStore::new([
  339.             'entity' => $context->getEntity(),
  340.             'batchActionDto' => $batchActionDto,
  341.         ]));
  342.         $event = new AfterCrudActionEvent($context$responseParameters);
  343.         $this->get('event_dispatcher')->dispatch($event);
  344.         if ($event->isPropagationStopped()) {
  345.             return $event->getResponse();
  346.         }
  347.         return $this->redirect($batchActionDto->getReferrerUrl());
  348.     }
  349.     public function autocomplete(AdminContext $context): JsonResponse
  350.     {
  351.         $queryBuilder $this->createIndexQueryBuilder($context->getSearch(), $context->getEntity(), FieldCollection::new([]), FilterCollection::new());
  352.         $autocompleteContext $context->getRequest()->get(AssociationField::PARAM_AUTOCOMPLETE_CONTEXT);
  353.         /** @var CrudControllerInterface $controller */
  354.         $controller $this->get(ControllerFactory::class)->getCrudControllerInstance($autocompleteContext[EA::CRUD_CONTROLLER_FQCN], Action::INDEX$context->getRequest());
  355.         /** @var FieldDto $field */
  356.         $field FieldCollection::new($controller->configureFields($autocompleteContext['originatingPage']))->getByProperty($autocompleteContext['propertyName']);
  357.         /** @var \Closure|null $queryBuilderCallable */
  358.         $queryBuilderCallable $field->getCustomOption(AssociationField::OPTION_QUERY_BUILDER_CALLABLE);
  359.         if (null !== $queryBuilderCallable) {
  360.             $queryBuilderCallable($queryBuilder);
  361.         }
  362.         $paginator $this->get(PaginatorFactory::class)->create($queryBuilder);
  363.         return JsonResponse::fromJsonString($paginator->getResultsAsJson());
  364.     }
  365.     public function createIndexQueryBuilder(SearchDto $searchDtoEntityDto $entityDtoFieldCollection $fieldsFilterCollection $filters): QueryBuilder
  366.     {
  367.         return $this->get(EntityRepository::class)->createQueryBuilder($searchDto$entityDto$fields$filters);
  368.     }
  369.     public function renderFilters(AdminContext $context): KeyValueStore
  370.     {
  371.         $fields FieldCollection::new($this->configureFields(Crud::PAGE_INDEX));
  372.         $this->get(EntityFactory::class)->processFields($context->getEntity(), $fields);
  373.         $filters $this->get(FilterFactory::class)->create($context->getCrud()->getFiltersConfig(), $context->getEntity()->getFields(), $context->getEntity());
  374.         /** @var FiltersFormType $filtersForm */
  375.         $filtersForm $this->get(FormFactory::class)->createFiltersForm($filters$context->getRequest());
  376.         $formActionParts parse_url($filtersForm->getConfig()->getAction());
  377.         $queryString $formActionParts[EA::QUERY] ?? '';
  378.         parse_str($queryString$queryStringAsArray);
  379.         unset($queryStringAsArray[EA::FILTERS], $queryStringAsArray[EA::PAGE]);
  380.         $responseParameters KeyValueStore::new([
  381.             'templateName' => 'crud/filters',
  382.             'filters_form' => $filtersForm,
  383.             'form_action_query_string_as_array' => $queryStringAsArray,
  384.         ]);
  385.         return $this->configureResponseParameters($responseParameters);
  386.     }
  387.     public function createEntity(string $entityFqcn)
  388.     {
  389.         return new $entityFqcn();
  390.     }
  391.     public function updateEntity(EntityManagerInterface $entityManager$entityInstance): void
  392.     {
  393.         $entityManager->persist($entityInstance);
  394.         $entityManager->flush();
  395.     }
  396.     public function persistEntity(EntityManagerInterface $entityManager$entityInstance): void
  397.     {
  398.         $entityManager->persist($entityInstance);
  399.         $entityManager->flush();
  400.     }
  401.     public function deleteEntity(EntityManagerInterface $entityManager$entityInstance): void
  402.     {
  403.         $entityManager->remove($entityInstance);
  404.         $entityManager->flush();
  405.     }
  406.     public function createEditForm(EntityDto $entityDtoKeyValueStore $formOptionsAdminContext $context): FormInterface
  407.     {
  408.         return $this->createEditFormBuilder($entityDto$formOptions$context)->getForm();
  409.     }
  410.     public function createEditFormBuilder(EntityDto $entityDtoKeyValueStore $formOptionsAdminContext $context): FormBuilderInterface
  411.     {
  412.         return $this->get(FormFactory::class)->createEditFormBuilder($entityDto$formOptions$context);
  413.     }
  414.     public function createNewForm(EntityDto $entityDtoKeyValueStore $formOptionsAdminContext $context): FormInterface
  415.     {
  416.         return $this->createNewFormBuilder($entityDto$formOptions$context)->getForm();
  417.     }
  418.     public function createNewFormBuilder(EntityDto $entityDtoKeyValueStore $formOptionsAdminContext $context): FormBuilderInterface
  419.     {
  420.         return $this->get(FormFactory::class)->createNewFormBuilder($entityDto$formOptions$context);
  421.     }
  422.     /**
  423.      * Used to add/modify/remove parameters before passing them to the Twig template.
  424.      */
  425.     public function configureResponseParameters(KeyValueStore $responseParameters): KeyValueStore
  426.     {
  427.         return $responseParameters;
  428.     }
  429.     protected function getContext(): ?AdminContext
  430.     {
  431.         return $this->get(AdminContextProvider::class)->getContext();
  432.     }
  433.     protected function ajaxEdit(EntityDto $entityDto, ?string $propertyNamebool $newValue): AfterCrudActionEvent
  434.     {
  435.         $this->get(EntityUpdater::class)->updateProperty($entityDto$propertyName$newValue);
  436.         $event = new BeforeEntityUpdatedEvent($entityDto->getInstance());
  437.         $this->get('event_dispatcher')->dispatch($event);
  438.         $entityInstance $event->getEntityInstance();
  439.         $this->updateEntity($this->get('doctrine')->getManagerForClass($entityDto->getFqcn()), $entityInstance);
  440.         $this->get('event_dispatcher')->dispatch(new AfterEntityUpdatedEvent($entityInstance));
  441.         $entityDto->setInstance($entityInstance);
  442.         $parameters KeyValueStore::new([
  443.             'action' => Action::EDIT,
  444.             'entity' => $entityDto,
  445.         ]);
  446.         $event = new AfterCrudActionEvent($this->getContext(), $parameters);
  447.         $this->get('event_dispatcher')->dispatch($event);
  448.         return $event;
  449.     }
  450.     protected function processUploadedFiles(FormInterface $form): void
  451.     {
  452.         /** @var FormInterface $child */
  453.         foreach ($form as $child) {
  454.             $config $child->getConfig();
  455.             if (!$config->getType()->getInnerType() instanceof FileUploadType) {
  456.                 if ($config->getCompound()) {
  457.                     $this->processUploadedFiles($child);
  458.                 }
  459.                 continue;
  460.             }
  461.             /** @var FileUploadState $state */
  462.             $state $config->getAttribute('state');
  463.             if (!$state->isModified()) {
  464.                 continue;
  465.             }
  466.             $uploadDelete $config->getOption('upload_delete');
  467.             if ($state->hasCurrentFiles() && ($state->isDelete() || (!$state->isAddAllowed() && $state->hasUploadedFiles()))) {
  468.                 foreach ($state->getCurrentFiles() as $file) {
  469.                     $uploadDelete($file);
  470.                 }
  471.                 $state->setCurrentFiles([]);
  472.             }
  473.             $filePaths = (array) $child->getData();
  474.             $uploadDir $config->getOption('upload_dir');
  475.             $uploadNew $config->getOption('upload_new');
  476.             foreach ($state->getUploadedFiles() as $index => $file) {
  477.                 $fileName u($filePaths[$index])->replace($uploadDir'')->toString();
  478.                 $uploadNew($file$uploadDir$fileName);
  479.             }
  480.         }
  481.     }
  482.     protected function getRedirectResponseAfterSave(AdminContext $contextstring $action): RedirectResponse
  483.     {
  484.         $submitButtonName $context->getRequest()->request->all()['ea']['newForm']['btn'];
  485.         if (Action::SAVE_AND_CONTINUE === $submitButtonName) {
  486.             $url $this->get(AdminUrlGenerator::class)
  487.                 ->setAction(Action::EDIT)
  488.                 ->setEntityId($context->getEntity()->getPrimaryKeyValue())
  489.                 ->generateUrl();
  490.             return $this->redirect($url);
  491.         }
  492.         if (Action::SAVE_AND_RETURN === $submitButtonName) {
  493.             $url $context->getReferrer()
  494.                 ?? $this->get(AdminUrlGenerator::class)->setAction(Action::INDEX)->generateUrl();
  495.             return $this->redirect($url);
  496.         }
  497.         if (Action::SAVE_AND_ADD_ANOTHER === $submitButtonName) {
  498.             $url $this->get(AdminUrlGenerator::class)->setAction(Action::NEW)->generateUrl();
  499.             return $this->redirect($url);
  500.         }
  501.         return $this->redirectToRoute($context->getDashboardRouteName());
  502.     }
  503. }