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

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