Easyadminbundle: Is there anything new to use translatable entities?

Created on 4 May 2017  ·  19Comments  ·  Source: EasyCorp/EasyAdminBundle

Hi, Exists one issue in 2016 (#865) but is there anything new to use them?

Most helpful comment

While preparing to upgrade to SF4 and Flex I had a go at this:

Integrating KnpLabs/DoctrineBehaviors and A2lixTranslationFormBundle

Install the 2 Bundles as per their documentation:

KnpLabs/DoctrineBehaviors

A2lixTranslationFormBundle

Translatable entity

Add a __get function to your translatable entities:

/**
 * @ORM\Entity()
 */
class Article
{

    use ORMBehaviors\Translatable\Translatable;

    public function __call($method, $arguments)
    {
        $method = ('get' === substr($method, 0, 3) || 'set' === substr($method, 0, 3)) ? $method : 'get'. ucfirst($method);

        return $this->proxyCurrentLocaleTranslation($method, $arguments);
    }

    public function __get($name)
    {
        $method = 'get'. ucfirst($name);
        $arguments = [];
        return $this->proxyCurrentLocaleTranslation($method, $arguments);
    }

The __get function will be called by EasyAdmin and so you can reference the fields in the show and list views.

Adjusting your EasyAdmin configuration

Nothing daunting here:

easy_admin:
    entities:
        Article:
            class: App\Entity\Article
            label: 'Artikel'
            list:
                fields:
                    - { property: 'title' } #A Translatable field
                    - { property: 'public' }
            form:
                fields:
                    - { property: 'translations', label: false, type: A2lix\TranslationFormBundle\Form\Type\TranslationsType ,
                        type_options: {
                            default_locale: '%locale%',
                            fields: {
                                title: {field_type: 'Symfony\Component\Form\Extension\Core\Type\TextType' },
                                description: {field_type: 'Ivory\CKEditorBundle\Form\Type\CKEditorType' }
                                }
                        }
                      }
                    - { property: 'public' }

That's it, you now have fully translatable entities integrated into EasyAdmin.

The type_options are optional, and only needed if you want to adjust to your needs, like in this example integrating a Wysiwig editor.

For SF4 I had to use a fork fsi-open/IvoryCKEditorBundle. Resources/public is empty in that fork, so I got that from the original.

All 19 comments

@mazetuski

I use translatable behavior Doctrine extension with EasyAdminBundle but in a very simple way : changing the locale (with a menu integrated to EasyAdmin) permits to change the translation. I have a mechanism to impose that any new article (my app is a kind of blog) has to be first filled in the default locale (en) and then can be translated in other locales (fr).

I don't know if it is enough for you or if you want something more sophisticated as described in #865.

You can have look to my repo. If you do, please chose the branch fix_sensiolabs_insight_violations (work in progress).

I'm afraid there's no news about this and I can't work on this. I know nothing about translatable entities, except that people have a lot of issues working with them. My guess is that this is something that Doctrine should fix or improve and then we (Symfony, bundles) could use them more easily. I'm really sorry.

While preparing to upgrade to SF4 and Flex I had a go at this:

Integrating KnpLabs/DoctrineBehaviors and A2lixTranslationFormBundle

Install the 2 Bundles as per their documentation:

KnpLabs/DoctrineBehaviors

A2lixTranslationFormBundle

Translatable entity

Add a __get function to your translatable entities:

/**
 * @ORM\Entity()
 */
class Article
{

    use ORMBehaviors\Translatable\Translatable;

    public function __call($method, $arguments)
    {
        $method = ('get' === substr($method, 0, 3) || 'set' === substr($method, 0, 3)) ? $method : 'get'. ucfirst($method);

        return $this->proxyCurrentLocaleTranslation($method, $arguments);
    }

    public function __get($name)
    {
        $method = 'get'. ucfirst($name);
        $arguments = [];
        return $this->proxyCurrentLocaleTranslation($method, $arguments);
    }

The __get function will be called by EasyAdmin and so you can reference the fields in the show and list views.

Adjusting your EasyAdmin configuration

Nothing daunting here:

easy_admin:
    entities:
        Article:
            class: App\Entity\Article
            label: 'Artikel'
            list:
                fields:
                    - { property: 'title' } #A Translatable field
                    - { property: 'public' }
            form:
                fields:
                    - { property: 'translations', label: false, type: A2lix\TranslationFormBundle\Form\Type\TranslationsType ,
                        type_options: {
                            default_locale: '%locale%',
                            fields: {
                                title: {field_type: 'Symfony\Component\Form\Extension\Core\Type\TextType' },
                                description: {field_type: 'Ivory\CKEditorBundle\Form\Type\CKEditorType' }
                                }
                        }
                      }
                    - { property: 'public' }

That's it, you now have fully translatable entities integrated into EasyAdmin.

The type_options are optional, and only needed if you want to adjust to your needs, like in this example integrating a Wysiwig editor.

For SF4 I had to use a fork fsi-open/IvoryCKEditorBundle. Resources/public is empty in that fork, so I got that from the original.

On SF4 I experienced some CSS small inconsistencies using this approach. For a better matching with the default EasyAdminBundle stylesheet I'd recommend to override the default template file from A2lixTranslationFormBundle into templates/bundles/A2lixTranslationFormBundle/bootstrap_4_layout.html.twig and use this markup which have already the small adjustments needed:

{% block a2lix_translations_widget %}
    {{ form_errors(form) }}

    <div class="a2lix_translations">
        <ul class="a2lix_translationsLocales nav nav-tabs" role="tablist">
        {% for translationsFields in form %}
            {% set locale = translationsFields.vars.name %}

            <li class="nav-item {% if app.request.locale == locale %}active{% endif %}">
                <a href="#" class="nav-link" data-toggle="tab" data-target=".{{ translationsFields.vars.id }}_a2lix_translationsFields-{{ locale }}" role="tab">
                    {{ translationsFields.vars.label|default(locale|humanize)|trans }}
                    {% if form.vars.default_locale == locale %}{{ '[Default]'|trans }}{% endif %}
                    {% if translationsFields.vars.required %}*{% endif %}
                </a>
            </li>
        {% endfor %}
        </ul>

        <div class="a2lix_translationsFields tab-content">
        {% for translationsFields in form %}
            {% set locale = translationsFields.vars.name %}

            <div class="{{ translationsFields.vars.id }}_a2lix_translationsFields-{{ locale }} tab-pane {% if app.request.locale == locale %}active{% endif %} {% if not form.vars.valid %}sonata-ba-field-error{% endif %}" role="tabpanel">
                {{ form_errors(translationsFields) }}
                {{ form_widget(translationsFields) }}
            </div>
        {% endfor %}
        </div>
    </div>
{% endblock %}

{% block a2lix_translationsForms_widget %}
    {{ block('a2lix_translations_widget') }}
{% endblock %}

(Do a diff between original and overrided file to see difference/changes)

@FireFoxIXI thanks for your input, i'm using SF3.4 and have implemented yur solution, but I get the following error now : The "name" field cannot be used in the "sort" option of the "list" view of the "Project" entity because it's a virtual property that is not persisted in the database.
name being a translatable property. Can you help?

Here is my entity

<?php

namespace AppBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\Validator\Constraints as Assert;
use Vich\UploaderBundle\Mapping\Annotation as Vich;

/**
 * Project
 *
 * @ORM\Table(name="project")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\ProjectRepository")
 * @Vich\Uploadable
 */
class Project
{
    use ORMBehaviors\Translatable\Translatable;

    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="city", type="string", length=255)
     * @Assert\NotBlank()
     */
    private $city;

    /**
     * @var string
     *
     * @ORM\Column(name="region", type="string", length=255, nullable=true)
     */
    private $region;

    /**
     * @var string
     *
     * @ORM\Column(name="country", type="string", length=255)
     * @Assert\Country()
     */
    private $country;

    /**
     * @var boolean
     *
     * @ORM\Column(name="isPublished", type="boolean")
     */
    private $isPublished = 0;

    /**
     * @var boolean
     *
     * @ORM\Column(name="isFocused", type="boolean")
     */
    private $isFocused = 0;

    /**
     * @var string
     *
     * @ORM\Column(type="string", length=255)
     */
    private $cover;

    /**
     * @var File
     *
     * @Vich\UploadableField(mapping="project_covers", fileNameProperty="cover")
     * @Assert\Image(
     *     maxSize="1024k",
     *     mimeTypes={"image/jpeg", "image/gif","image/png"},
     *     minWidth = 250,
     *     minHeight = 250
     * )
     */
    private $coverFile;

    /**
     * @var \DateTime
     *
     * @ORM\Column(type="datetime")
     */
    private $updatedAt;

    /**
     * @ORM\OneToMany(targetEntity="AppBundle\Entity\ProjectMedia", mappedBy="project", cascade={"persist","remove"}, orphanRemoval=true)
     */
    private $medias;

    /**
     * @ORM\ManyToMany(targetEntity="AppBundle\Entity\Tag", inversedBy="projects")
     */
    private $tags;


    /**
     * Project constructor.
     */
    public function __construct()
    {
        $this->medias = new ArrayCollection();
        $this->tags = new ArrayCollection();
    }

    /**
     * @param $method
     * @param $arguments
     * @return mixed
     */
    public function __call($method, $arguments)
    {
        $method = ('get' === substr($method, 0, 3) || 'set' === substr($method, 0, 3)) ? $method : 'get'. ucfirst($method);

        return $this->proxyCurrentLocaleTranslation($method, $arguments);
    }

    /**
     * @param $name
     * @return mixed
     */
    public function __get($name)
    {
        $method = 'get'. ucfirst($name);
        $arguments = [];
        return $this->proxyCurrentLocaleTranslation($method, $arguments);
    }

    /**
     * Get id
     *
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set city
     *
     * @param string $city
     *
     * @return Project
     */
    public function setCity($city)
    {
        $this->city = $city;

        return $this;
    }

    /**
     * Get city
     *
     * @return string
     */
    public function getCity()
    {
        return $this->city;
    }

    /**
     * Set region
     *
     * @param string $region
     *
     * @return Project
     */
    public function setRegion($region)
    {
        $this->region = $region;

        return $this;
    }

    /**
     * Get region
     *
     * @return string
     */
    public function getRegion()
    {
        return $this->region;
    }

    /**
     * Set country
     *
     * @param string $country
     *
     * @return Project
     */
    public function setCountry($country)
    {
        $this->country = $country;

        return $this;
    }

    /**
     * Get country
     *
     * @return string
     */
    public function getCountry()
    {
        return $this->country;
    }

    /**
     * Set isPublished
     *
     * @param boolean $isPublished
     *
     * @return Project
     */
    public function setIsPublished($isPublished)
    {
        $this->isPublished = $isPublished;

        return $this;
    }

    /**
     * Get isPublished
     *
     * @return boolean
     */
    public function getIsPublished()
    {
        return $this->isPublished;
    }

    /**
     * Set isFocused
     *
     * @param boolean $isFocused
     *
     * @return Project
     */
    public function setIsFocused($isFocused)
    {
        $this->isFocused = $isFocused;

        return $this;
    }

    /**
     * Get isFocused
     *
     * @return boolean
     */
    public function getIsFocused()
    {
        return $this->isFocused;
    }

    /**
     * Set coverFile
     *
     * @param File|null $cover
     */
    public function setCoverFile(File $cover = null)
    {
        $this->coverFile = $cover;
        if ($cover) {
            $this->updatedAt = new \DateTime('now');
        }
    }

    /**
     * Get coverFile
     *
     * @return File
     */
    public function getCoverFile()
    {
        return $this->coverFile;
    }

    /**
     * Set cover
     *
     * @param $cover
     */
    public function setCover($cover)
    {
        $this->cover = $cover;
    }

    /**
     * Get cover
     *
     * @return mixed
     */
    public function getCover()
    {
        return $this->cover;
    }

    /**
     * Get updatedAt
     *
     * @return \DateTime
     */
    public function getUpdatedAt()
    {
        return $this->updatedAt;
    }

    /**
     * Set updatedAt
     *
     * @param \DateTime $updatedAt
     */
    public function setUpdatedAt($updatedAt)
    {
        $this->updatedAt = $updatedAt;
    }

    /**
     * Add media
     *
     * @param ProjectMedia $media
     *
     * @return Project
     */
    public function addMedia(ProjectMedia $media)
    {
        $this->medias[] = $media;
        $media->setProject($this);

        return $this;
    }

    /**
     * Remove media
     *
     * @param ProjectMedia $media
     */
    public function removeMedia(ProjectMedia $media)
    {
        $this->medias->removeElement($media);
    }

    /**
     * Get medias
     *
     * @return ArrayCollection
     */
    public function getMedias()
    {
        return $this->medias;
    }

    /**
     * Add tag
     *
     * @param Tag $tag
     *
     * @return Project
     */
    public function addTag(Tag $tag)
    {
        $this->tags[] = $tag;
        $tag->addProject($this);

        return $this;
    }

    /**
     * Remove tag
     *
     * @param Tag $tag
     */
    public function removeTag(Tag $tag)
    {
        $this->tags->removeElement($tag);
    }

    /**
     * Get tags
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getTags()
    {
        return $this->tags;
    }
}

It's Translatable counterpart

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * ProjectTranslation
 *
 * @ORM\Table(name="project_translation")
 * @ORM\Entity()
 */
class ProjectTranslation
{
    use ORMBehaviors\Translatable\Translation;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=255)
     * @Assert\NotBlank()
     */
    private $name;

    /**
     * @var string
     *
     * @ORM\Column(name="tech", type="text")
     * @Assert\NotBlank()
     */
    private $tech;

    /**
     * @var string
     *
     * @ORM\Column(name="description", type="text")
     * @Assert\NotBlank()
     */
    private $description;

    /**
     * @var string
     *
     * @ORM\Column(name="slug", type="string", length=128, unique=true)
     * @Gedmo\Slug(fields={"name"})
     */
    private $slug;


    /**
     * Set name
     *
     * @param string $name
     *
     * @return ProjectTranslation
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * Get name
     *
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Set tech
     *
     * @param string $tech
     *
     * @return ProjectTranslation
     */
    public function setTech($tech)
    {
        $this->tech = $tech;

        return $this;
    }

    /**
     * Get tech
     *
     * @return string
     */
    public function getTech()
    {
        return $this->tech;
    }

    /**
     * Set description
     *
     * @param string $description
     *
     * @return ProjectTranslation
     */
    public function setDescription($description)
    {
        $this->description = $description;

        return $this;
    }

    /**
     * Get description
     *
     * @return string
     */
    public function getDescription()
    {
        return $this->description;
    }


    /**
     * Set slug
     *
     * @param string $slug
     *
     * @return ProjectTranslation
     */
    public function setSlug($slug)
    {
        $this->slug = $slug;

        return $this;
    }

    /**
     * Get slug
     *
     * @return string
     */
    public function getSlug()
    {
        return $this->slug;
    }
}

and my config

a2lix_translation_form:
    locale_provider: default
    locales: [en, fr]
    default_locale: fr
    required_locales: [en]
    templating: "@A2lixTranslationForm/bootstrap_4_layout.html.twig"

And for EasyAdmin

Project:
            class: AppBundle\Entity\Project
            list:
                title: 'Projets'
                fields:
                    - { property: 'cover', type: 'image', base_path: '%app.path.project_covers%' }
                    - { property: 'name', label: 'Nom' }
                    - { property: 'city', label: 'Ville' }
                    - { property: 'country', label: 'Pays' }
                    - { property: 'isFocused', label: 'Focus' }
                    - { property: 'isPublished', label: 'Publication' }
                    - { property: 'tags', label: 'Tags' }
                sort: ['name', 'ASC']
            form:
                fields:
                    - { property: 'translations', label: false, type: A2lix\TranslationFormBundle\Form\Type\TranslationsType }
                    - { property: 'coverFile', label: 'Vignette', type: 'vich_image' }
                    - { property: 'name', label: 'Nom du projet' }
                    - { property: 'tags', label: 'Tags', type: 'easyadmin_autocomplete' }
                    - { property: 'city', label: 'Ville' }
                    - { property: 'region', label: 'Région' }
                    - { property: 'country', label: 'Pays', type: 'country' }
                    - { property: 'tech', label: 'Caratéristiques techniques', type: 'FOS\CKEditorBundle\Form\Type\CKEditorType', type_options: { config_name: 'tech_config' } }
                    - { property: 'description', label: 'Description', type: 'FOS\CKEditorBundle\Form\Type\CKEditorType' }
                    - { property: 'medias', label: 'Slider', type: 'collection', type_options: { entry_type: 'AppBundle\Form\ProjectMediaType', by_reference: false } }

@FireFoxIXI, how do you solve the issue with search through the field that is translatable?

Also you have to add @A2lixTranslationForm/bootstrap_4_layout.html.twig to easy admin config

easy_admin:
    design:
        form_theme:
            - '@EasyAdmin/form/bootstrap_4.html.twig'
            - '@A2lixTranslationForm/bootstrap_4_layout.html.twig'

For SF4 I had to use a fork fsi-open/IvoryCKEditorBundle. Resources/public is empty in that fork, so I got that from the original.

I used:
composer require friendsofsymfony/ckeditor-bundle:2.x-dev
since 4.4 support is not yet ready: https://github.com/FriendsOfSymfony/FOSCKEditorBundle/issues/204

The trick is still working ?

Hello guys! I have troubles with Symfony 5, EasyAdmin 3 and KNP translatable
First Entity:

class GradeTaskMark implements TranslatableInterface
{
    use TranslatableTrait;

    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="integer")
     */
    private $value;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getValue(): ?int
    {
        return $this->value;
    }

    public function setValue(int $value): self
    {
        $this->value = $value;

        return $this;
    }

    public function __call($method, $arguments)
    {
        $method = ('get' === substr($method, 0, 3) || 'set' === substr($method, 0, 3)) ? $method : 'get'. ucfirst($method);

        return $this->proxyCurrentLocaleTranslation($method, $arguments);
    }

    public function __get($name)
    {
        $method = 'get'. ucfirst($name);
        $arguments = [];
        return $this->proxyCurrentLocaleTranslation($method, $arguments);
    }

}

Translation entity:

class GradeTaskMarkTranslation implements TranslationInterface
{
    use TranslationTrait;

    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $title;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getTitle(): ?string
    {
        return $this->title;
    }

    public function setTitle(string $title): self
    {
        $this->title = $title;

        return $this;
    }

}

AdminController:

class GradeTaskMarkCrudController extends AbstractCrudController
{
    public static function getEntityFqcn(): string
    {
        return GradeTaskMark::class;
    }


    public function configureFields(string $pageName): iterable
    {
        return [
            Field::new('translations')
                ->setFormType(TranslationsType::class)
                ->setFormTypeOptions(
                    [
                        'default_locale' => 'ru',
                        'fields' => [
                            'title' => TextType::class,
                        ],
                    ]
                )->hideOnIndex(),
            TextField::new('title'),
            IntegerField::new('value'),
        ];
    }

}

alix config:

a2lix_translation_form:
    locale_provider: default
    locales: [en, ru]
    default_locale: ru
    required_locales: [ru, en]
    templating: "@A2lixTranslationForm/bootstrap_4_layout.html.twig"

easyadmin config:

easy_admin:
    design:
        form_theme:
            - '@EasyAdmin/form/bootstrap_4.html.twig'
            - '@A2lixTranslationForm/bootstrap_4_layout.html.twig'

And i get an error:
The Doctrine type of the "translations" field is "4", which is not supported by EasyAdmin yet.

What problem can be? Help please! Thank you

I made new Field in EasyAdmin:

final class TranslationField implements FieldInterface
{
    use FieldTrait;

    public static function new(string $propertyName, ?string $label = null): self
    {
        return (new self())
            ->setProperty($propertyName)
            ->setLabel($label)
            ->setFormType(TranslationsType::class)
            ->setFormTypeOptions(
                [
                    'default_locale' => '%locale%',
                ]
            );
    }
}

And got new error from A2lix:
Argument 1 passed to A2lix\AutoFormBundle\ObjectInfo\DoctrineORMInfo::__construct() must be an instance of Doctrine\Common\Persistence\Mapping\ClassMetadataFactory, instance of Doctrine\ORM\Mapping\ClassMetadataFactory given, called in /var/www/kr/sova-grade-form/var/cache/dev/ContainerSd2qxYP/getA2lixAutoForm_Form_Manipulator_DoctrineOrmManipulatorService.php on line 24

I removed parameter type in __construct and method getAssocsConfig for test, and it works. How can i fix it without changing a2lix bundle?)

Hi,

Any news to make it work with symfo 5 and EasyAdmin 3.x ?

I am also having this error :
The Doctrine type of the "translations" field is "4", which is not supported by EasyAdmin yet.

I guess it come from this :

    public function configureFields(string $pageName): iterable
    {
        return [
            Field::new('translations')
                ->setFormType(TranslationsType::class)
                ->setFormTypeOptions(
                    [
                        'default_locale' => 'fr',
                    ]
                ),
            IdField::new('id', 'Id')->hideOnForm(),
            TextField::new('label', 'Label'),
            NumberField::new('coefficient', 'Coefficient'),
        ];
    }

@e1sep0 you have any solution? "The Doctrine type of the "translations" field is "4", which is not supported by EasyAdmin yet."

@e1sep0 you have any solution? "The Doctrine type of the "translations" field is "4", which is not supported by EasyAdmin yet."

I decided so:

<?php

namespace App\Admin\Field;

use A2lix\TranslationFormBundle\Form\Type\TranslationsType;
use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface;
use EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait;

final class TranslationField implements FieldInterface
{
    use FieldTrait;

    public static function new(string $propertyName, ?string $label = null, $fieldsConfig = []): self
    {
        return (new self())
            ->setProperty($propertyName)
            ->setLabel($label)
            ->setFormType(TranslationsType::class)
            ->setFormTypeOptions(
                [
                    'default_locale' => '%locale%',
                    'fields' => $fieldsConfig,
                ]
            );
    }
}

and in CrudController:

public function configureFields(string $pageName): iterable
    {
        $fieldsConfig = [
            'subject' => [
                'field_type' => TextareaType::class,
                'required' => true,
                'label' => 'Тема',
            ],
            'text' => [
                'field_type' => CKEditorType::class,
                'required' => true,
                'label' => 'Текст',
            ],
        ];

        return [
            TranslationField::new('translations', 'Переводы', $fieldsConfig)
                ->setRequired(true)
                ->hideOnIndex(),
            TextField::new('subject')->hideOnForm()->setLabel('Тема'),
            BooleanField::new('isActive')->setLabel('Активность'),
        ];
    }

@e1sep0 thanks a lot, it works! Did you make beautiful tabs?

bootstrap templating not including? how it include "@A2lixTranslationForm/bootstrap_4_layout.html.twig" ?

bootstrap templating not including? how it include "@A2lixTranslationForm/bootstrap_4_layout.html.twig" ?

included:

public function configureCrud(Crud $crud): Crud
    {
        return $crud
            ->setFormThemes(
                [
                    '@A2lixTranslationForm/bootstrap_4_layout.html.twig',
                    '@EasyAdmin/crud/form_theme.html.twig',
                    '@FOSCKEditor/Form/ckeditor_widget.html.twig',
                ]
            );
    }

@e1sep0 thanks a lot, it works! Did you make beautiful tabs?

image

@e1sep0 it work! thanks a lot!

Was this page helpful?
0 / 5 - 0 ratings