chamber-orchestra / meta
Symfony 8 library providing a Doctrine ORM trait for SEO meta fields — title, description, keywords, Open Graph image, and robots behaviour
Installs: 1
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/chamber-orchestra/meta
Requires
- php: ^8.5
- chamber-orchestra/view-bundle: 8.0.*
- doctrine/orm: ^3.0
- symfony/http-foundation: 8.0.*
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.0
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^13.0
README
Symfony 8 library providing a Doctrine ORM trait for SEO meta fields — title, description, keywords, Open Graph image, and robots behaviour. Mix into any Doctrine entity with zero boilerplate.
Features
MetaTrait— adds SEO fields to any Doctrine entity via a singleusestatementRobotsBehaviour— int-backed PHP enum withformat()for robots meta tag output (index, follow,noindex, nofollow, etc.)getMeta()— returns a clean associative array for rendering, with automatic HTML stripping on descriptions- Native Doctrine enum mapping —
RobotsBehaviourstored asSMALLINTwithenumType, hydrated directly as an enum case - File-bundle integration — transient
File $metaImageproperty with#[UploadableProperty]for automatic image upload handling viachamber-orchestra/file-bundle
Requirements
- PHP ^8.5
- Symfony ^8.0
- Doctrine ORM ^3.0
chamber-orchestra/view-bundle^8.0chamber-orchestra/file-bundle(in consuming application, for image upload support)
Installation
composer require chamber-orchestra/meta
Usage
1. Add meta fields to your entity
use ChamberOrchestra\FileBundle\Mapping\Annotation as Upload; use ChamberOrchestra\Meta\Entity\MetaTrait; use Doctrine\ORM\Mapping as ORM; #[ORM\Entity] #[Upload\Uploadable(prefix: 'article')] class Article { use MetaTrait; #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column] private ?int $id = null; // your entity fields... }
The #[Uploadable] attribute is required for file-bundle to handle the metaImage upload automatically.
This adds the following columns and properties:
| Property | Type | Persisted | Description |
|---|---|---|---|
title |
string |
yes | Page title (H1) |
metaTitle |
string |
yes | <title> / og:title |
metaImage |
File |
no | Transient upload (file-bundle) |
metaImagePath |
string |
yes | Social share image path |
metaDescription |
text |
yes | Meta description |
metaKeywords |
string |
yes | Meta keywords |
robotsBehaviour |
smallint |
yes | Robots enum (default: 1) |
2. Render meta tags in Twig
Pass the getMeta() array and the entity to your base layout, then build the <head>:
{# base.html.twig #} {% set meta = entity.meta %} {%- set title -%} {{ "meta.title.common"|trans }}{{ meta.title|default('') ? ' | ' ~ meta.title }} {%- if app.request.query.has("page") and app.request.query.get("page") > 1 -%} | {{ "meta.title.page"|trans({"page": app.request.query.get("page")}) -}} {%- endif -%} {%- endset -%} {%- set title = title|replace({"\n": "", "\r\n": "", "\t": "", "\n\r": ""})|trim -%} {%- set description = meta.description|default("meta.description.common"|trans) -%} {%- set keywords = meta.keywords|default("meta.keywords.common"|trans) -%} {%- set socialTitle = meta.title|default(title) -%} {%- set socialDescription = meta.description|default(description) -%} <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <title>{{ title }}</title> {%- if not app.request.query.has("page") and not app.request.query.has("filter") and not app.request.query.has("sort") and not app.request.query.has("order") and not app.request.query.has("limit") -%} <meta name="keywords" content="{{ keywords }}"> <meta name="description" content="{{ description }}"> {%- endif %} <meta name="robots" content="{{ entity.formattedRobotsBehaviour }}"> <meta property="og:url" content="{{ app.request.uri }}"> <meta property="og:title" content="{{ socialTitle }}"> <meta property="og:description" content="{{ socialDescription }}"> {%- if meta.image %} <meta property="og:image" content="{{ absolute_url(meta.image) }}"> {%- endif %} <link rel="canonical" href="{{ app.request.uri }}"> <link rel="icon" type="image/svg+xml" href="/favicon.svg"> <link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96"> <link rel="shortcut icon" href="/favicon.ico"> <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"> <link rel="manifest" href="/site.webmanifest"> <meta name="msapplication-TileColor" content="#da532c"> <meta name="theme-color" content="#ffffff"> {% block stylesheets %}{% endblock %} </head>
The getMeta() method automatically strips HTML tags from the description.
3. Robots behaviour enum
use ChamberOrchestra\Meta\Entity\Helper\RobotsBehaviour; $entity->getRobotsBehaviour(); // RobotsBehaviour::IndexNoFollow $entity->getFormattedRobotsBehaviour(); // "index, nofollow" RobotsBehaviour::NoIndexNoFollow->format(); // "noindex, nofollow"
Available cases:
| Case | Value | Output |
|---|---|---|
IndexFollow |
0 | index, follow |
IndexNoFollow |
1 | index, nofollow |
NoIndexFollow |
2 | noindex, follow |
NoIndexNoFollow |
3 | noindex, nofollow |
Testing
composer install
composer test
composer analyse
composer cs-check
License
MIT