Installation:
composer require sonata-project/formatter-bundle
Enable the bundle in config/bundles.php:
return [
// ...
Sonata\FormatterBundle\SonataFormatterBundle::class => ['all' => true],
];
Basic Usage:
Register a formatter in config/packages/sonata_formatter.yaml:
sonata_formatter:
formats:
- { name: 'default', extension: 'html', mime_type: 'text/html' }
- { name: 'text', extension: 'txt', mime_type: 'text/plain' }
First Use Case:
Inject Sonata\FormatterBundle\Formatter\FormatterInterface into a service/controller and use it to format content:
use Sonata\FormatterBundle\Formatter\FormatterInterface;
public function __construct(private FormatterInterface $formatter) {}
public function renderContent(string $content, string $format = 'default'): string
{
return $this->formatter->format($content, $format);
}
config/packages/sonata_formatter.yaml (default config)src/Formatter/FormatterInterface.php (core interface)src/DependencyInjection/Configuration.php (customization points)Dynamic Format Handling:
Use the format() method to dynamically apply formatting based on user input or request parameters:
$formatted = $this->formatter->format($rawContent, $request->query->get('format', 'default'));
Twig Integration: Extend Twig with custom filters for seamless template integration:
{{ content|format('default') }}
Register the Twig extension in config/packages/twig.yaml:
twig:
globals:
formatter: '@sonata.formatter.formatter'
Event-Driven Formatting:
Listen to sonata.formatter.format events to pre/post-process content:
use Sonata\FormatterBundle\Event\FormatEvent;
public function onFormat(FormatEvent $event)
{
if ($event->getFormat() === 'text') {
$event->setContent(strtoupper($event->getContent()));
}
}
Custom Formatters:
Implement Sonata\FormatterBundle\Formatter\FormatterInterface for bespoke logic:
class CustomFormatter implements FormatterInterface
{
public function format($content, $format): string
{
return str_replace('foo', 'bar', $content);
}
}
Register it in services.yaml:
services:
App\Formatter\CustomFormatter:
tags: [sonata.formatter]
Sonata\FormatterBundle\Doctrine\Types\TextType for automatic formatting in database fields.Symfony\Component\HttpFoundation\Response:
return new Response($this->formatter->format($content, 'text'), 200, ['Content-Type' => 'text/plain']);
sonata_admin:
types:
text:
formatter:
format: 'default'
Format Mismatch:
sonata_formatter.formats causes FormatNotFoundException.try {
$formatted = $this->formatter->format($content, 'nonexistent');
} catch (FormatNotFoundException $e) {
return $this->formatter->format($content, 'default');
}
Circular Dependencies:
EventDispatcher::DISPATCHER_PRIORITY_LOW for listeners.Performance:
$this->formatter->format($content, 'default', ['cache' => true]);
Log Formatting: Enable debug mode to log format operations:
sonata_formatter:
debug: true
Check logs for sonata.formatter entries.
Content Dumping:
Use var_dump() or dd() to inspect raw vs. formatted content:
$raw = $entity->getContent();
$formatted = $this->formatter->format($raw, 'default');
dump($raw, $formatted);
Default Format:
default format is auto-configured but may conflict with custom formats. Explicitly define it:
sonata_formatter:
formats:
default:
name: 'default'
extension: 'html'
mime_type: 'text/html'
Mime Type Overrides:
text/html for HTML). Use fileinfo to auto-detect:
sonata_formatter:
mime_types:
'text/html': ['html', 'htm']
Extension Points:
sonata.formatter to auto-register.sonata.formatter.event_subscriber for priority control.Batch Processing:
Use Sonata\FormatterBundle\Formatter\FormatterPool to format multiple contents at once:
$pool = $this->container->get('sonata.formatter.pool');
$results = $pool->formatAll([$content1, $content2], 'text');
Security: Sanitize user-provided formats to prevent injection:
$allowedFormats = ['default', 'text'];
$format = in_array($request->get('format'), $allowedFormats)
? $request->get('format')
: 'default';
Testing:
Mock FormatterInterface in tests:
$formatter = $this->createMock(FormatterInterface::class);
$formatter->method('format')->willReturn('formatted!');
How can I help you explore Laravel packages today?