Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Fieldtype Richtext Laravel Package

ibexa/fieldtype-richtext

The official Ibexa RichText field type for Ibexa DXP stores rich formatted text as structured XML, enabling clean rendering and editing of content. Part of Ibexa DXP; install via the platform.

View on GitHub
Deep Wiki
Context7

Getting Started

This is not a Laravel package — it is a Symfony bundle that ships the ezrichtext (RichText) Field Type for Ibexa DXP. Don't try to composer require it into a Laravel app; it depends on ibexa/core, the Field Type registry, and the repository layer. Install it the supported way (as part of an Ibexa DXP install) and let Composer pull it in.

First steps:

  1. Confirm it's already there. In a working install run composer show ibexa/fieldtype-richtext and check config/bundles.php for IbexaFieldTypeRichTextBundle. If you have Ibexa DXP, you almost never add it manually — you add it to a content type.
  2. Look at the Field Type identifier in Ibexa\FieldTypeRichText\FieldType\RichText\Type::IDENTIFIER (historically ezrichtext; the README refers to it as ibexa_richtext). Everything — config keys, Twig rendering, migrations — hangs off that identifier.
  3. Read the value object first. Ibexa\FieldTypeRichText\FieldType\RichText\Value holds a DOMDocument in $value->xml. That is the single most important fact about this package: the stored payload is structured XML (a DocBook-ish dialect), not HTML and not Markdown.
  4. First use case: add a RichText field to a content type (e.g. body), edit it in the back office, then render it in Twig:
{{ ibexa_render_field(content, 'body') }}
{# or, legacy naming #}
{{ ez_render_field(content, 'body') }}

If that renders, the plumbing works. Then move on to reading/writing the field from PHP.

Implementation Patterns

Rendering in templates. Prefer the render helper over dumping the XML. If you need the raw document (e.g. to iterate over embedded content references), drop down to field.value.xml:

{% set doc = content.getFieldValue('body').xml %}
{% for embed in doc.getElementsByTagName('ezembed') %}
    {{ ez_render_field(embed.getAttribute('data-content-id')|ibexa_internal_id, 'embed') }}
{% endfor %}

Creating/updating a field programmatically. Build the XML, then let the converter normalise it — don't hand-assemble nodes and hope validation passes:

use Ibexa\FieldTypeRichText\FieldType\RichText\Value;

$doc = new \DOMDocument();
$doc->loadXML('<section xmlns="http://docbook.org/ns/docbook"><para>Hello</para></section>');

$value = new Value();
$value->xml = $doc;

$content->setFieldValue('body', $value);
$contentService->publishVersion($content->getVersionInfo());

Converting HTML ↔ XML. The bundle exposes converter interfaces (Ibexa\Contracts\FieldTypeRichText\RichText\Converter) and an aggregate converter configured under ibexa.field_type.richtext.converter. Use the input converter when importing legacy HTML into a RichText field, and the output converter when you need HTML for a non-Twig consumer (API response, PDF, search index export). Inject the named converter service rather than new-ing anything.

Rendering outside the back office. If you expose content via a JSON API, don't return $value->xml->saveXML() and let the consumer figure it out. Convert to XHTML server-side with the output converter and cache the result keyed on contentId + versionNo + siteaccess. Conversion walks the whole DOM and resolves embeds; doing it per request at scale will hurt.

Migrations. In ibexa:migrations YAML, a richtext field value is raw XML:

- type: content
  mode: create
  fields:
    - identifier: body
      fieldtype: ezrichtext
      value: "<section xmlns=\"http://docbook.org/ns/docbook\"><para>Hi</para></section>"

Custom tags. These are the extension point for "a thing inside the text" (embed block, product card, ad slot). Implement the custom tag interface (name + converter + template) and register it with the ibexa.field_type.richtext.custom_tag tag, then declare it in the siteaccess config so the editor toolbar offers it. The tag name must match between the config, the XML element, and the converter — mismatches produce silently empty output rather than an error.

Gotchas and Tips

Pin a recent patch. Two security/correctness fixes matter: XSS in custom tags (fixed in 4.6.25 / 5.0.3) and inline custom tags rendering empty when the inline string is "0" (fixed in 5.0.8, note the PHP falsy-string trap). Also setBooleanValue had a bad cast corrected in 5.0.8. If you're on an older tag, upgrade.

The RNG schema is a wall you will hit. Every stored document is validated against ezpublish.rng. Add a custom element without extending the schema and publishing fails with a validation error that reads like a malformed-XML complaint. Recent versions added an option to extend ezpublish.rng — use it, and extend it in the same change as the custom tag, not later.

Don't edit the XML in the database. The back office re-saves through the converter chain; anything you inject that isn't schema-valid will be rejected on the next edit, and inline markup like <strong> written as HTML will be dropped or mangled. Always go through Value + converter.

Empty vs. missing. $value->xml is a DOMDocument; an empty field is an empty document, not null. getFieldValue() on a field that doesn't exist returns null — guard both cases before calling saveXML().

Twig auto-escaping. Converted output is HTML, so it needs |raw if you're printing a converter result (the render helper already handles this). Printing xml.saveXML() directly into a template is an XSS hole and also leaks internal embed IDs.

Config quirks. Toolbar buttons, tag presets, custom tag declarations and style definitions are all per-siteaccess under ibexa.system.<scope>.fieldtypes.ezrichtext. Put shared defaults in default, override per siteaccess. Note the recent "fallback configuration when failing init" fix — an invalid toolbar config used to blow up the editor rather than degrade, so validate after editing YAML.

Front-end build. The editor plugins ship TypeScript/JS that must be compiled through Webpack Encore. If the toolbar renders but plugins 404, your Encore config is stale — re-run the asset build and clear the back-office cache. TS config support was only added in 5.0.8, which tells you how fragile this area has been.

Front-end event subtleties. If custom JS calls updateInput, the optional shouldFireInputEvent attribute controls whether an input event fires; fire it indiscriminately and you'll clear validation error messages the user needed to see.

Debugging strategy. Validation errors come from Ibexa\Contracts\FieldTypeRichText\RichText\Validator — catch and log the specific RNG line number, not just "invalid XML". For rendering bugs, dump $value->xml->saveXML() and diff against a known-good document produced by the back office; nine times out of ten the difference is a missing namespace or a data- attribute the converter expects.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
roundly-consulting/kubernetes-api-for-laravel
roundly-consulting/plausible-for-laravel
gvp/gvp-laravel-template
metasyncsite/laravel-metasync-client
roundly-consulting/onboarding-for-laravel
andydefer/laravel-notification
adachsoft/cli-process-tester
jhaskell13/laraflow
sebastienheyd/boilerplate-media-manager
ccast/tagixo-primix
pretendtrue/laravel-builder
sawmainek/laravel-scout-tntsearch-driver
tomshaw/laravel-calendar-table
dibakar/laravel-ownership
steadrun/laravel-monitor
labyrinthe/connect-laravel
elmogy/layan
drewdan/sentinel-client
dskripchenko/laravel-llm
fatryst/nuonuo