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

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->container->get(FieldProvider::class)->getDefaultFields($pageName);
  82. }
  83. public static function getSubscribedServices(): array
  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->container->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->container->get(FilterFactory::class)->create($context->getCrud()->getFiltersConfig(), $fields, $context->getEntity());
  113. $queryBuilder = $this->createIndexQueryBuilder($context->getSearch(), $context->getEntity(), $fields, $filters);
  114. $paginator = $this->container->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->container->get(AdminUrlGenerator::class)
  119. ->set(EA::PAGE, $paginator->getLastPage())
  120. ->generateUrl());
  121. }
  122. $entities = $this->container->get(EntityFactory::class)->createCollection($context->getEntity(), $paginator->getResults());
  123. $this->container->get(EntityFactory::class)->processFieldsForAll($entities, $fields);
  124. $actions = $this->container->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->container->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->container->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->container->get(EntityFactory::class)->processFields($context->getEntity(), FieldCollection::new($this->configureFields(Crud::PAGE_DETAIL)));
  155. $this->container->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->container->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->container->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->container->get(EntityFactory::class)->processFields($context->getEntity(), FieldCollection::new($this->configureFields(Crud::PAGE_EDIT)));
  182. $this->container->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->container->get('event_dispatcher')->dispatch($event);
  200. $entityInstance = $event->getEntityInstance();
  201. $this->updateEntity($this->container->get('doctrine')->getManagerForClass($context->getEntity()->getFqcn()), $entityInstance);
  202. $this->container->get('event_dispatcher')->dispatch(new AfterEntityUpdatedEvent($entityInstance));
  203. return $this->getRedirectResponseAfterSave($context, Action::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->container->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->container->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->container->get(EntityFactory::class)->processFields($context->getEntity(), FieldCollection::new($this->configureFields(Crud::PAGE_NEW)));
  233. $this->container->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->container->get('event_dispatcher')->dispatch($event);
  242. $entityInstance = $event->getEntityInstance();
  243. $this->persistEntity($this->container->get('doctrine')->getManagerForClass($context->getEntity()->getFqcn()), $entityInstance);
  244. $this->container->get('event_dispatcher')->dispatch(new AfterEntityPersistedEvent($entityInstance));
  245. $context->getEntity()->setInstance($entityInstance);
  246. return $this->getRedirectResponseAfterSave($context, Action::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->container->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->container->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->container->get('event_dispatcher')->dispatch($event);
  281. if ($event->isPropagationStopped()) {
  282. return $event->getResponse();
  283. }
  284. $entityInstance = $event->getEntityInstance();
  285. try {
  286. $this->deleteEntity($this->container->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->container->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->container->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->container->get(AdminUrlGenerator::class)->setAction(Action::INDEX)->unset(EA::ENTITY_ID)->generateUrl());
  303. }
  304. public function batchDelete(AdminContext $context, BatchActionDto $batchActionDto): Response
  305. {
  306. $event = new BeforeCrudActionEvent($context);
  307. $this->container->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->container->get('doctrine')->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->container->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->container->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->container->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->container->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->container->get(PaginatorFactory::class)->create($queryBuilder);
  363. return JsonResponse::fromJsonString($paginator->getResultsAsJson());
  364. }
  365. public function createIndexQueryBuilder(SearchDto $searchDto, EntityDto $entityDto, FieldCollection $fields, FilterCollection $filters): QueryBuilder
  366. {
  367. return $this->container->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->container->get(EntityFactory::class)->processFields($context->getEntity(), $fields);
  373. $filters = $this->container->get(FilterFactory::class)->create($context->getCrud()->getFiltersConfig(), $context->getEntity()->getFields(), $context->getEntity());
  374. /** @var FiltersFormType $filtersForm */
  375. $filtersForm = $this->container->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 $entityDto, KeyValueStore $formOptions, AdminContext $context): FormInterface
  407. {
  408. return $this->createEditFormBuilder($entityDto, $formOptions, $context)->getForm();
  409. }
  410. public function createEditFormBuilder(EntityDto $entityDto, KeyValueStore $formOptions, AdminContext $context): FormBuilderInterface
  411. {
  412. return $this->container->get(FormFactory::class)->createEditFormBuilder($entityDto, $formOptions, $context);
  413. }
  414. public function createNewForm(EntityDto $entityDto, KeyValueStore $formOptions, AdminContext $context): FormInterface
  415. {
  416. return $this->createNewFormBuilder($entityDto, $formOptions, $context)->getForm();
  417. }
  418. public function createNewFormBuilder(EntityDto $entityDto, KeyValueStore $formOptions, AdminContext $context): FormBuilderInterface
  419. {
  420. return $this->container->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->container->get(AdminContextProvider::class)->getContext();
  432. }
  433. protected function ajaxEdit(EntityDto $entityDto, ?string $propertyName, bool $newValue): AfterCrudActionEvent
  434. {
  435. $this->container->get(EntityUpdater::class)->updateProperty($entityDto, $propertyName, $newValue);
  436. $event = new BeforeEntityUpdatedEvent($entityDto->getInstance());
  437. $this->container->get('event_dispatcher')->dispatch($event);
  438. $entityInstance = $event->getEntityInstance();
  439. $this->updateEntity($this->container->get('doctrine')->getManagerForClass($entityDto->getFqcn()), $entityInstance);
  440. $this->container->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->container->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 $context, string $action): RedirectResponse
  483. {
  484. $submitButtonName = $context->getRequest()->request->all()['ea']['newForm']['btn'];
  485. if (Action::SAVE_AND_CONTINUE === $submitButtonName) {
  486. $url = $this->container->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->container->get(AdminUrlGenerator::class)->setAction(Action::INDEX)->generateUrl();
  495. return $this->redirect($url);
  496. }
  497. if (Action::SAVE_AND_ADD_ANOTHER === $submitButtonName) {
  498. $url = $this->container->get(AdminUrlGenerator::class)->setAction(Action::NEW)->generateUrl();
  499. return $this->redirect($url);
  500. }
  501. return $this->redirectToRoute($context->getDashboardRouteName());
  502. }
  503. }