backports murph-skeleton
This commit is contained in:
parent
61f0324e2d
commit
1e542c185c
|
@ -46,6 +46,9 @@ abstract class CrudController extends AdminController
|
||||||
protected function doNew(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeCreate = null): Response
|
protected function doNew(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeCreate = null): Response
|
||||||
{
|
{
|
||||||
$configuration = $this->getConfiguration();
|
$configuration = $this->getConfiguration();
|
||||||
|
|
||||||
|
$this->prepareEntity($entity);
|
||||||
|
|
||||||
$form = $this->createForm($configuration->getForm('new'), $entity);
|
$form = $this->createForm($configuration->getForm('new'), $entity);
|
||||||
|
|
||||||
if ($request->isMethod('POST')) {
|
if ($request->isMethod('POST')) {
|
||||||
|
@ -86,6 +89,9 @@ abstract class CrudController extends AdminController
|
||||||
protected function doEdit(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeUpdate = null): Response
|
protected function doEdit(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeUpdate = null): Response
|
||||||
{
|
{
|
||||||
$configuration = $this->getConfiguration();
|
$configuration = $this->getConfiguration();
|
||||||
|
|
||||||
|
$this->prepareEntity($entity);
|
||||||
|
|
||||||
$form = $this->createForm($configuration->getForm('edit'), $entity);
|
$form = $this->createForm($configuration->getForm('edit'), $entity);
|
||||||
|
|
||||||
if ($request->isMethod('POST')) {
|
if ($request->isMethod('POST')) {
|
||||||
|
@ -181,4 +187,15 @@ abstract class CrudController extends AdminController
|
||||||
$session->set($form->getName(), $filters);
|
$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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@ class CrudConfiguration
|
||||||
protected array $viewDatas = [];
|
protected array $viewDatas = [];
|
||||||
protected array $fields = [];
|
protected array $fields = [];
|
||||||
protected array $maxPerPage = [];
|
protected array $maxPerPage = [];
|
||||||
|
protected array $locales = [];
|
||||||
|
protected ?string $defaultLocale = null;
|
||||||
|
|
||||||
protected static $self;
|
protected static $self;
|
||||||
|
|
||||||
|
@ -198,4 +200,29 @@ class CrudConfiguration
|
||||||
{
|
{
|
||||||
return $this->maxPerPage[$page] ?? $default;
|
return $this->maxPerPage[$page] ?? $default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -- */
|
||||||
|
|
||||||
|
public function setI18n(array $locales, string $defaultLocale): self
|
||||||
|
{
|
||||||
|
$this->locales = $locales;
|
||||||
|
$this->defaultLocale = $defaultLocale;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLocales(): array
|
||||||
|
{
|
||||||
|
return $this->locales;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDefaultLocale(): ?string
|
||||||
|
{
|
||||||
|
return $this->defaultLocale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isI18n(): bool
|
||||||
|
{
|
||||||
|
return !empty($this->locales);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
namespace App\Core\Crud\Field;
|
namespace App\Core\Crud\Field;
|
||||||
|
|
||||||
use App\Core\Crud\Exception\CrudConfigurationException;
|
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
|
||||||
use Symfony\Component\PropertyAccess\PropertyAccess;
|
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||||
use Twig\Environment;
|
use Twig\Environment;
|
||||||
|
|
||||||
|
@ -14,11 +14,11 @@ use Twig\Environment;
|
||||||
*/
|
*/
|
||||||
abstract class Field
|
abstract class Field
|
||||||
{
|
{
|
||||||
public function buildView(Environment $twig, $entity, array $options)
|
public function buildView(Environment $twig, $entity, array $options, ?string $locale = null)
|
||||||
{
|
{
|
||||||
return $twig->render($this->getView($options), [
|
return $twig->render($this->getView($options), [
|
||||||
'entity' => $entity,
|
'entity' => $entity,
|
||||||
'value' => $this->getValue($entity, $options),
|
'value' => $this->getValue($entity, $options, $locale),
|
||||||
'options' => $options,
|
'options' => $options,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -43,12 +43,20 @@ abstract class Field
|
||||||
return $resolver;
|
return $resolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getValue($entity, array $options)
|
protected function getValue($entity, array $options, ?string $locale = null)
|
||||||
{
|
{
|
||||||
if (null !== $options['property']) {
|
if (null !== $options['property']) {
|
||||||
$propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()->getPropertyAccessor();
|
$propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()->getPropertyAccessor();
|
||||||
|
|
||||||
$value = $propertyAccessor->getValue($entity, $options['property']);
|
try {
|
||||||
|
$value = $propertyAccessor->getValue($entity, $options['property']);
|
||||||
|
} catch (NoSuchPropertyException $e) {
|
||||||
|
if (null !== $locale) {
|
||||||
|
$value = $propertyAccessor->getValue($entity->translate($locale), $options['property']);
|
||||||
|
} else {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
} elseif (null !== $options['property_builder']) {
|
} elseif (null !== $options['property_builder']) {
|
||||||
$value = call_user_func($options['property_builder'], $entity, $options);
|
$value = call_user_func($options['property_builder'], $entity, $options);
|
||||||
} else {
|
} else {
|
||||||
|
|
27
core/Crud/Field/ImageField.php
Normal file
27
core/Crud/Field/ImageField.php
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Core\Crud\Field;
|
||||||
|
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* class ImageField.
|
||||||
|
*
|
||||||
|
* @author Simon Vieille <simon@deblan.fr>
|
||||||
|
*/
|
||||||
|
class ImageField extends Field
|
||||||
|
{
|
||||||
|
public function configureOptions(OptionsResolver $resolver): OptionsResolver
|
||||||
|
{
|
||||||
|
parent::configureOptions($resolver);
|
||||||
|
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'view' => '@Core/admin/crud/field/image.html.twig',
|
||||||
|
'image_attr' => [],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$resolver->setAllowedTypes('image_attr', ['array']);
|
||||||
|
|
||||||
|
return $resolver;
|
||||||
|
}
|
||||||
|
}
|
20
core/Manager/TranslatableEntityManager.php
Normal file
20
core/Manager/TranslatableEntityManager.php
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Core\Manager;
|
||||||
|
|
||||||
|
use App\Core\Entity\EntityInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* class TranslatableEntityManager.
|
||||||
|
*
|
||||||
|
* @author Simon Vieille <simon@deblan.fr>
|
||||||
|
*/
|
||||||
|
class TranslatableEntityManager extends EntityManager
|
||||||
|
{
|
||||||
|
protected function persist(EntityInterface $entity)
|
||||||
|
{
|
||||||
|
$this->entityManager->persist($entity);
|
||||||
|
$entity->mergeNewTranslations();
|
||||||
|
$this->flush();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,12 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
{{ form_widget(form) }}
|
{% for item in form %}
|
||||||
|
{% if configuration.isI18n and item.vars.name == 'translations' %}
|
||||||
|
{{ include(configuration.view('form_translations', '@Core/admin/crud/_form_translations.html.twig')) }}
|
||||||
|
{% else %}
|
||||||
|
{{ include(configuration.view('form_widget', '@Core/admin/crud/_form_widget.html.twig')) }}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
21
core/Resources/views/admin/crud/_form_translations.html.twig
Normal file
21
core/Resources/views/admin/crud/_form_translations.html.twig
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<ul class="nav nav-pills mt-3 mb-3 p-0">
|
||||||
|
{% for locale in configuration.locales %}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link {% if loop.first %}active{% endif %}" data-toggle="tab" href="#form-locale-{{ loop.index }}">
|
||||||
|
<span class="flag-icon flag-icon-{{ locale }}"></span>
|
||||||
|
|
||||||
|
{{ locale|upper|trans }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="tab-content">
|
||||||
|
{% for locale in configuration.locales %}
|
||||||
|
<div class="tab-pane {% if loop.first %}show active{% endif %}" id="form-locale-{{ loop.index }}">
|
||||||
|
{% for item in item.children[locale] %}
|
||||||
|
{{ include(configuration.view('form_widget', '@Core/admin/crud/_form_widget.html.twig')) }}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
1
core/Resources/views/admin/crud/_form_widget.html.twig
Normal file
1
core/Resources/views/admin/crud/_form_widget.html.twig
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{{ form_row(item) }}
|
|
@ -70,7 +70,7 @@
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div class="tab-pane active">
|
<div class="tab-pane active">
|
||||||
<div class="tab-form">
|
<div class="tab-form">
|
||||||
{{ include(configuration.view('editForm', '@Core/admin/crud/_form.html.twig')) }}
|
{{ include(configuration.view('form', '@Core/admin/crud/_form.html.twig')) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
1
core/Resources/views/admin/crud/field/image.html.twig
Normal file
1
core/Resources/views/admin/crud/field/image.html.twig
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<img {% for k, v in options.image_attr %}{{ k }}="{{ v }}"{% endfor %} src="{{ asset(value) }}">
|
|
@ -65,7 +65,15 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for item in pager %}
|
{% for item in pager %}
|
||||||
{% block list_item %}
|
{% block list_item %}
|
||||||
<tr data-dblclick="">
|
{%- set dbClick %}
|
||||||
|
{% if configuration.action('index', 'show', true) %}
|
||||||
|
{{ path(configuration.pageRoute('show'), {entity: item.id}) }}
|
||||||
|
{% elseif configuration.action('index', 'edit', true) %}
|
||||||
|
{{ path(configuration.pageRoute('edit'), {entity: item.id}) }}
|
||||||
|
{% endif %}
|
||||||
|
{% endset -%}
|
||||||
|
|
||||||
|
<tr data-dblclick="{{ dbClick }}">
|
||||||
{% for config in configuration.fields('index') %}
|
{% for config in configuration.fields('index') %}
|
||||||
{% 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 %}
|
||||||
|
@ -73,14 +81,14 @@
|
||||||
<td {% for key, value in attr %}{{ key }}="{{ value }}"{% endfor %}>
|
<td {% for key, value in attr %}{{ key }}="{{ value }}"{% endfor %}>
|
||||||
{% if action == 'show' %}
|
{% if action == 'show' %}
|
||||||
<a href="{{ path(configuration.pageRoute('show'), {entity: item.id}) }}">
|
<a href="{{ path(configuration.pageRoute('show'), {entity: item.id}) }}">
|
||||||
{{ render_field(item, config) }}
|
{{ render_field(item, config, configuration.defaultLocale) }}
|
||||||
</a>
|
</a>
|
||||||
{% elseif action == 'edit' %}
|
{% elseif action == 'edit' %}
|
||||||
<a href="{{ path(configuration.pageRoute('edit'), {entity: item.id}) }}">
|
<a href="{{ path(configuration.pageRoute('edit'), {entity: item.id}) }}">
|
||||||
{{ render_field(item, config) }}
|
{{ render_field(item, config, configuration.defaultLocale) }}
|
||||||
</a>
|
</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ render_field(item, config) }}
|
{{ render_field(item, config, configuration.defaultLocale) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div class="tab-pane active">
|
<div class="tab-pane active">
|
||||||
<div class="tab-form">
|
<div class="tab-form">
|
||||||
{{ include(configuration.view('newForm', '@Core/admin/crud/_form.html.twig')) }}
|
{{ include(configuration.view('form', '@Core/admin/crud/_form.html.twig')) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
{% block show %}
|
{% block show %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
{{ include(configuration.view('showEntity', '@Core/admin/crud/_show.html.twig')) }}
|
{{ include(configuration.view('show_entity', '@Core/admin/crud/_show.html.twig')) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -33,12 +33,12 @@ class CrudExtension extends AbstractExtension
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function renderField($entity, array $config): string
|
public function renderField($entity, array $config, ?string $locale = null): string
|
||||||
{
|
{
|
||||||
$field = $config['field'];
|
$field = $config['field'];
|
||||||
$instance = new $field();
|
$instance = new $field();
|
||||||
$resolver = $instance->configureOptions(new OptionsResolver());
|
$resolver = $instance->configureOptions(new OptionsResolver());
|
||||||
|
|
||||||
return $instance->buildView($this->twig, $entity, $resolver->resolve($config['options']));
|
return $instance->buildView($this->twig, $entity, $resolver->resolve($config['options']), $locale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue