add translations in CRUD
This commit is contained in:
parent
70759a2d50
commit
6a9830a494
|
@ -13,6 +13,7 @@ $pagination-active-bg: #343a40;
|
|||
@import "~choices.js/src/styles/choices.scss";
|
||||
@import "~bootstrap/scss/bootstrap.scss";
|
||||
@import "~@fortawesome/fontawesome-free/css/all.css";
|
||||
@import "~flag-icon-css/sass/flag-icon.scss";
|
||||
|
||||
@for $i from 1 through 100 {
|
||||
.miw-#{$i*5} {
|
||||
|
@ -20,6 +21,10 @@ $pagination-active-bg: #343a40;
|
|||
}
|
||||
}
|
||||
|
||||
.flag-icon-en {
|
||||
background-image: url(~flag-icon-css/flags/4x3/gb.svg);
|
||||
}
|
||||
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
"doctrine/doctrine-bundle": "^2.2",
|
||||
"doctrine/doctrine-migrations-bundle": "^3.0",
|
||||
"doctrine/orm": "^2.8",
|
||||
"knplabs/doctrine-behaviors": "^2.2",
|
||||
"knplabs/knp-paginator-bundle": "^5.4",
|
||||
"phpdocumentor/reflection-docblock": "^5.2",
|
||||
"scheb/2fa-google-authenticator": "^5.7",
|
||||
|
|
|
@ -19,4 +19,5 @@ return [
|
|||
Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle::class => ['all' => true],
|
||||
App\Core\Bundle\CoreBundle::class => ['all' => true],
|
||||
App\Bundle\AppBundle::class => ['all' => true],
|
||||
Knp\DoctrineBehaviors\DoctrineBehaviorsBundle::class => ['all' => true],
|
||||
];
|
||||
|
|
|
@ -46,6 +46,9 @@ abstract class CrudController extends AdminController
|
|||
protected function doNew(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeCreate = null): Response
|
||||
{
|
||||
$configuration = $this->getConfiguration();
|
||||
|
||||
$this->prepareEntity($entity);
|
||||
|
||||
$form = $this->createForm($configuration->getForm('new'), $entity);
|
||||
|
||||
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
|
||||
{
|
||||
$configuration = $this->getConfiguration();
|
||||
|
||||
$this->prepareEntity($entity);
|
||||
|
||||
$form = $this->createForm($configuration->getForm('edit'), $entity);
|
||||
|
||||
if ($request->isMethod('POST')) {
|
||||
|
@ -181,4 +187,15 @@ abstract class CrudController extends AdminController
|
|||
$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 $fields = [];
|
||||
protected array $maxPerPage = [];
|
||||
protected array $locales = [];
|
||||
protected ?string $defaultLocale = null;
|
||||
|
||||
protected static $self;
|
||||
|
||||
|
@ -198,4 +200,29 @@ class CrudConfiguration
|
|||
{
|
||||
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;
|
||||
|
||||
use App\Core\Crud\Exception\CrudConfigurationException;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||
use Twig\Environment;
|
||||
|
||||
|
@ -14,11 +14,11 @@ use Twig\Environment;
|
|||
*/
|
||||
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), [
|
||||
'entity' => $entity,
|
||||
'value' => $this->getValue($entity, $options),
|
||||
'value' => $this->getValue($entity, $options, $locale),
|
||||
'options' => $options,
|
||||
]);
|
||||
}
|
||||
|
@ -43,12 +43,20 @@ abstract class Field
|
|||
return $resolver;
|
||||
}
|
||||
|
||||
protected function getValue($entity, array $options)
|
||||
protected function getValue($entity, array $options, ?string $locale = null)
|
||||
{
|
||||
if (null !== $options['property']) {
|
||||
$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']) {
|
||||
$value = call_user_func($options['property_builder'], $entity, $options);
|
||||
} else {
|
||||
|
|
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="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>
|
||||
|
||||
|
|
33
core/Resources/views/admin/crud/_form_translations.html.twig
Normal file
33
core/Resources/views/admin/crud/_form_translations.html.twig
Normal file
|
@ -0,0 +1,33 @@
|
|||
{% set doRender = false %}
|
||||
|
||||
{% set render %}
|
||||
<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] %}
|
||||
{% if not item.isRendered %}
|
||||
{% set doRender = true %}
|
||||
{% endif %}
|
||||
|
||||
{{ include(configuration.view('form_widget', '@Core/admin/crud/_form_widget.html.twig')) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{% if doRender %}
|
||||
{{ render|raw }}
|
||||
{% endif %}
|
3
core/Resources/views/admin/crud/_form_widget.html.twig
Normal file
3
core/Resources/views/admin/crud/_form_widget.html.twig
Normal file
|
@ -0,0 +1,3 @@
|
|||
{% if not item.isRendered %}
|
||||
{{ form_row(item) }}
|
||||
{% endif %}
|
|
@ -70,7 +70,7 @@
|
|||
<div class="tab-content">
|
||||
<div class="tab-pane active">
|
||||
<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>
|
||||
|
|
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>
|
||||
{% for item in pager %}
|
||||
{% 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') %}
|
||||
{% set attr = config.options.attr is defined ? config.options.attr : [] %}
|
||||
{% set action = config.options.action is defined ? config.options.action : null %}
|
||||
|
@ -73,14 +81,14 @@
|
|||
<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) }}
|
||||
{{ render_field(item, config, configuration.defaultLocale) }}
|
||||
</a>
|
||||
{% elseif action == 'edit' %}
|
||||
<a href="{{ path(configuration.pageRoute('edit'), {entity: item.id}) }}">
|
||||
{{ render_field(item, config) }}
|
||||
{{ render_field(item, config, configuration.defaultLocale) }}
|
||||
</a>
|
||||
{% else %}
|
||||
{{ render_field(item, config) }}
|
||||
{{ render_field(item, config, configuration.defaultLocale) }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
<div class="tab-content">
|
||||
<div class="tab-pane active">
|
||||
<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>
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
{% block show %}
|
||||
<div class="row">
|
||||
<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>
|
||||
{% 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'];
|
||||
$instance = new $field();
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
|
10042
package-lock.json
generated
Normal file
10042
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -22,6 +22,7 @@
|
|||
"@fortawesome/fontawesome-free": "^5.11.2",
|
||||
"bootstrap": "^4.3.1",
|
||||
"choices.js": "^9.0.1",
|
||||
"flag-icon-css": "^3.5.0",
|
||||
"jquery": "^3.6.0",
|
||||
"popper.js": "^1.16.0",
|
||||
"qrcodejs": "^1.0.0",
|
||||
|
|
15
symfony.lock
15
symfony.lock
|
@ -11,6 +11,9 @@
|
|||
"bjeavons/zxcvbn-php": {
|
||||
"version": "1.2.0"
|
||||
},
|
||||
"brick/math": {
|
||||
"version": "0.9.2"
|
||||
},
|
||||
"cocur/slugify": {
|
||||
"version": "v4.0.0"
|
||||
},
|
||||
|
@ -111,6 +114,9 @@
|
|||
"khanamiryan/qrcode-detector-decoder": {
|
||||
"version": "1.0.4"
|
||||
},
|
||||
"knplabs/doctrine-behaviors": {
|
||||
"version": "2.2.0"
|
||||
},
|
||||
"knplabs/knp-components": {
|
||||
"version": "v3.0.1"
|
||||
},
|
||||
|
@ -132,6 +138,9 @@
|
|||
"myclabs/php-enum": {
|
||||
"version": "1.8.0"
|
||||
},
|
||||
"nette/utils": {
|
||||
"version": "v3.2.2"
|
||||
},
|
||||
"nikic/php-parser": {
|
||||
"version": "v4.10.4"
|
||||
},
|
||||
|
@ -162,6 +171,12 @@
|
|||
"psr/log": {
|
||||
"version": "1.1.3"
|
||||
},
|
||||
"ramsey/collection": {
|
||||
"version": "1.1.3"
|
||||
},
|
||||
"ramsey/uuid": {
|
||||
"version": "4.1.1"
|
||||
},
|
||||
"scheb/2fa-bundle": {
|
||||
"version": "5.0",
|
||||
"recipe": {
|
||||
|
|
|
@ -2747,6 +2747,11 @@ find-up@^4.0.0:
|
|||
locate-path "^5.0.0"
|
||||
path-exists "^4.0.0"
|
||||
|
||||
flag-icon-css@^3.5.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/flag-icon-css/-/flag-icon-css-3.5.0.tgz#430747d5cb91e60babf85494de99173c16dc7cf2"
|
||||
integrity sha512-pgJnJLrtb0tcDgU1fzGaQXmR8h++nXvILJ+r5SmOXaaL/2pocunQo2a8TAXhjQnBpRLPtZ1KCz/TYpqeNuE2ew==
|
||||
|
||||
follow-redirects@^1.0.0:
|
||||
version "1.13.3"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267"
|
||||
|
|
Loading…
Reference in a new issue