murph-core/src/core/Controller/Admin/Crud/CrudController.php

475 lines
16 KiB
PHP

<?php
namespace App\Core\Controller\Admin\Crud;
use App\Core\Controller\Admin\AdminController;
use App\Core\Crud\CrudConfiguration;
use App\Core\Entity\EntityInterface;
use App\Core\Manager\EntityManager;
use App\Core\Repository\RepositoryQuery;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;
/**
* class CrudController.
*
* @author Simon Vieille <simon@deblan.fr>
*/
abstract class CrudController extends AdminController
{
protected array $filters = [];
protected array $sort = [
'label' => null,
'direction' => null,
];
abstract protected function getConfiguration(): CrudConfiguration;
protected function doIndex(int $page, RepositoryQuery $query, Request $request, Session $session, string $context = 'index'): Response
{
$configuration = $this->getConfiguration();
$this->applySort('index', $query, $request);
$this->updateFilters($request, $session);
$pager = $query
->usefilters($this->filters)
->paginate($page, $configuration->getMaxPerPage($context))
;
return $this->render($this->getConfiguration()->getView($context), [
'configuration' => $configuration,
'pager' => $pager,
'sort' => $this->sort,
'context' => $context,
'filters' => [
'show' => null !== $configuration->getForm('filter'),
'isEmpty' => empty($this->filters),
],
]);
}
protected function doNew(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeCreate = null, string $context = 'new'): Response
{
$configuration = $this->getConfiguration();
$this->prepareEntity($entity);
$form = $this->createForm($configuration->getForm('new'), $entity, $configuration->getFormOptions($context));
if ($request->isMethod('POST')) {
$form->handleRequest($request);
if ($form->isValid()) {
if (null !== $beforeCreate) {
call_user_func_array($beforeCreate, [$entity, $form, $request]);
}
$entityManager->create($entity);
$this->addFlash('success', 'The data has been saved.');
return $this->redirectToRoute($configuration->getPageRoute('edit'), array_merge(
['entity' => $entity->getId()],
$configuration->getPageRouteParams('edit')
));
}
$this->addFlash('warning', 'The form is not valid.');
}
return $this->render($configuration->getView($context), [
'form' => $form->createView(),
'configuration' => $configuration,
'context' => $context,
'entity' => $entity,
]);
}
protected function doShow(EntityInterface $entity, string $context = 'show'): Response
{
$configuration = $this->getConfiguration();
return $this->render($configuration->getView($context), [
'entity' => $entity,
'context' => $context,
'configuration' => $configuration,
]);
}
protected function doEdit(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeUpdate = null, string $context = 'edit'): Response
{
$configuration = $this->getConfiguration();
$this->prepareEntity($entity);
$form = $this->createForm($configuration->getForm('edit'), $entity, $configuration->getFormOptions($context));
if ($request->isMethod('POST')) {
$form->handleRequest($request);
if ($form->isValid()) {
if (null !== $beforeUpdate) {
call_user_func_array($beforeUpdate, [$entity, $form, $request]);
}
$entityManager->update($entity);
$this->addFlash('success', 'The data has been saved.');
return $this->redirectToRoute($configuration->getPageRoute($context), array_merge(
['entity' => $entity->getId()],
$configuration->getPageRouteParams($context)
));
}
$this->addFlash('warning', 'The form is not valid.');
}
return $this->render($configuration->getView($context), [
'form' => $form->createView(),
'context' => $context,
'configuration' => $configuration,
'entity' => $entity,
]);
}
protected function doInlineEdit(string $context, string $label, EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeUpdate = null): Response
{
$configuration = $this->getConfiguration();
$this->prepareEntity($entity);
$builder = $this->createFormBuilder($entity);
$callback = $configuration->getFields($context)[$label]['options']['inline_form'] ?? null;
$validationCallback = $configuration->getFields($context)[$label]['options']['inline_form_validation'] ?? null;
if (null === $callback) {
throw $this->createNotFoundException();
}
call_user_func_array($callback, [$builder, $entity]);
$redirectTo = $request->query->get('redirectTo');
$form = $builder->getForm();
$session = $request->getSession();
$lastRequestId = sprintf(
'inline_request_%s_%s_%s_%s',
get_class($entity),
$entity->getId(),
$context,
$label
);
$lastRequest = $session->get($lastRequestId);
if (null !== $lastRequest && !$request->isMethod('POST')) {
$fakeRequest = Request::create(
uri: $request->getUri(),
method: 'POST',
parameters: [$form->getName() => $lastRequest]
);
$form->handleRequest($fakeRequest);
if (null !== $validationCallback) {
call_user_func_array($validationCallback, [$entity, $form, $request]);
}
$session->remove($lastRequestId);
}
if ($request->isMethod('POST')) {
$form->handleRequest($request);
if (null !== $validationCallback) {
call_user_func_array($validationCallback, [$entity, $form, $request]);
}
if ($form->isValid()) {
if (null !== $beforeUpdate) {
call_user_func_array($beforeUpdate, [$entity, $form, $request]);
}
$session->remove($lastRequestId);
$entityManager->update($entity);
$this->addFlash('success', 'The data has been saved.');
return $this->redirect($redirectTo);
}
$session->set($lastRequestId, $request->request->get('form'));
$this->addFlash('warning', 'The form is not valid.');
return $this->redirect(sprintf(
'%s?data-modal=%s',
$redirectTo,
urlencode($request->getUri())
));
}
return $this->render($configuration->getView('inline_edit'), [
'form' => $form->createView(),
'configuration' => $configuration,
'entity' => $entity,
'context' => $context,
'label' => $label,
'redirectTo' => $redirectTo,
]);
}
protected function doSort(int $page, RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session): Response
{
$configuration = $this->getConfiguration();
$context = $request->query->get('context', 'index');
if (!$configuration->getIsSortableCollection($context)) {
throw $this->createNotFoundException();
}
$this->applySort($context, $query, $request);
$this->updateFilters($request, $session);
$pager = $query
->useFilters($this->filters)
->paginate($page, $configuration->getMaxPerPage($context))
;
if ($this->isCsrfTokenValid('sort', $request->query->get('_token'))) {
$items = $request->request->get('items', []);
$setter = 'set'.$configuration->getSortableCollectionProperty();
$orderStart = ($page - 1) * $configuration->getMaxPerPage($context);
foreach ($pager as $key => $entity) {
if (isset($items[$key + 1])) {
$entity->{$setter}($items[$key + 1] + $orderStart);
$entityManager->update($entity);
}
}
$this->addFlash('success', 'The data has been saved.');
} else {
$this->addFlash('warning', 'The form is not valid.');
}
return $this->json([]);
}
protected function doBatch(int $page, RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session): Response
{
$configuration = $this->getConfiguration();
$datas = $request->request->get('batch', []);
$context = $datas['context'] ?? 'index';
$target = $datas['target'] ?? null;
$action = $datas['action'] ?? null;
$token = $datas['_token'] ?? null;
$items = $datas['items'] ?? [];
$batchAction = $configuration->getBatchAction($context, $action);
if (empty($context) || empty($action) || empty($target)) {
return $this->json([]);
}
if (!$this->isCsrfTokenValid('batch', $token) || empty($batchAction)) {
$this->addFlash('warning', 'The form is not valid.');
return $this->json([]);
}
$callback = $batchAction['callback'];
$this->applySort($context, $query, $request);
$this->updateFilters($request, $session);
$query->useFilters($this->filters);
$useSelection = 'selection' === $target;
if ($batchAction['isGlobal']) {
$selection = null;
if ($useSelection) {
$queryClone = clone $query;
$pager = $queryClone->paginate($page, $configuration->getMaxPerPage($context));
$selection = [];
foreach ($pager as $key => $entity) {
if (isset($items[$key + 1])) {
$selection[] = $entity;
}
}
}
$result = $callback($query, $entityManager, $selection);
if ($result instanceof Response) {
return $result;
}
return $this->redirect($request->query->get('redirectTo'));
}
$pager = $useSelection
? $query->paginate($page, $configuration->getMaxPerPage($context))
: $query->find()
;
foreach ($pager as $key => $entity) {
if (($useSelection && isset($items[$key + 1])) || !$useSelection) {
$callback($entity, $entityManager);
}
}
$this->addFlash('success', 'Batch action done.');
return $this->json([]);
}
protected function doDelete(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeDelete = null, string $route = 'index'): Response
{
$configuration = $this->getConfiguration();
if ($this->isCsrfTokenValid('delete'.$entity->getId(), $request->request->get('_token'))) {
if (null !== $beforeDelete) {
call_user_func($beforeDelete, $entity);
}
$entityManager->delete($entity);
$this->addFlash('success', 'The data has been removed.');
}
return $this->redirectToRoute($configuration->getPageRoute($route));
}
protected function doFilter(Session $session, string $context = 'filter'): Response
{
$configuration = $this->getConfiguration();
$type = $configuration->getForm('filter');
if (null === $type) {
throw $this->createNotFoundException();
}
$form = $this->createForm($type, null, $configuration->getFormOptions('filter'));
$form->submit($session->get($form->getName(), []));
return $this->render($configuration->getView($context), [
'form' => $form->createView(),
'context' => $context,
'configuration' => $configuration,
]);
}
protected function updateFilters(Request $request, Session $session)
{
$configuration = $this->getConfiguration();
$type = $configuration->getForm('filter');
if (null === $type) {
return;
}
$form = $this->createForm($type, null, $configuration->getFormOptions('filter'));
if ($request->query->has($form->getName())) {
$filters = $request->query->get($form->getName());
if ('0' === $filters) {
$filters = [];
}
} elseif ($session->has($form->getName())) {
$filters = $session->get($form->getName());
} else {
$filters = [];
}
$form->submit($filters);
if (empty($filters)) {
$this->filters = $filters;
$session->set($form->getName(), $filters);
} elseif ($form->isValid()) {
$this->filters = $form->getData();
$session->set($form->getName(), $filters);
}
}
protected function prepareEntity(EntityInterface $entity)
{
$configuration = $this->getConfiguration();
if ($configuration->isI18n()) {
foreach ($configuration->getLocales() as $locale) {
$entity->addTranslation($entity->translate($locale, false));
}
}
}
protected function applySort(string $context, RepositoryQuery $query, Request $request)
{
$configuration = $this->getConfiguration();
if ($configuration->getIsSortableCollection($context)) {
$query->orderBy(sprintf('.%s', $configuration->getSortableCollectionProperty()));
return;
}
$defaultSort = $configuration->getDefaultSort($context);
$session = $request->getSession();
$sessionId = sprintf('%s_%s_sort', $context, get_called_class());
$sessionSortName = sprintf('%s_label', $sessionId);
$sessionSortDirection = sprintf('%s_direction', $sessionId);
$name = $request->query->get('_sort', $session->get($sessionSortName)) ?? $defaultSort['label'] ?? null;
$direction = strtolower(
$request->query->get(
'_sort_direction',
$session->get($sessionSortDirection)
) ?? $defaultSort['direction'] ?? 'asc'
);
$session->set($sessionSortName, $name);
$session->set($sessionSortDirection, $direction);
if (empty($name)) {
return;
}
if (!in_array($direction, ['asc', 'desc'])) {
$direction = 'asc';
}
foreach ($configuration->getFields($context) as $label => $field) {
$sortOption = $field['options']['sort'] ?? null;
if (null === $sortOption) {
continue;
}
if ($sortOption[0] !== $name) {
continue;
}
$sorter = $sortOption[1];
if (is_string($sorter)) {
$query->orderBy($sorter, $direction);
} else {
call_user_func_array($sorter, [$query, $direction]);
}
$this->sort = [
'label' => $label,
'direction' => $direction,
];
return;
}
}
}