add base of crud controller
This commit is contained in:
parent
b0990c0adf
commit
b556432f3e
156
core/Controller/Admin/Crud/CrudController.php
Normal file
156
core/Controller/Admin/Crud/CrudController.php
Normal file
|
@ -0,0 +1,156 @@
|
|||
<?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 Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
use App\Core\Repository\RepositoryQuery;
|
||||
|
||||
/**
|
||||
* class CrudController.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
abstract class CrudController extends AdminController
|
||||
{
|
||||
abstract protected function getConfiguration(): CrudConfiguration;
|
||||
|
||||
protected function doIndex(int $page = 1, RepositoryQuery $query, Request $request, Session $session): Response
|
||||
{
|
||||
/*$this->updateFilters($options['request'], $options['session']);*/
|
||||
|
||||
$pager = $query
|
||||
//->useFilters($this->filters)
|
||||
->paginate($page)
|
||||
;
|
||||
|
||||
/*$viewOptions = array_merge([
|
||||
'pager' => $pager,
|
||||
'hasFilters' => !empty($this->filters),
|
||||
], $options['viewOptions']);*/
|
||||
|
||||
$configuration = $this->getConfiguration();
|
||||
|
||||
return $this->render($this->getConfiguration()->getView('index'), [
|
||||
'configuration' => $configuration,
|
||||
'pager' => $pager,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function doNew(EntityInterface $entity, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
$form = $this->createForm($this->forms['new'], $entity);
|
||||
|
||||
if ($request->isMethod('POST')) {
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isValid()) {
|
||||
$entityManager->create($entity);
|
||||
$this->addFlash('success', 'The data has been saved.');
|
||||
|
||||
return $this->redirectToRoute($configuration->getPageRoute('edit'), [
|
||||
'entity' => $entity->getId(),
|
||||
]);
|
||||
}
|
||||
$this->addFlash('warning', 'The form is not valid.');
|
||||
}
|
||||
|
||||
return $this->render($configuration->getView('new'), [
|
||||
'form' => $form->createView(),
|
||||
'entity' => $entity,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function doShow(EntityInterface $entity): Response
|
||||
{
|
||||
$configuration = $this->getConfiguration();
|
||||
|
||||
return $this->render($configuration->getView('show'), [
|
||||
'entity' => $entity,
|
||||
'configuration' => $configuration,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function doEdit(EntityInterface $entity, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
$configuration = $this->getConfiguration();
|
||||
|
||||
$form = $this->createForm($configuration->getForm('edit'), $entity);
|
||||
|
||||
if ($request->isMethod('POST')) {
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isValid()) {
|
||||
$entityManager->update($entity);
|
||||
$this->addFlash('success', 'The data has been saved.');
|
||||
|
||||
return $this->redirectToRoute($configuration->getPageRoute('edit'), [
|
||||
'entity' => $entity->getId(),
|
||||
]);
|
||||
}
|
||||
$this->addFlash('warning', 'The form is not valid.');
|
||||
}
|
||||
|
||||
return $this->render($configuration->getView('edit'), [
|
||||
'form' => $form->createView(),
|
||||
'configuration' => $configuration,
|
||||
'entity' => $entity,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function doDelete(EntityInterface $entity, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
if ($this->isCsrfTokenValid('delete'.$entity->getId(), $request->request->get('_token'))) {
|
||||
$entityManager->delete($entity);
|
||||
|
||||
$this->addFlash('success', 'The data has been removed.');
|
||||
}
|
||||
|
||||
return $this->redirectToRoute($configuration->getPageRoute('index'));
|
||||
}
|
||||
|
||||
protected function doFilter(Session $session): Response
|
||||
{
|
||||
$form = $this->createForm($this->forms['filters']);
|
||||
$form->submit($session->get($this->filterRequestId, []));
|
||||
|
||||
return $this->render($this->getView('filters'), [
|
||||
'form' => $form->createView(),
|
||||
]);
|
||||
}
|
||||
|
||||
protected function updateFilters(Request $request, Session $session)
|
||||
{
|
||||
if ($request->query->has($this->filterRequestId)) {
|
||||
$filters = $request->query->get($this->filterRequestId);
|
||||
|
||||
if ('0' === $filters) {
|
||||
$filters = [];
|
||||
}
|
||||
} elseif ($session->has($this->filterRequestId)) {
|
||||
$filters = $session->get($this->filterRequestId);
|
||||
} else {
|
||||
$filters = [];
|
||||
}
|
||||
|
||||
if (isset($this->forms['filters'])) {
|
||||
$form = $this->createForm($this->forms['filters']);
|
||||
$form->submit($filters);
|
||||
} else {
|
||||
$form = null;
|
||||
}
|
||||
|
||||
if (empty($filters)) {
|
||||
$this->filters = $filters;
|
||||
$session->set($this->filterRequestId, $filters);
|
||||
} elseif (null !== $form && $form->isValid()) {
|
||||
$this->filters = $form->getData();
|
||||
$session->set($this->filterRequestId, $filters);
|
||||
}
|
||||
}
|
||||
}
|
149
core/Crud/CrudConfiguration.php
Normal file
149
core/Crud/CrudConfiguration.php
Normal file
|
@ -0,0 +1,149 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Crud;
|
||||
|
||||
/**
|
||||
* class CrudConfiguration.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class CrudConfiguration
|
||||
{
|
||||
protected array $pageTitles = [];
|
||||
protected array $pageRoutes = [];
|
||||
protected array $actions = [];
|
||||
protected array $actionTitles = [];
|
||||
protected array $forms = [];
|
||||
protected array $formOptions = [];
|
||||
protected array $views = [];
|
||||
protected array $fields = [];
|
||||
|
||||
/* -- */
|
||||
|
||||
public function setPageTitle(string $page, string $title): self
|
||||
{
|
||||
$this->pageTitles[$page] = $title;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPageTitle(string $page, ?string $default = null): ?string
|
||||
{
|
||||
return $this->pageTitles[$page] ?? $default;
|
||||
}
|
||||
|
||||
/* -- */
|
||||
|
||||
public function setPageRoute(string $page, string $route): self
|
||||
{
|
||||
$this->pageRoutes[$page] = $route;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPageRoute(string $page): ?string
|
||||
{
|
||||
return $this->pageRoutes[$page];
|
||||
}
|
||||
|
||||
/* -- */
|
||||
|
||||
public function setForm(string $context, string $form, array $options = []): self
|
||||
{
|
||||
$this->forms[$context] = $form;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getForm(string $context): string
|
||||
{
|
||||
return $this->forms[$context];
|
||||
}
|
||||
|
||||
public function setFormOptions(string $context, array $options = []): self
|
||||
{
|
||||
$this->formOptions[$context] = $options;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFormOptions(string $context): array
|
||||
{
|
||||
return $this->formOptions[$context] ?? [];
|
||||
}
|
||||
|
||||
/* -- */
|
||||
|
||||
public function setAction(string $page, string $action, bool $enabled): self
|
||||
{
|
||||
if (!isset($this->actions[$page])) {
|
||||
$this->actions[$page] = [];
|
||||
}
|
||||
|
||||
$this->actions[$page][$action] = $enabled;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAction(string $page, string $action, bool $default = true)
|
||||
{
|
||||
return $this->actions[$page][$action] ?? $default;
|
||||
}
|
||||
|
||||
/* -- */
|
||||
|
||||
public function setActionTitle(string $page, string $action, string $title): self
|
||||
{
|
||||
if (!isset($this->actionTitles[$page])) {
|
||||
$this->actionTitles[$page] = [];
|
||||
}
|
||||
|
||||
$this->actions[$page][$action] = $title;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getActionTitle(string $page, string $action, ?string $default = null): ?string
|
||||
{
|
||||
return $this->actionTitles[$page][$action] ?? $default;
|
||||
}
|
||||
|
||||
/* -- */
|
||||
|
||||
public function setView(string $context, string $view): self
|
||||
{
|
||||
$this->views[$context] = $view;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getView(string $context, ?string $default = null)
|
||||
{
|
||||
if (null === $default) {
|
||||
$default = sprintf('@Core/admin/crud/%s.html.twig', $context);
|
||||
}
|
||||
|
||||
return $this->views[$context] ?? $default;
|
||||
}
|
||||
|
||||
/* -- */
|
||||
|
||||
public function setField(string $context, string $label, string $field, array $options): self
|
||||
{
|
||||
if (!isset($this->fields[$context])) {
|
||||
$this->fields[$context] = [];
|
||||
}
|
||||
|
||||
$this->fields[$context][$label] = [
|
||||
'field' => $field,
|
||||
'options' => $options,
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFields(string $context): array
|
||||
{
|
||||
return $this->fields[$context] ?? [];
|
||||
}
|
||||
}
|
12
core/Crud/Exception/CrudConfigurationException.php
Normal file
12
core/Crud/Exception/CrudConfigurationException.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Crud\Exception;
|
||||
|
||||
/**
|
||||
* class CrudConfigurationException.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class CrudConfigurationException extends \Exception
|
||||
{
|
||||
}
|
29
core/Crud/Field/ButtonField.php
Normal file
29
core/Crud/Field/ButtonField.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Crud\Field;
|
||||
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* class ButtonField.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class ButtonField extends Field
|
||||
{
|
||||
public function configureOptions(OptionsResolver $resolver): OptionsResolver
|
||||
{
|
||||
parent::configureOptions($resolver);
|
||||
|
||||
$resolver->setDefaults([
|
||||
'view' => '@Core/admin/crud/field/button.html.twig',
|
||||
'button_attr' => [],
|
||||
'button_tag' => 'button',
|
||||
]);
|
||||
|
||||
$resolver->setAllowedTypes('button_attr', ['array']);
|
||||
$resolver->setAllowedTypes('button_tag', ['string']);
|
||||
|
||||
return $resolver;
|
||||
}
|
||||
}
|
25
core/Crud/Field/DateField.php
Normal file
25
core/Crud/Field/DateField.php
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Crud\Field;
|
||||
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* class DateField.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class DateField extends Field
|
||||
{
|
||||
public function configureOptions(OptionsResolver $resolver): OptionsResolver
|
||||
{
|
||||
parent::configureOptions($resolver);
|
||||
|
||||
$resolver->setDefaults([
|
||||
'view' => '@Core/admin/crud/field/date.html.twig',
|
||||
'format' => 'Y-m-d',
|
||||
]);
|
||||
|
||||
return $resolver;
|
||||
}
|
||||
}
|
25
core/Crud/Field/DatetimeField.php
Normal file
25
core/Crud/Field/DatetimeField.php
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Crud\Field;
|
||||
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* class DatetimeField.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class DatetimeField extends Field
|
||||
{
|
||||
public function configureOptions(OptionsResolver $resolver): OptionsResolver
|
||||
{
|
||||
parent::configureOptions($resolver);
|
||||
|
||||
$resolver->setDefaults([
|
||||
'view' => '@Core/admin/crud/field/date.html.twig',
|
||||
'format' => 'Y-m-d H:i:s',
|
||||
]);
|
||||
|
||||
return $resolver;
|
||||
}
|
||||
}
|
64
core/Crud/Field/Field.php
Normal file
64
core/Crud/Field/Field.php
Normal file
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Crud\Field;
|
||||
|
||||
use App\Core\Crud\Exception\CrudConfigurationException;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||
use Twig\Environment;
|
||||
|
||||
/**
|
||||
* class Field.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
abstract class Field
|
||||
{
|
||||
public function buildView(Environment $twig, $entity, array $options)
|
||||
{
|
||||
return $twig->render($this->getView($options), [
|
||||
'value' => $this->getValue($entity, $options),
|
||||
'options' => $options,
|
||||
]);
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): OptionsResolver
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'property' => null,
|
||||
'property_builder' => null,
|
||||
'view' => null,
|
||||
'attr' => [],
|
||||
]);
|
||||
|
||||
$resolver->setRequired('view');
|
||||
$resolver->setAllowedTypes('property', ['null', 'string']);
|
||||
$resolver->setAllowedTypes('view', 'string');
|
||||
$resolver->setAllowedTypes('attr', 'array');
|
||||
$resolver->setAllowedTypes('property_builder', ['null', 'callable']);
|
||||
|
||||
return $resolver;
|
||||
}
|
||||
|
||||
protected function getValue($entity, array $options)
|
||||
{
|
||||
$propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()
|
||||
->getPropertyAccessor()
|
||||
;
|
||||
|
||||
if (null !== $options['property']) {
|
||||
$value = $propertyAccessor->getValue($entity, $options['property']);
|
||||
} elseif (null !== $options['property_builder']) {
|
||||
$value = call_user_func($options['property_builder'], $entity, $options);
|
||||
} else {
|
||||
throw new CrudConfigurationException('Unable to get the value. One of "property" and "property_builder" is required.');
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
protected function getView(array $options)
|
||||
{
|
||||
return $options['view'];
|
||||
}
|
||||
}
|
24
core/Crud/Field/TextField.php
Normal file
24
core/Crud/Field/TextField.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Crud\Field;
|
||||
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* class TextField.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class TextField extends Field
|
||||
{
|
||||
public function configureOptions(OptionsResolver $resolver): OptionsResolver
|
||||
{
|
||||
parent::configureOptions($resolver);
|
||||
|
||||
$resolver->setDefaults([
|
||||
'view' => '@Core/admin/crud/field/text.html.twig',
|
||||
]);
|
||||
|
||||
return $resolver;
|
||||
}
|
||||
}
|
6
core/Resources/views/admin/crud/_form.html.twig
Normal file
6
core/Resources/views/admin/crud/_form.html.twig
Normal file
|
@ -0,0 +1,6 @@
|
|||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
{{ form_widget(form) }}
|
||||
</div>
|
||||
</div>
|
||||
|
1
core/Resources/views/admin/crud/_show.html.twig
Normal file
1
core/Resources/views/admin/crud/_show.html.twig
Normal file
|
@ -0,0 +1 @@
|
|||
<p>{{ '{__toString}'|build_string(entity) }}</p>
|
88
core/Resources/views/admin/crud/edit.html.twig
Normal file
88
core/Resources/views/admin/crud/edit.html.twig
Normal file
|
@ -0,0 +1,88 @@
|
|||
{% extends '@Core/admin/layout.html.twig' %}
|
||||
|
||||
{% block title %}{{ configuration.pageTitle('edit')|trans|build_string(entity) }} - {{ parent() }}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
{% block header %}
|
||||
<div class="bg-light pl-5 pr-4 pt-5 pb-5">
|
||||
<div class="crud-header">
|
||||
{% block header_title %}
|
||||
<h1 class="crud-header-title">{{ configuration.pageTitle('edit')|trans|build_string(entity) }}</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block header_actions %}
|
||||
<div class="crud-header-actions">
|
||||
<div class="btn-group">
|
||||
{% if configuration.action('edit', 'back', true) %}
|
||||
<a href="{{ path(configuration.pageRoute('index')) }}" class="btn btn-light">
|
||||
<span class="fa fa-list pr-1"></span>
|
||||
{{ configuration.actionTitle('edit', 'back', 'Back to the list')|trans }}
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% if configuration.action('edit', 'show', true) %}
|
||||
<a href="{{ path(configuration.pageRoute('show'), {entity: entity.id}) }}" class="btn btn-secondary">
|
||||
<span class="fa fa-eye pr-1"></span>
|
||||
{{ configuration.actionTitle('edit', 'show', 'Show')|trans|build_string(entity) }}
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
<button type="submit" form="form-main" class="btn btn-primary">
|
||||
<span class="fa fa-save pr-1"></span>
|
||||
{{ configuration.actionTitle('edit', 'save', 'Save')|trans|build_string(entity) }}
|
||||
</button>
|
||||
|
||||
{% block dropdownMenu %}
|
||||
{% set menu = '' %}
|
||||
|
||||
{% if configuration.action('edit', 'delete', true) %}
|
||||
{% set item %}
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<button type="submit" form="form-delete" class="dropdown-item">
|
||||
{{ configuration.actionTitle('edit', 'delete', 'Delete')|trans|build_string(entity) }}
|
||||
</button>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{% set menu = menu ~ item %}
|
||||
{% endif %}
|
||||
|
||||
{% if menu %}
|
||||
<button type="button" class="btn btn-white dropdown-toggle dropdown-toggle-hide-after" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="font-weight-bold">
|
||||
⋅⋅⋅
|
||||
</span>
|
||||
</button>
|
||||
|
||||
{{ menu|raw }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block form %}
|
||||
<form action="{{ app.request.uri }}" method="post" id="form-main" enctype="multipart/form-data">
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active">
|
||||
<div class="tab-form">
|
||||
{{ include(configuration.view('editForm', '@Core/admin/crud/_form.html.twig')) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ form_rest(form) }}
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
{% if configuration.action('edit', 'delete', true) %}
|
||||
<form method="post" action="{{ path(configuration.pageRoute('delete'), {entity: entity.id}) }}" id="form-delete" data-form-confirm>
|
||||
<input type="hidden" name="_method" value="DELETE">
|
||||
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ entity.id) }}">
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endblock %}
|
1
core/Resources/views/admin/crud/field/button.html.twig
Normal file
1
core/Resources/views/admin/crud/field/button.html.twig
Normal file
|
@ -0,0 +1 @@
|
|||
<{{ options.button_tag }} {% for k, v in options.button_attr %}{{ k }}="{{ v }}"{% endfor %}>{{ value }}</{{ options.button_tag }}>
|
1
core/Resources/views/admin/crud/field/date.html.twig
Normal file
1
core/Resources/views/admin/crud/field/date.html.twig
Normal file
|
@ -0,0 +1 @@
|
|||
{{ value|date(options.format) }}
|
1
core/Resources/views/admin/crud/field/text.html.twig
Normal file
1
core/Resources/views/admin/crud/field/text.html.twig
Normal file
|
@ -0,0 +1 @@
|
|||
{{ value }}
|
112
core/Resources/views/admin/crud/index.html.twig
Normal file
112
core/Resources/views/admin/crud/index.html.twig
Normal file
|
@ -0,0 +1,112 @@
|
|||
{% extends '@Core/admin/layout.html.twig' %}
|
||||
|
||||
{% block title %}{{ configuration.pageTitle('index')|trans }} - {{ parent() }}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
{% block header %}
|
||||
<div class="bg-light pl-5 pr-4 pt-5 {% if pager.paginationData.pageCount < 2 %}pb-5{% endif %}">
|
||||
<div class="crud-header">
|
||||
{% block header_title %}
|
||||
<h1 class="crud-header-title">{{ configuration.pageTitle('index')|trans }}</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block header_actions %}
|
||||
<div class="crud-header-actions">
|
||||
<div class="btn-group">
|
||||
{% if configuration.action('index', 'new', true) %}
|
||||
<a href="{{ path(configuration.pageRoute('new')) }}" class="btn btn-primary">
|
||||
<span class="fa fa-plus pr-1"></span>
|
||||
{{ configuration.actionTitle('index', 'new', 'New')|trans }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
||||
{{ knp_pagination_render(pager) }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block list %}
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
{% block list_header %}
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
{% for label, config in configuration.fields('index') %}
|
||||
{% set attr = config.options.attr is defined ? config.options.attr : [] %}
|
||||
|
||||
<th {% for key, value in attr %}{{ key }}="{{ value }}"{% endfor %}>
|
||||
{{ label|trans }}
|
||||
</th>
|
||||
{% endfor %}
|
||||
<th class="col-2 miw-100 text-right">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% endblock %}
|
||||
|
||||
{% block list_items %}
|
||||
<tbody>
|
||||
{% for item in pager %}
|
||||
{% block list_item %}
|
||||
<tr data-dblclick="">
|
||||
{% for config in configuration.fields('index') %}
|
||||
{% set attr = config.options.attr is defined ? config.options.attr : [] %}
|
||||
{% set action = config.options.action is defined ? config.options.action : null %}
|
||||
|
||||
<td {% for key, value in attr %}{{ key }}="{{ value }}"{% endfor %}>
|
||||
{% if action == 'show' %}
|
||||
<a href="{{ path(configuration.pageRoute('show'), {entity: item.id}) }}">
|
||||
{{ render_field(item, config) }}
|
||||
</a>
|
||||
{% elseif action == 'edit' %}
|
||||
<a href="{{ path(configuration.pageRoute('edit'), {entity: item.id}) }}">
|
||||
{{ render_field(item, config) }}
|
||||
</a>
|
||||
{% else %}
|
||||
{{ render_field(item, config) }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
|
||||
{% if configuration.action('index', 'edit', true) or configuration.action('index', 'delete', true) %}
|
||||
<td class="col-2 miw-100 text-right">
|
||||
{% if configuration.action('index', 'edit', true) %}
|
||||
<a href="{{ path(configuration.pageRoute('edit'), {entity: item.id}) }}" class="btn btn-sm btn-primary mr-1">
|
||||
<span class="fa fa-edit"></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% if configuration.action('index', 'delete', true) %}
|
||||
<button type="submit" form="form-delete-{{ item.id }}" class="btn btn-sm btn-danger">
|
||||
<span class="fa fa-trash"></span>
|
||||
</button>
|
||||
|
||||
<form method="post" action="{{ path(configuration.pageRoute('delete'), {entity: item.id}) }}" id="form-delete-{{ item.id }}" data-form-confirm>
|
||||
<input type="hidden" name="_method" value="DELETE">
|
||||
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ item.id) }}">
|
||||
</form>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endblock %}
|
||||
{% else %}
|
||||
<tr>
|
||||
<td class="col-12 text-center p-4 text-black-50">
|
||||
<div class="display-1">
|
||||
<span class="fa fa-search"></span>
|
||||
</div>
|
||||
<div class="display-5 mt-3">
|
||||
Aucun résultat
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
{% endblock %}
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
47
core/Resources/views/admin/crud/new.html.twig
Normal file
47
core/Resources/views/admin/crud/new.html.twig
Normal file
|
@ -0,0 +1,47 @@
|
|||
{% extends '@Core/admin/layout.html.twig' %}
|
||||
|
||||
{% block title %}{{ configuration.pageTitle('new')|trans|build_string(entity) }} - {{ parent() }}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
{% block header %}
|
||||
<div class="bg-light pl-5 pr-4 pt-5 pb-5">
|
||||
<div class="crud-header">
|
||||
{% block header_title %}
|
||||
<h1 class="crud-header-title">{{ configuration.pageTitle('new')|trans|build_string(entity) }}</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block header_actions %}
|
||||
<div class="crud-header-actions">
|
||||
<div class="btn-group">
|
||||
{% if configuration.action('new', 'back', true) %}
|
||||
<a href="{{ path(configuration.pageRoute('index')) }}" class="btn btn-light">
|
||||
<span class="fa fa-list pr-1"></span>
|
||||
{{ configuration.actionTitle('new', 'back', 'Back to the list')|trans }}
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
<button type="submit" form="form-main" class="btn btn-primary">
|
||||
<span class="fa fa-save pr-1"></span>
|
||||
{{ configuration.actionTitle('new', 'save', 'Save')|trans|build_string(entity) }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block form %}
|
||||
<form action="{{ app.request.uri }}" method="post" id="form-main" enctype="multipart/form-data">
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active">
|
||||
<div class="tab-form">
|
||||
{{ include(configuration.view('newForm', '@Core/admin/crud/_form.html.twig')) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ form_rest(form) }}
|
||||
</form>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
44
core/Resources/views/admin/crud/show.html.twig
Normal file
44
core/Resources/views/admin/crud/show.html.twig
Normal file
|
@ -0,0 +1,44 @@
|
|||
{% extends '@Core/admin/layout.html.twig' %}
|
||||
|
||||
{% block title %}{{ configuration.pageTitle('show')|trans|build_string(entity) }} - {{ parent() }}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
{% block header %}
|
||||
<div class="bg-light pl-5 pr-4 pt-5 pb-5">
|
||||
<div class="crud-header">
|
||||
{% block header_title %}
|
||||
<h1 class="crud-header-title">{{ configuration.pageTitle('show')|trans|build_string(entity) }}</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block header_actions %}
|
||||
<div class="crud-header-actions">
|
||||
<div class="btn-group">
|
||||
{% if configuration.action('show', 'back', true) %}
|
||||
<a href="{{ path(configuration.pageRoute('index')) }}" class="btn btn-light">
|
||||
<span class="fa fa-list pr-1"></span>
|
||||
{{ configuration.actionTitle('show', 'back', 'Back to the list')|trans }}
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% if configuration.action('show', 'edit', true) %}
|
||||
<a href="{{ path(configuration.pageRoute('edit'), {entity: entity.id}) }}" class="btn btn-primary">
|
||||
<span class="fa fa-edit pr-1"></span>
|
||||
|
||||
{{ configuration.actionTitle('show', 'edit', 'Edit')|trans|build_string(entity) }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block show %}
|
||||
<div class="row">
|
||||
<div class="col-md-12 p-3">
|
||||
{{ include(configuration.view('showEntity', '@Core/admin/crud/_show.html.twig')) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
44
core/Twig/Extension/CrudExtension.php
Normal file
44
core/Twig/Extension/CrudExtension.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Twig\Extension;
|
||||
|
||||
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessor;
|
||||
use Twig\Environment;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFunction;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class CrudExtension extends AbstractExtension
|
||||
{
|
||||
protected PropertyAccessor $propertyAccessor;
|
||||
protected Environment $twig;
|
||||
|
||||
public function __construct(Environment $twig)
|
||||
{
|
||||
$this->propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()
|
||||
->getPropertyAccessor()
|
||||
;
|
||||
|
||||
$this->twig = $twig;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFunctions()
|
||||
{
|
||||
return [
|
||||
new TwigFunction('render_field', [$this, 'renderField'], ['is_safe' => ['html']]),
|
||||
];
|
||||
}
|
||||
|
||||
public function renderField($entity, array $config): string
|
||||
{
|
||||
$field = $config['field'];
|
||||
$instance = new $field;
|
||||
$resolver = $instance->configureOptions(new OptionsResolver());
|
||||
|
||||
return $instance->buildView($this->twig, $entity, $resolver->resolve($config['options']));
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue