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

Seo Bundle Laravel Package

runroom-packages/seo-bundle

A Symfony bundle by Runroom that adds SEO tooling to your projects, helping manage meta tags, sitemaps, and related search-engine configuration with sensible defaults and easy integration.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: the package is Symfony, not Laravel. runroom-packages/seo-bundle is a Symfony bundle whose only reason to exist is adding SEO fields to entities managed through SonataAdminBundle (Doctrine ORM + Sonata Admin form mappers). There is no service provider, no Eloquent trait, no Blade directive. If you are on Laravel, you are evaluating this as prior art, not as a drop-in dependency — see the last section for the realistic options.

For a Symfony project that already runs Sonata Admin, the minimal path is:

  1. Install — composer require runroom-packages/seo-bundle. Check composer.json constraints against your Symfony/Sonata versions before committing; the repo is low-traffic (1 star, 0 dependents), so expect stale version bounds and be ready to loosen them or fork.
  2. Register the bundle — add it to config/bundles.php (or AppKernel::registerBundles() on older skeletons). Most Runroom bundles need no mandatory config; if a Configuration class exists, defaults are usually fine.
  3. Make an entity SEO-aware — implement the bundle's SEO interface and use its trait (look in src/Model/). This adds the field set: title, description/keywords, noIndex/noFollow, canonical URL, and typically Open Graph/Twitter overrides plus sitemap priority/changefreq.
  4. Generate schema — bin/console doctrine:schema:update --dump-sql or make:migration/doctrine:migrations:diff. The trait contributes mapped columns, nothing else.
  5. Let Sonata pick it up — the bundle ships an AbstractAdminExtension that injects an "SEO" tab into any admin whose subject implements the interface. Register it with the sonata.admin.extension tag (usually autoconfigured) and it applies globally.
  6. Render the tags — check src/Twig/ and Resources/views/ for the exposed helper; call it in your base layout <head>. Edit a record in the admin, then view source to confirm output.

First use case: a Page or Post entity — add the trait, get a SEO tab in the existing admin form for free, and replace hand-written <title>/<meta name="description"> in the layout with the bundle's renderer.

Where to look, in order: README.md → src/Model/ (exact field list and interface name) → src/Admin/ (how the tab is added and whether it is conditional) → src/Twig/ + Resources/views/ (rendering contract) → DependencyInjection/Configuration.php.

Implementation Patterns

Trait + interface is the whole integration contract. Everything else (admin tab, rendering, sitemap hooks) keys off $entity instanceof SeoInterface. Keep the interface on a base entity or a shared SeoAwareTrait of your own if you have many SEO-able models.

class Page implements SeoInterface
{
    use SeoEntityTrait; // fields + getters/setters from src/Model
}

Fallback chain in the renderer. The useful day-to-day pattern is: entity SEO field → site-wide default (from parameters) → derived value (entity title, current URL for canonical). Do this in a thin decorator around the bundle's renderer rather than editing vendor templates, so upgrades stay cheap.

Per-admin control. The admin extension typically applies to every admin implementing the interface. If one admin shouldn't expose SEO (e.g. a join table or an internal log), don't implement the interface there rather than fighting the extension. If the extension supports a target/admin-code option, use it; otherwise extend the shipped extension and override the method that decides whether to add the tab.

Locale handling is yours. Trait fields are plain columns. For multi-language sites you need either a per-locale entity (one row per locale — simplest, works with Sonata's TranslatableAdmin) or an explicit translation relation. Do not assume the bundle handles this.

Rendering pattern (names vary — confirm against src/Twig):

{# templates/base.html.twig #}
<head>
    <title>{% block title %}{{ seo_title(entity|default(null)) }}{% endblock %}</title>
    {{ seo_meta_tags(entity|default(null)) }}
</head>

and override the bundle's template in templates/bundles/RunroomSeoBundle/... if you need different tag order or extra hreflang output.

Headless / API use. Since the fields are just Doctrine columns, serialise them (seo.title, seo.description, seo.noIndex, seo.canonicalUrl) and let the front-end emit tags. This is often the better integration point than the Twig renderer.

Sitemap. If you need XML sitemaps, pair this with runroom-packages/sitemap-bundle or Sonata's SEO bundle — this package's priority/changefreq fields are metadata, not a generator.

If you are coming from Laravel: treat the field set and admin UX as the spec and reimplement it. spatie/laravel-sitemap + artesaos/seotools cover the generation side; a seo_meta JSON column or a HasSeo trait plus a Filament/Nova fieldset reproduces the editing side in an afternoon. Borrow the column names above so a future content migration is mechanical.

Gotchas and Tips

  • Dependency rot is the main risk. Zero dependents and 1 star means nobody is filing upgrade issues. Pin the version, and expect to patch composer.json constraints for current Symfony/Sonata majors. Budget for a fork.
  • The admin extension is global by default. After installing, every admin implementing the interface gets the tab — including ones you didn't think about. Audit implements SeoInterface across the codebase before deploying.
  • Trait columns break existing rows. Non-nullable booleans (noIndex, noFollow) and non-nullable strings need defaults in the generated migration, or the ALTER TABLE fails on populated tables. Review doctrine:migrations:diff output before running it.
  • Column name collisions. title and description are extremely common column names. The trait will clash with existing columns; rename yours or use the interface without the trait and map the getters manually.
  • No validation of meta length. Nothing stops an editor pasting 400 characters into the description. Add @Assert\Length on the entity (or constraint config if the bundle exposes it) — this is the single highest-value addition you can make.
  • Canonical URLs: absolute vs relative. Render absolute URLs from the request context; a relative canonical is ignored by most crawlers. Also be careful that noIndex doesn't conflict with a sitemap inclusion rule.
  • Escaping. Descriptions and OG titles are editor-supplied. Ensure the renderer escapes attribute values (Twig autoescape covers {{ }}, but not if a template uses |raw for JSON-LD).
  • HTTP caching. The rendered <head> is part of the cacheable response. Editing SEO fields in the admin must invalidate that page's cache — wire it into your existing purger, or editors will report "changes don't show up".
  • Debugging checklist: bin/console debug:container | grep -i seo for services, bin/console debug:twig for the extension, and bin/console debug:container --tag=sonata.admin.extension to confirm the extension is tagged. If the tab is missing, it's almost always a missing tag, a wrong target config, or the admin subject not implementing the interface (proxy classes and inherited interfaces count — verify with instanceof in a test).
  • Extension points, cheapest to most invasive: override templates → decorate the renderer service → extend/subclass the admin extension → fork. Stay in the first two.
  • Test it functionally. One test that renders a page with noIndex: true and asserts robots: noindex, nofollow plus the canonical is worth more than any unit test of getters, and it catches renderer regressions after upgrades.
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.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log