Jonathan Scheiber

Mettre en place CKEditor 5 avec Symfony, Asset Mapper et Stimulus

Temps de lecture : 3 minutes 23 commentaires
Mettre en place CKEditor 5 avec Symfony, Asset Mapper et Stimulus

La mise en place de CKEditor 5 dans Symfony, que ce soit avec Asset Mapper ou Webpack Encore, n'est pas chose aisée.

Dans cet article, nous allons voir comment faire cela avec Symfony, Asset Mapper et un petit contrôleur Stimulus chargé en lazy, afin de ne pas charger CKEditor sur les pages n'en ayant pas besoin.

Les prérequis de cet article :

Ajout des ressources CKEditor

Installons d'abord les fichiers JS/CSS de CKEditor avec Asset Mapper :

bin/console importmap:require ckeditor5
bin/console importmap:require ckeditor5/dist/ckeditor5.min.css
# Si vous avez besoin de traductions, par exemple ici les traductions en français
bin/console importmap:require ckeditor5/translations/fr.js

Configuration de CKEditor

Passons au fichier JS qui va nous permettre de configurer CKEditor : clé de licence, traductions et plugins chargés...

Dans cet exemple, on importe des plugins de base et on configure la barre d'outils associée, tout en chargeant les traductions en français.

// assets/ckeditor5.js
import {
    Autoformat,
    Bold,
    ClassicEditor,
    Essentials,
    Italic,
    Link,
    Paragraph,
} from 'ckeditor5';
// Si vous devez importer des traductions, ici les traductions en français
import coreTranslations from 'ckeditor5/translations/fr.js';
import 'ckeditor5/dist/ckeditor5.min.css';

export default class EnhancedEditor extends ClassicEditor {}

EnhancedEditor.builtinPlugins = [Autoformat, Bold, Essentials, Italic, Link, Paragraph];

EnhancedEditor.defaultConfig = {
    licenseKey: 'GPL',
    toolbar: [
        'bold',
        'italic',
        '|',
        'link',
        '|',
        'undo',
        'redo',
    ],
    // Vous pouvez supprimer la ligne suivante si vous n'avez pas besoin de charger des traductions
    translations: [coreTranslations],
};

Contrôleur Stimulus

Voici maintenant le contrôleur Stimulus, qui va permettre de charger les ressources à la demande (le contrôleur étant chargé de manière lazy), et d'instancier les éditeurs s'ils apparaissent dynamiquement sur la page.

// assets/controllers/ckeditor5_controller.js
import { Controller } from '@hotwired/stimulus';
import EnhancedEditor from '../ckeditor5.js';

/* stimulusFetch: 'lazy' */
export default class extends Controller {
    connect() {
        this.editor = EnhancedEditor.create(this.element)
            .then(editor => (this.editor = editor))
            .catch(error => console.error(error));
    }

    disconnect() {
        this.editor.destroy().catch(error => console.error(error));
    }
}

Form type dédié

Pour se faciliter la vie, on va créer un form type dédié qui va permettre d'aisément charger CKEditor dans un formulaire.

<?php

declare(strict_types=1);

namespace App\Form\Type;

use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\UX\StimulusBundle\Helper\StimulusHelper;

/**
 * @extends AbstractType<string>
 */
final class CKEditor5Type extends AbstractType
{
    public function __construct(
        #[Autowire(service: 'stimulus.helper')]
        private readonly StimulusHelper $stimulusHelper,
    ) {}

    public function finishView(FormView $view, FormInterface $form, array $options): void
    {
        parent::finishView($view, $form, $options);

        $attr = $this->stimulusHelper->createStimulusAttributes();
        $attr->addController('ckeditor5');

        $view->vars['attr'] = $attr->toArray();
    }

    public function getParent(): string
    {
        return TextareaType::class;
    }
}

Enfin, on va pouvoir l'utiliser !

Eh oui, on va enfin pouvoir l'utiliser comme on veut dans nos formulaires ! Et voici comment :

public function buildForm(FormBuilderInterface $builder, array $options): void
{
    $builder->add('content', CKEditor5Type::class);
}

Et voilà, c'est terminé ! :) Si vous avez des questions, remarques, suggestions ... N'hésitez pas à poster un commentaire ci-dessous :)

Commentaires

Posté par Stof le 21/01/2025 à 15:25.

To avoid leaking resources, your Stimulus component should also handle the case where it is remove dynamically from the page, not just when it is added. For that, you need to store the editor instance (returned asynchronously from ClassicEditor.create()) and call its destroy() method in disconnect

Posté par jmsche le 22/01/2025 à 13:26.

Thank you for your feedback Stof, I fixed the Stimulus controller :)

Posté par Hervé Le Crosnier le 15/03/2025 à 16:57.

Merci beaucoup, c'est très clair, efficace.
Compte-tenu de l'usage massif de CKeditor dans les projets symfony, cela devrait figurer sur leur site...

Avez-vous d'autres éclairage sur l'usage de plugins sur CKeditor avec symfony et les assetmap ?

Posté par jmsche le 15/03/2025 à 18:05.

Bonjour Hervé, de quels éclairages auriez-vous besoin ? Un plugin en particulier ?

Posté par Hervé Le Crosnier le 15/03/2025 à 18:13.

Non, quelque chose de plus général : comment créer un plugin dans cette configuration (je savais le faire en CK4 sans asset mapper). En fait, ce n'est pas tellement ce qu'on met dans le plugin, mais où met-on le plugin.
Et aussi, où met-on les diverses configurations pour les intégrer au niveau du FormType de tel ou tel objet.
En fait, C'est une question sur l'architecture : où met-on les choses et comment les appelle-t-on.
Le genre de chose qui doit être tellement évident pour les spécialistes que c'est justement ce qu'on ne trouve pas... et aussi ce que votre post a très bien réussi : nom des fichiers, positionnement et liens.
Merci encore
Hervé

Posté par Yolserve le 27/07/2025 à 09:21.

Qu'en est-il de l'intégration avec EasyAdmin

Posté par jmsche le 27/07/2025 à 09:43.

Bonjour Yolserve,

Il suffit d'utiliser le nouveau form type créé avec EasyAdmin, et de configurer EasyAdmin pour qu'il charge un asset où les contrôleurs Stimulus sont chargés.

Posté par plesk le 27/07/2025 à 18:23.

Bonjour, comment ajouter plusieurs langues, par exemple fr, en, de, it, etc.? Merci.

Posté par plesk le 27/07/2025 à 20:56.

J'ai trouver une solution qui marche correctement.

// assets/controllers/ckeditor5_controller.js

import { Controller } from '@hotwired/stimulus';
import EnhancedEditor from '../ckeditor5.js';

/* stimulusFetch: 'lazy' */
export default class extends Controller {
    connect() {
        const locale = document.querySelector('html').getAttribute('lang') || 'en';
        this.editor = EnhancedEditor.create(this.element, {
            language: locale
        })
            .then(editor => (this.editor = editor))
            .catch(error => console.error(error));
    }

    disconnect() {
        if (this.editor) {
            this.editor.destroy().catch(error => console.error(error));
        }
    }
}
// assets/ckeditor5.js 
import {
    Autoformat,
    Bold,
    ClassicEditor,
    Essentials,
    Italic,
    Link,
    Paragraph,
} from 'ckeditor5';

import 'ckeditor5/dist/ckeditor5.min.css';

import translationsFr from 'ckeditor5/translations/fr.js';
import translationsEn from 'ckeditor5/translations/en.js';
import translationsDe from 'ckeditor5/translations/de.js';
import translationsIt from 'ckeditor5/translations/it.js';

export default class EnhancedEditor extends ClassicEditor { }

EnhancedEditor.builtinPlugins = [Autoformat, Bold, Essentials, Italic, Link, Paragraph];

EnhancedEditor.defaultConfig = {
    licenseKey: 'GPL',
    toolbar: [
        'bold',
        'italic',
        '|',
        'link',
        '|',
        'undo',
        'redo',
    ],

    translations: [translationsFr, translationsEn, translationsDe, translationsIt],
};
Posté par Damien le 21/08/2025 à 23:04.

Bonjour,
Perso j'ai beau suivre le tuto et faire l'appel dans EasyAdmin via la fonction configureAssets et le setFormType, rien à faire pour faire fonctionner CKEditor.
C'est fou que ça soit toujours aussi compliqué en 2025.
Est-ce possible d'avoir un exemple EasyAdmin svp ?

Posté par jmsche le 28/08/2025 à 21:12.

Bonjour Damien,

Avec EasyAdmin, tu as bien ajouté l'entrée qui charge les contrôleurs Stimulus ?

Sinon, ça ne peut pas fonctionner.

N'hésite pas à envoyer un lien vers un reproducer. :)

Posté par Peter Harbig le 31/08/2025 à 16:35.

Dear Damien,
many thanks for this very helpful solution.

Peter

Posté par Damien le 02/09/2025 à 09:36.

Bonjour jmsche.
Je pense que oui, j'ai configuré cela via la fonction "configureAssets".
Sinon c'est que je ne vois pas de quoi l'on parle, je vais recheck ça pour être sûr de ne rien louper du coup mais si tu as un exemple, je ne dis pas non :)
Pour le reproducer, je vais le préparer si ça ne veut toujours pas.

Posté par Damien le 04/09/2025 à 22:04.

Bonsoir, J'ai réessayé aujourd'hui pendant des heures, tentait via override du layout, configureAssets et autre mais rien à faire. Je vais refaire encore des tentatives demain et sinon bah, je ne sais pas :( Merci en tout cas pour vos retours.

Posté par jmsche le 08/09/2025 à 20:34.

Bonsoir Damien,

Je viens de faire un test rapide basé sur la démo d'EasyAdmin et ça a l'air de fonctionner.

Le repository Git : https://github.com/jmsche/easyadmin-ckeditor5

Tu peux voir le résultat sur la page "Form Field Reference" après avoir installé et démarré le projet (j'ai mis le champ au début de la page).

N'hésite pas à me contacter sur Slack si besoin, ou même juste à me dire si ça t'a aidé :)

Posté par Damien le 10/09/2025 à 22:43.

Bonsoir,
Merci beaucoup pour le repo.
La seule différence que j'ai trouvé mais qui fait que ça fonctionne (alors qu'il me semblait l'avoir testé), c'est que le configureAssets est dans le dashboard controller au lieu du controller de l'objet.
En tout cas merci beaucoup ça fonctionne à présent !!
Bonne soirée

Posté par rachid le 11/09/2025 à 22:53.

Bonjour,
je viens de lancer un projet Symfony 7. Avec une seule entité Article et admin/article/new. J'ai tout de suite suivi toutes vos étapes mais la barre ckeditor ne s'affiche pas. Pourquoi??? merci pour votre aide.

Posté par jmsche le 11/09/2025 à 23:07.

Bonjour Rachid, sans reproducer, code ou message d'erreur ça va être compliqué de répondre...

Posté par Damien le 12/09/2025 à 09:13.

@Rachid, le repo qui a été préparé pour moi est top et encore merci à @jmsche N'hésite pas à le parcourir et le tester sur ton poste.

Posté par rachid le 13/09/2025 à 18:09.

Bonjour @Damien et merci. Bonjour @jmsche. Je précise que je suis débutant autodidacte. Comment utiliser le Form Field Reference? La barre CKEditor5 ne s'affiche pas dans le content. Comment faire pour qu'elle le soit? Merci.

Posté par jmsche le 14/09/2025 à 11:44.

Bonjour Rachid,

Je t'invite à jeter un œil au dernier commit sur le repository.

Tu n'as pas spécialement besoin du FormFieldReference, il faut juste déclarer le contrôleur Stimulus CKEditor via un data attribute sur le form type (via une option, ou via un form type custom comme je l'ai fait ici), et faire en sorte que le contrôleur Stimulus soit chargé sur la page (donc le fichier assets/bootstrap.js doit être chargé).

Posté par rachid le 14/09/2025 à 15:49.

Bonjour et merci pour la réponse. Mais franchement si ça vous embête pas, j'ai besoin d'être guidé pas à pas. Déjà, je ne sais pas où placer ce code :

public function buildForm(FormBuilderInterface $builder, array $options): void
{
    $builder->add('content', CKEditor5Type::class);
}

Ensuite, pourrait tu me préciser comment déclarer le contrôleur Stimulus CKEditor via un data attribute sur le form type via : Quel form type? Quel data attribute?
Est-ce que je dois ajouter mon propre crud Article pour pouvoir accéder à admin/ article/new afin d'y placer la varre ckeditor? Merci

Posté par jmsche le 14/09/2025 à 15:57.

Je t'invite à lire un peu la documentation d'EasyAdmin sur les Fields, tout y est documenté : https://symfony.com/bundles/EasyAdminBundle/current/fields.html

Si tu as vraiment besoin d'aide par après, tu peux me contacter sur le Slack Symfony.

Ajouter un commentaire

Ce champ accepte le contenu au format Markdown.