Installation:
composer require aldaflux/seo-bundle
Register the bundle in config/bundles.php (Symfony 4+) or AppKernel.php (Symfony 3):
return [
// ...
Aldaflux\Bundle\SeoBundle\LeogoutSeoBundle::class => ['all' => true],
];
Basic Configuration (config/packages/aldafluxt_seo.yaml):
aldafluxt_seo:
general:
title: "Default Title"
description: "Default description."
image: "https://example.com/default-image.jpg"
basic: ~
og: ~
twitter: ~
First Use Case:
Render SEO tags in a Twig template (templates/base.html.twig):
<head>
{{ aldaflux_seo() }} {# Renders all generators #}
</head>
Controller Integration: Override SEO values dynamically in controllers:
use Symfony\Component\HttpFoundation\Response;
class ArticleController extends AbstractController
{
public function show(Article $article): Response
{
$this->get('aldaflux_seo.provider.generator')
->get('basic')
->setTitle($article->getTitle())
->setDescription($article->getExcerpt())
->setCanonical($this->generateUrl('article_show', ['id' => $article->getId()]));
return $this->render('article/show.html.twig');
}
}
Model Implementation (e.g., src/Entity/Article.php):
use Aldaflux\Bundle\SeoBundle\Seo\Basic\BasicSeoInterface;
class Article implements BasicSeoInterface
{
public function getSeoTitle(): string { return $this->title; }
public function getSeoDescription(): string { return $this->excerpt; }
public function getSeoKeywords(): string { return implode(', ', $this->tags); }
}
Controller Usage:
public function show(Article $article): Response
{
$this->get('aldaflux_seo.provider.generator')
->get('basic')
->fromResource($article);
return $this->render('article/show.html.twig');
}
Render only specific generators (e.g., Open Graph):
<head>
{{ aldaflux_seo('og') }} {# Only Open Graph tags #}
</head>
Kernel Events (e.g., src/EventListener/SeoListener.php):
use Symfony\Component\HttpKernel\Event\ResponseEvent;
class SeoListener
{
public function onKernelResponse(ResponseEvent $event)
{
if ($event->getRequest()->get('_route') === 'article_show') {
$this->get('aldaflux_seo.provider.generator')
->get('twitter')
->setCard('summary_large_image');
}
}
}
Register in services.yaml:
services:
App\EventListener\SeoListener:
tags:
- { name: kernel.event_listener, event: kernel.response }
Missing Generators:
If no tags render, ensure generators are enabled in config/packages/aldafluxt_seo.yaml:
basic: ~ {# Explicitly enable #}
og: ~
twitter: ~
Image Paths:
Use absolute URLs for image fields (e.g., https://example.com/image.jpg). Relative paths may break in production.
Canonical URLs:
Always set canonical for critical pages to avoid duplicate content issues:
->setCanonical($this->generateUrl('canonical_route', $params))
Twitter Card Validation: Test Twitter cards using Twitter’s Card Validator. Common issues:
twitter:card type (e.g., summary, summary_large_image).summary) or ≥ 300x157px (for summary_large_image).Caching:
SEO tags are rendered per-request. For static sites, cache the rendered <head> section:
{# Cache for 1 hour #}
{% cache app.seo_tags('article_' ~ article.id) %}
{{ aldaflux_seo() }}
{% endcache %}
Inspect Output: Temporarily render raw tags to debug:
{{ dump(aldaflux_seo('basic').getMetaTags()) }}
Service Container: Access generators directly for debugging:
$basicGenerator = $this->get('aldaflux_seo.provider.generator')->get('basic');
var_dump($basicGenerator->getTitle());
Configuration Overrides:
Use environment-specific configs (e.g., config/packages/dev/aldafluxt_seo.yaml) to test changes without redeploying.
Custom Generators:
Extend AbstractSeoGenerator for niche use cases (e.g., LinkedIn tags):
class LinkedInGenerator extends AbstractSeoGenerator
{
public function setLinkedInTitle($title)
{
$this->tagBuilder->addMeta('linkedin:title')->setContent($title);
return $this;
}
}
Register as a service:
services:
app.seo.linkedin:
class: App\Generator\LinkedInGenerator
arguments: ['@aldaflux_seo.builder']
tags:
- { name: aldaflux_seo.generator, alias: linkedin }
Dynamic Image Handling:
Use Symfony’s UrlGenerator to resolve image paths dynamically:
$imageUrl = $this->generateUrl('app_asset', ['path' => 'images/article.jpg']);
$this->get('aldaflux_seo.provider.generator')
->get('og')
->setImage($imageUrl);
Fallback Values:
Implement DescriptionSeoInterface for minimal resources:
class MinimalResource implements DescriptionSeoInterface
{
public function getSeoDescription(): string { return 'Fallback description'; }
}
Performance: For large-scale apps, lazy-load generators:
# config/packages/aldafluxt_seo.yaml
aldafluxt_seo:
basic: ~ {# Load only when needed #}
og: ~
twitter: ~
Load dynamically in controllers:
$this->get('aldaflux_seo.provider.generator')->get('og');
Testing: Mock generators in PHPUnit:
$mockGenerator = $this->createMock(SeoGeneratorInterface::class);
$mockGenerator->method('getMetaTags')->willReturn(['<meta name="test" content="value">']);
$container->set('aldaflux_seo.provider.generator', $this->createMock(GeneratorProvider::class));
How can I help you explore Laravel packages today?