Installation:
composer require sonata-project/seo-bundle
Enable the bundle in config/bundles.php:
return [
// ...
SonataSeoBundle::class => ['all' => true],
];
Configuration:
Add basic SEO settings in config/packages/sonata_seo.yaml:
sonata_seo:
title: "Default Title"
description: "Default Description"
keywords: "default, keywords"
robots: "index, follow"
meta:
property: "value"
First Use Case: Override SEO metadata for a route in a controller:
use Sonata\SeoBundle\Model\SeoMetadataInterface;
public function showAction(SeoMetadataInterface $metadata): Response
{
$metadata->setTitle('Custom Page Title');
$metadata->setDescription('Custom description for SEO');
return $this->render('page/show.html.twig', [
'metadata' => $metadata,
]);
}
Twig Integration:
Use the sonata_seo Twig extension to render metadata in templates:
{{ sonata_seo.render() }}
Dynamic Metadata Handling:
SeoMetadataInterface in controllers to dynamically set metadata per route.$metadata->setTitle($entity->getSeoTitle());
$metadata->setMeta('og:image', $entity->getOgImageUrl());
Entity-Based SEO:
title, description, metaTags).SeoMetadataInterface from the entity:
public function onKernelView(GetResponseForControllerResultEvent $event)
{
$metadata = $event->getControllerResult();
if ($metadata instanceof SeoMetadataInterface && $metadata->getEntity()) {
$entity = $metadata->getEntity();
$metadata->setTitle($entity->getSeoTitle());
}
}
Route-Based Overrides:
sonata_seo:
routes:
app_homepage:
title: "Welcome to Our Site"
description: "Discover amazing features"
Twig Extensions:
sonata_seo.render() to output metadata in layouts.{{ sonata_seo.render({ exclude: ['keywords'] }) }}
API Responses:
public function onKernelView(GetResponseForControllerResultEvent $event)
{
$metadata = $event->getControllerResult();
if ($metadata instanceof SeoMetadataInterface) {
$event->getResponse()->headers->set('X-Seo-Title', $metadata->getTitle());
}
}
Symfony UX/Turbo:
sonata_seo to hydrate metadata on page changes via JavaScript:
document.addEventListener('turbo:load', () => {
fetch('/seo/metadata').then(response => {
response.json().then(data => {
document.querySelector('meta[name="description"]').content = data.description;
});
});
});
CMS Integration:
$admin->getShowMapper()->add('seoTitle', 'text', [
'label' => 'SEO Title',
]);
Sitemap Generation:
sonata-project/google-analytics-bundle or custom sitemap generators to include SEO-optimized URLs.Testing:
SeoMetadataInterface in PHPUnit tests:
$metadata = $this->createMock(SeoMetadataInterface::class);
$metadata->method('getTitle')->willReturn('Test Title');
Performance:
$metadata = $cache->get('seo_metadata_' . $route, function() use ($metadata) {
return $metadata;
});
Metadata Overrides:
SeoMetadataInterface in controllers for dynamic control.Twig Auto-escaping:
|raw if needed:
{{ sonata_seo.render()|raw }}
Entity Hydration:
metadata->setEntity($entity) before hydration will skip entity-based SEO logic.Deprecated Methods:
setMetaTag(); prefer setMeta() for consistency.Caching Headers:
X-Seo-*) may conflict with CDN caching. Explicitly set Cache-Control headers.Check Rendered Metadata:
dump() to inspect metadata:
{{ dump(sonata_seo.getMetadata()) }}
Event Debugging:
SeoMetadataInterface events in a subscriber:
public function onSeoMetadata(SeoMetadataEvent $event)
{
\Log::debug('SEO Metadata:', [
'title' => $event->getMetadata()->getTitle(),
'route' => $event->getRoute(),
]);
}
Browser DevTools:
<head> using Chrome DevTools (Elements > Head).Custom Metadata Types:
SeoMetadataInterface to add custom fields:
class CustomSeoMetadata extends SeoMetadata implements CustomSeoMetadataInterface
{
private $customField;
public function setCustomField(string $value): void
{
$this->customField = $value;
}
}
Metadata Providers:
Sonata\SeoBundle\Provider\MetadataProviderInterface to fetch metadata from external sources (e.g., APIs):
class ApiMetadataProvider implements MetadataProviderInterface
{
public function getMetadata(string $route): SeoMetadataInterface
{
$metadata = new SeoMetadata();
$apiData = $this->fetchFromApi($route);
$metadata->setTitle($apiData['title']);
return $metadata;
}
}
Event Subscribers:
sonata.seo.metadata events to modify metadata dynamically:
public static function getSubscribedEvents()
{
return [
SeoEvents::METADATA => 'onSeoMetadata',
];
}
Twig Extensions:
$twig->addExtension(new class extends \Sonata\SeoBundle\Twig\SeoExtension
{
public function render(array $options = []): string
{
// Custom logic
return parent::render($options);
}
});
Configuration Overrides:
config/packages/dev/sonata_seo.yaml) to toggle SEO features in development.metadata->setCanonicalUrl() to avoid duplicate content issues.setMeta() for social media previews:
$metadata->setMeta('og:type', 'website');
$metadata->setMeta('twitter:card', 'summary_large_image');
$metadata->setTitle($this->translator->trans('seo.title', [], 'messages'));
How can I help you explore Laravel packages today?