src/Controller/Overriding/FlexyAbstractCrudController.php line 165

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