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

Formatter Bundle Laravel Package

rmzamora/formatter-bundle

A Laravel/PHP formatter bundle that provides reusable formatting helpers for consistent, clean output across models, views, and APIs.

View on GitHub
Deep Wiki
Context7

Getting Started

This is not a Laravel package, and the first step of the assessment is deciding whether you need it at all — the answer is almost certainly no. rmzamora/formatter-bundle is a fork of Symfony's SonataFormatterBundle, archived, with 0 stars and 0 dependents. It is a Symfony Bundle wired through DependencyInjection/Extension, service tags, and a config tree; none of that is consumable by Laravel's container or package discovery. There is no service provider to register and nothing to composer require into a Laravel app that won't drag in Symfony/Sonata dependencies.

Where to look first, if you encounter it (in a legacy Symfony/Sonata admin you're maintaining or migrating off):

  1. Formatter/BaseFormatter.php and Formatter/MarkdownFormatter.php — the rendering contract is tiny: a format(string $text) method plus a processor/name getter. Port this contract, not the bundle.
  2. DependencyInjection/Configuration.php — the whole visible API is the config tree: a default_formatters map and per-formatter extension lists. This tells you what the package actually does.
  3. DependencyInjection/*Extension.php — shows how formatters are collected from tagged services into a pool, which is the one pattern worth copying.

First use case — pick the right one:

  • You're in Laravel: skip the package. Your "formatter bundle" is Str::markdown() / league/commonmark for markdown, Blade for templating, and mews/purifier for rich HTML. Add a format column to the content table and a small FormatterManager if you need the discriminator.
  • You're in a legacy Symfony/Sonata admin: the first use case is enabling it on a Sonata TextType field so admin-editable text can be Markdown or WYSIWYG, with the formatter chosen per field. You register it, define sonata_formatter.default_formatters, and reference sonata.formatter.pool from your admin config.

Implementation Patterns

The formatter-pool pattern (the one idea worth stealing). The bundle maps a stored string (markdown, richhtml, twig, …) to a service that renders it. Reproduce this in Laravel with a Manager:

class FormatterManager extends Manager
{
    public function getDefaultDriver(): string { return config('formatters.default'); }

    protected function createMarkdownDriver(): Formatter {
        return new MarkdownFormatter(new CommonMarkConverter(config('formatters.markdown')));
    }
    protected function createRichHtmlDriver(): Formatter {
        return new PurifiedHtmlFormatter(app(Purifier::class));
    }
}

Register extend() calls for custom formats, bind as a singleton, and call resolve($content->format)->render($content->body).

Store raw, render on read (or on save), cache the output. The package is stateless — it renders every time it's asked. In practice you want a rendered_html column or a cache keyed by hash('xxh3', $body) . ':' . $format. Render on save with a queued job if the format is expensive; render on read with a short cache if you need to change formatters retroactively.

Keep the discriminator column and its values. If you are migrating off the bundle, the highest-fidelity path is a 1:1 reimplementation: keep format = 'markdown' | 'richhtml' | 'twig' in the database and write one Laravel driver per existing formatter. Golden-file test: render every stored row through the old formatter and the new one, diff the HTML, fix until the diff is empty. This is the only reliable way to migrate a CMS without breaking published content.

Admin/WYSIWYG integration. In Sonata the pattern is TextType + IvoryCKEditorBundle + sonata.formatter.pool. In Laravel the equivalent is a Filament/Nova rich-editor field that writes both body (raw) and rendered_html, with the editor's output going through the richhtml sanitizer on save. Don't let the editor write rendered HTML directly to the public column.

Sandbox anything templated. The twig formatter renders stored content as a Twig template. If you port it to Blade, only ever run it against trusted, developer-authored templates with a fixed variable whitelist — never against user-supplied content. If the requirement is "users can write templating syntax," that's a sandboxed evaluation problem, not a formatter.

Config surface to mirror. config/formatters.php with default, drivers (class + options), and per-driver extensions (CommonMark extensions, allowed HTML tags). Keep all of it config-driven so the format value in the database is the only thing the app has to know.

Gotchas and Tips

  • Archived + 0 dependents is the headline. No security patches, no PHP 8.x/Symfony 6–7 support, and Composer will fight you on constraints. Treat it as a reference implementation to read, not a dependency to install.
  • Bundle ≠ package. There's no Laravel bridge to write here. The Symfony DI tags, ContainerAware services, and config tree don't translate; a hand-written service provider that "loads" the bundle will fail at the first tagged-service lookup. Port the ~100 lines of formatter classes instead.
  • The twig formatter is remote code execution if content is user-editable. This is the single biggest footgun in the package. Same caution applies to any templating driver you build.
  • Markdown dialects differ. php-markdown (the old bundle's likely backend) and league/commonmark produce different HTML for tables, footnotes, and nested lists. Test against real stored content, not toy strings.
  • Silent empty output. Formatters in this family typically return '' for unsupported input rather than throwing. When a field renders blank, log the format name and an input hash before assuming a bug in the formatter — it's usually a mismatched format value or a missing extension.
  • Rich HTML needs sanitizing, not just formatting. The richhtml formatter passes HTML through; the bundle does not purify it. In Laravel use mews/purifier (HTMLPurifier) with an explicit allowed-tags profile, on save.
  • Caching invalidation. If you cache rendered output, key on content hash, not on record ID — otherwise edits serve stale HTML. Invalidate in the model's saved/deleted events.
  • If you must install the fork, use a Composer vcs repository entry and pin the commit, plus a replace/provide block if other packages expect the upstream name. Isolate it behind an interface so removal is a one-file change.
  • Keep the extension point, drop the package: the durable artifact is the two-method formatter contract (render() + a processor/name accessor) and the pool that resolves by stored name. That pattern is worth carrying forward; the Symfony wiring is not.
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.
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle