add batch actions to CRUD
This commit is contained in:
parent
0fe6b4f3ed
commit
3cd4ef76bd
|
@ -163,6 +163,54 @@ abstract class CrudController extends AdminController
|
||||||
return $this->json([]);
|
return $this->json([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function doBatch(int $page = 1, 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);
|
||||||
|
|
||||||
|
if ($target === 'selection') {
|
||||||
|
$isSelection = true;
|
||||||
|
$pager = $query->paginate($page, $configuration->getMaxPerPage($context));
|
||||||
|
} else {
|
||||||
|
$isSelection = false;
|
||||||
|
$pager = $query->find();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($pager as $key => $entity) {
|
||||||
|
if (($isSelection && isset($items[$key + 1])) || !$isSelection) {
|
||||||
|
$callback($entity, $entityManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->addFlash('success', 'Batch action done.');
|
||||||
|
|
||||||
|
return $this->json([]);
|
||||||
|
}
|
||||||
|
|
||||||
protected function doDelete(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeDelete = null): Response
|
protected function doDelete(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeDelete = null): Response
|
||||||
{
|
{
|
||||||
$configuration = $this->getConfiguration();
|
$configuration = $this->getConfiguration();
|
||||||
|
|
|
@ -14,6 +14,7 @@ class CrudConfiguration
|
||||||
protected array $pageTitles = [];
|
protected array $pageTitles = [];
|
||||||
protected array $pageRoutes = [];
|
protected array $pageRoutes = [];
|
||||||
protected array $actions = [];
|
protected array $actions = [];
|
||||||
|
protected array $batchActions = [];
|
||||||
protected array $actionTitles = [];
|
protected array $actionTitles = [];
|
||||||
protected array $forms = [];
|
protected array $forms = [];
|
||||||
protected array $formOptions = [];
|
protected array $formOptions = [];
|
||||||
|
@ -110,6 +111,35 @@ class CrudConfiguration
|
||||||
return $this->actions[$page][$action] ?? $default;
|
return $this->actions[$page][$action] ?? $default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setBatchAction(string $page, string $action, string $label, callable $callback): self
|
||||||
|
{
|
||||||
|
if (!isset($this->actions[$page])) {
|
||||||
|
$this->batchActions[$page] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->batchActions[$page][$action] = [
|
||||||
|
'label' => $label,
|
||||||
|
'callback' => $callback,
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBatchActions(string $page)
|
||||||
|
{
|
||||||
|
return $this->batchActions[$page] ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBatchAction(string $page, string $action)
|
||||||
|
{
|
||||||
|
return $this->batchActions[$page][$action] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasBatchAction(string $page)
|
||||||
|
{
|
||||||
|
return !empty($this->batchActions[$page]);
|
||||||
|
}
|
||||||
|
|
||||||
/* -- */
|
/* -- */
|
||||||
|
|
||||||
public function setActionTitle(string $page, string $action, string $title): self
|
public function setActionTitle(string $page, string $action, string $title): self
|
||||||
|
|
|
@ -66,6 +66,14 @@ class <?= $class_name; ?> extends CrudController
|
||||||
return $this->doSort($page, $query, $entityManager, $request, $session);
|
return $this->doSort($page, $query, $entityManager, $request, $session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/admin/<?= $route; ?>/batch/{page}", name="admin_<?= $route; ?>_batch", methods={"POST"}, requirements={"page":"\d+"})
|
||||||
|
*/
|
||||||
|
public function batch(int $page = 1, RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session): Response
|
||||||
|
{
|
||||||
|
return $this->doBatch($page, $query, $entityManager, $request, $session);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Route("/admin/<?= $route; ?>/delete/{entity}", name="admin_<?= $route; ?>_delete", methods={"DELETE"})
|
* @Route("/admin/<?= $route; ?>/delete/{entity}", name="admin_<?= $route; ?>_delete", methods={"DELETE"})
|
||||||
*/
|
*/
|
||||||
|
@ -87,6 +95,7 @@ class <?= $class_name; ?> extends CrudController
|
||||||
->setPageRoute('edit', 'admin_<?= $route; ?>_edit')
|
->setPageRoute('edit', 'admin_<?= $route; ?>_edit')
|
||||||
->setPageRoute('show', 'admin_<?= $route; ?>_show')
|
->setPageRoute('show', 'admin_<?= $route; ?>_show')
|
||||||
->setPageRoute('sort', 'admin_<?= $route; ?>_sort')
|
->setPageRoute('sort', 'admin_<?= $route; ?>_sort')
|
||||||
|
->setPageRoute('batch', 'admin_<?= $route; ?>_batch')
|
||||||
->setPageRoute('delete', 'admin_<?= $route; ?>_delete')
|
->setPageRoute('delete', 'admin_<?= $route; ?>_delete')
|
||||||
->setPageRoute('filter', 'admin_<?= $route; ?>_filter')
|
->setPageRoute('filter', 'admin_<?= $route; ?>_filter')
|
||||||
|
|
||||||
|
@ -108,9 +117,16 @@ class <?= $class_name; ?> extends CrudController
|
||||||
// ->setAction('edit', 'show', true)
|
// ->setAction('edit', 'show', true)
|
||||||
// ->setAction('edit', 'delete', true)
|
// ->setAction('edit', 'delete', true)
|
||||||
|
|
||||||
|
// ->setAction('show', 'back', true)
|
||||||
|
// ->setAction('show', 'edit', true)
|
||||||
|
|
||||||
// ->setField('index', 'Label', Field\TextField::class, [
|
// ->setField('index', 'Label', Field\TextField::class, [
|
||||||
// 'property' => 'label',
|
// 'property' => 'label',
|
||||||
// ])
|
// ])
|
||||||
|
|
||||||
|
// ->setBatchAction('index', 'delete', 'Delete', function(EntityInterface $entity, EntityManager $manager) {
|
||||||
|
// $manager->delete($entity);
|
||||||
|
// })
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -161,3 +161,7 @@
|
||||||
"Results": "Résultats"
|
"Results": "Résultats"
|
||||||
"Clean all cache": "Nettoyer tout le cache"
|
"Clean all cache": "Nettoyer tout le cache"
|
||||||
"You can sort items with drag & drop": "Vous pouvez trier les élements via un glisser/déposer"
|
"You can sort items with drag & drop": "Vous pouvez trier les élements via un glisser/déposer"
|
||||||
|
"Batch action done.": "Action par lot effectuée."
|
||||||
|
"For selection": "Pour la sélection"
|
||||||
|
"For all items": "Pour tous les éléments"
|
||||||
|
"Run": "Lancer"
|
||||||
|
|
|
@ -87,6 +87,12 @@
|
||||||
{% block list_header %}
|
{% block list_header %}
|
||||||
<thead class="thead-light">
|
<thead class="thead-light">
|
||||||
<tr>
|
<tr>
|
||||||
|
{% if configuration.hasBatchAction(context) %}
|
||||||
|
<th class="crud-batch-column">
|
||||||
|
<input type="checkbox">
|
||||||
|
</th>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% for label, config in configuration.fields(context) %}
|
{% for label, config in configuration.fields(context) %}
|
||||||
{% block list_header_item %}
|
{% block list_header_item %}
|
||||||
{% set attr = config.options.attr is defined ? config.options.attr : [] %}
|
{% set attr = config.options.attr is defined ? config.options.attr : [] %}
|
||||||
|
@ -133,7 +139,7 @@
|
||||||
</th>
|
</th>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<th class="col-2 miw-100 text-right">
|
<th class="crud-action-column col-2 miw-100 text-right">
|
||||||
{{ 'Actions'|trans }}
|
{{ 'Actions'|trans }}
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -174,6 +180,12 @@
|
||||||
{% endset -%}
|
{% endset -%}
|
||||||
|
|
||||||
<tr {{ dataSortableItem|raw }} data-dblclick="{{ dbClick }}" class="{{ loop.index is odd ? 'is-odd' : 'is-even' }}">
|
<tr {{ dataSortableItem|raw }} data-dblclick="{{ dbClick }}" class="{{ loop.index is odd ? 'is-odd' : 'is-even' }}">
|
||||||
|
{% if configuration.hasBatchAction(context) %}
|
||||||
|
<td class="crud-batch-column">
|
||||||
|
<input type="checkbox" class="batch_form" name="batch[items][{{ loop.index }}]" form="form-batch" value="{{ loop.index }}">
|
||||||
|
</td>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% for config in configuration.fields(context) %}
|
{% for config in configuration.fields(context) %}
|
||||||
{% set attr = config.options.attr is defined ? config.options.attr : [] %}
|
{% set attr = config.options.attr is defined ? config.options.attr : [] %}
|
||||||
{% set action = config.options.action is defined ? config.options.action : null %}
|
{% set action = config.options.action is defined ? config.options.action : null %}
|
||||||
|
@ -193,7 +205,7 @@
|
||||||
</td>
|
</td>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
<td class="col-2 miw-200 text-right">
|
<td class="crud-action-column col-2 miw-200 text-right">
|
||||||
{% block list_item_actions_before %}{% endblock %}
|
{% block list_item_actions_before %}{% endblock %}
|
||||||
|
|
||||||
{% if configuration.action(context, 'show', true) %}
|
{% if configuration.action(context, 'show', true) %}
|
||||||
|
@ -224,17 +236,59 @@
|
||||||
</tr>
|
</tr>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% if loop.last and isSortable %}
|
|
||||||
<tr>
|
{% if loop.last and (isSortable or configuration.hasBatchAction(context)) %}
|
||||||
<td class="col-12 text-black-50 border-0" colspan="{{ configuration.fields(context)|length + 1 }}">
|
{% block list_footer %}
|
||||||
<span class="fa fa-hand-pointer"></span>
|
{% set count = configuration.fields(context)|length + 1 %}
|
||||||
{{ 'You can sort items with drag & drop'|trans }}
|
{% if configuration.hasBatchAction(context) %}
|
||||||
</td>
|
{% set count = count + 1 %}
|
||||||
</tr>
|
{% endif %}
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="col-12 text-black-50 border-0" colspan="{{ count }}">
|
||||||
|
{% if isSortable %}
|
||||||
|
<div class="d-block mb-2">
|
||||||
|
<span class="fa fa-hand-pointer"></span>
|
||||||
|
{{ 'You can sort items with drag & drop'|trans }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if configuration.hasBatchAction(context) %}
|
||||||
|
<div class="d-block">
|
||||||
|
<form class="form-inline" action="{{ path(configuration.pageRoute('batch'), {page: pager.currentPageNumber}) }}" id="form-batch" method="POST">
|
||||||
|
<select class="form-control my-1 mr-sm-2" name="batch[target]">
|
||||||
|
<option value="selection">{{ 'For selection'|trans }}</option>
|
||||||
|
<option value="all">{{ 'For all items'|trans }}</option>
|
||||||
|
</select>
|
||||||
|
<select class="form-control my-1 mr-sm-2" name="batch[action]">
|
||||||
|
<option value=""></option>
|
||||||
|
{% for action, conf in configuration.batchActions(context) %}
|
||||||
|
<option value="{{ action }}">
|
||||||
|
{{ conf.label|trans }}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
<input type="hidden" name="batch[_token]" value="{{ csrf_token('batch') }}">
|
||||||
|
<button type="submit" class="btn btn-primary my-1">
|
||||||
|
<span class="loader spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||||
|
|
||||||
|
{{ 'Run'|trans }}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endblock %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
|
{% set count = configuration.fields(context)|length + 1 %}
|
||||||
|
{% if configuration.hasBatchAction(context) %}
|
||||||
|
{% set count = count + 1 %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td class="col-12 text-center p-4 text-black-50" colspan="{{ configuration.fields(context)|length + 1 }}">
|
<td class="col-12 text-center p-4 text-black-50" colspan="{{ count }}">
|
||||||
<div class="display-1">
|
<div class="display-1">
|
||||||
<span class="fa fa-search"></span>
|
<span class="fa fa-search"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -53,16 +53,16 @@ class CrudExtension extends AbstractExtension
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_callable($hrefConfig)) {
|
if (is_callable($hrefConfig)) {
|
||||||
$href = call_user_func($hrefConfig, $entity, $config['options']);
|
$attrs['href'] = call_user_func($hrefConfig, $entity, $config['options']);
|
||||||
} else {
|
} else {
|
||||||
$href = $hrefConfig;
|
$attrs['href'] = $hrefConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($attrs as $k => $v) {
|
foreach ($attrs as $k => $v) {
|
||||||
$attributes .= sprintf('%s="%s" ', htmlspecialchars($k), htmlspecialchars($v));
|
$attributes .= sprintf(' %s="%s" ', htmlspecialchars($k), htmlspecialchars($v));
|
||||||
}
|
}
|
||||||
|
|
||||||
$render = sprintf('<a href="%s" %s>%s</a>', htmlspecialchars($href), $attributes, $render);
|
$render = sprintf('<a%s>%s</a>', $attributes, $render);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $render;
|
return $render;
|
||||||
|
|
Loading…
Reference in a new issue