This CKEditor5 plugin allows editing Twig templates.
Demo: mroca.github.io/ckeditor5-twig
Features:
- Display all available variables, their type & children.
{{ variable|filter }}
{% tag %}
blocks{% tag %} with content {% endtag %}
blocks{# comments #}
- Raw Twig content with Code blocks
- Allow using variables into images src
- Import / Export html code button
You must use the same major CKEditor version as the one supported by this plugin (see the package.json
file).
If you get a ckeditor-duplicated-modules
error, you have mismatching versions.
yarn add ckeditor5-twig
<textarea id="editor" style="display: none;">
<h1>My template</h1>
{% block twig-template %}
...
{% endblock %}
</textarea>
Note: you should use a
<textarea>
, or a<script type="text/template">
tags instead of a<div>
one in order to avoid your browser parsing it.
import TwigPlugin from 'ckeditor5-twig/twig/twigplugin';
import 'ckeditor5-twig/twig/plugin.css';
ClassicEditor
.create( document.querySelector( '#editor' ), {
plugins: [ '...', TwigPlugin ],
toolbar: [ '...', 'twigCommands' ],
config: {
...,
twig: {
variables: {
username: { type: 'string', label: 'The current user\'s username' },
country: {
type: 'object',
nullable: true,
properties: {
name: { type: 'string', label: 'The country name' },
cities: {
type: 'array',
label: 'All the country\'s cities',
children: { type: 'string', label: 'The city name' }
},
}
},
}
}
}
} )
.then( '...' )
.catch( '...' );
Translations are currently available for EN
and FR
locales (see the twig/twigpluginui.js
file).
The plugin brings an Image
feature allowing to use twig expression as src
value, e.g.: "https://cdn.my.site/images/" ~ user.avatar
When using a twig expression as image src
, a grey image is displayed with the src
value on it.
You must install the Image plugin in order to use it with the Twig plugin:
yarn add @ckeditor/ckeditor5-image
import Image from '@ckeditor/ckeditor5-image/src/image';
ClassicEditor.create( document.querySelector( '#editor' ), {
plugins: ['...', TwigPlugin, Image]
});
If you need to write your own twig code, you can use the Code blocks plugin.
The required config is described bellow. Behind the woods, the twig plugin will replace all <pre><code class="language-twig">
tags by <code-language-twig>
.
import TwigPlugin from 'ckeditor5-twig/twig/twigplugin';
import 'ckeditor5-twig/twig/plugin.css';
ClassicEditor
.create( document.querySelector( '#editor' ), {
plugins: [ '...', TwigPlugin, CodeBlock ],
toolbar: [ '...',, 'twigCommands', 'codeBlock' ],
config: {
...,
twig: { ... },
codeBlock: {
indentSequence: false,
languages: [
{ language: 'twig', label: 'Twig' }
// You can add other languages here, see https://ckeditor.com/docs/ckeditor5/latest/features/code-blocks.html#configuring-code-block-languages
]
}
}
} );
The Twig plugin is also compatible with HTML embed if using
<raw-html-embed>
tag instead of<div class="raw-html-embed">
, but there are some issues. As the embed html plugin parses its content, we cannot really use it for twig data:<html>{% if a > b %}</html>
will be replaced by{% if a > b %}
A PHP TwigVariablesExtractor
class allows to convert an array of items (objects, entities, arrays, types, ...)
into a variables
array that can be used as plugin config.
This class is currently located into demo/symfonyapp/src/Extractor
, and could later be moved into a dedicated repository
and composer package.
Extractor options
$propertyInfoExtractor = new PropertyInfoExtractor('...');
$extractor = new TwigVariablesExtractor($propertyInfoExtractor, [
'circular_reference_limit' => 1,
'max_depth' => 4,
]);
Groups & Serializer
If there are too many object properties to parse, you can use the @Groups
serializer annotation, then use the
serializer_groups context option
$extractor = new TwigVariablesExtractor();
$variables = $extractor->extract($types, ['serializer_groups' => ['foo']]);
Variables types
<?php
namespace App\Controller;
use App\Extractor\TwigVariablesExtractor;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
class DefaultController extends AbstractController
{
public function index(): Response
{
// Option 1: variables as an array
// $variables = ['name' => ['type' => 'string']];
// Option 2: TwigVariablesExtractor & variables defined as an object's properties
// $variables = $extractor->extract(MyTemplateVariablesObject::class);
// Option 3: TwigVariablesExtractor & variables defined in an array
$variables = [
'app' => [ // Manually defined config
'type' => 'object',
'label' => 'All app related global variables',
'properties' => [
'debug' => ['type' => 'boolean', 'label' => 'Is debug enabled?'],
'environment' => ['type' => 'string', 'label' => 'Current app env: dev or prod'],
]
],
'siteTitle' => 'string', // type as string
'user' => User::class, // object & children
'article' => BlogArticle::class, // class name
'comments' => [ // array with ONE item type
Comment::class // array content type
],
'calendar' => [ // associative array as object
'now' => 'datetime', // type name
'yesterday' => \DateTimeInterface::class, // interface name
'tomorrow' => new \DateTimeImmutable(), // object
],
];
$extractor = new TwigVariablesExtractor();
return $this->render('default/index.html.twig', [
'variables' => $extractor->extract($variables),
]);
}
}
All translations are defined in the twig/twigpluginui.js
file. Each one can be overridden by
adding the translation
yarn install
yarn watch
Then open the index.html
file (with the Run
button in PhpStorm / WebStorm, for instance).
Before pushing
yarn lint:fix
yarn stylelint:fix
Install the Symfony CLI, then:
cd demo/symfonyapp
composer install
yarn install
(yarn encore dev-server --port 8880 & symfony server:start --port 8000 --no-tls)
# or run the two commands in a different terminal
Then access the demo page
Before pushing
cd demo/symfonyapp
vendor/bin/php-cs-fixer fix
- As the twig tags are replaced by a
div
, they cannot be used intoa
,ul
list without breaking the html:<ul> {% for a in b %} <li>{{ a }}</li> {% endfor %} </ul> <ul> {# This comment should be replaced by a "li" tag, as it is located into an "ul" tag #} <li>This is a list item</li> </ul>
- Inline blocks are not handled for now:
<p>{% if a %}yes{% else %}no{% endif %}</p>
- The block statement content part must have a parent Paragraph in order to be writable. A Paragraph is currently added into each block content
else
is not well-supported for now as part of the graphicalif
/for
block
- Deal with inline &
li
statements - Allow passing available functions & filters into the variables list
- Validate the twig template with twig.js
DataExtractor
- object with
__toString
=>object|string
- object => display the class name