Jonathan Scheiber

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

Temps de lecture : 3 minutes Un commentaire
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() {
        EnhancedEditor.create(this.element).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`

Ajouter un commentaire