beyerz/open-graph-protocol-bundle
Installation:
composer require beyerz/open-graph-protocol-bundle
Add the bundle to config/bundles.php (Symfony 3.4+):
return [
// ...
Beyerz\OpenGraphProtocolBundle\OpenGraphProtocolBundle::class => ['all' => true],
];
Basic Configuration (config/packages/open_graph_protocol.yaml):
open_graph_protocol:
libraries:
base:
default: true
title: "Default Title"
description: "Default Description"
image: "%kernel.project_dir%/public/images/default-ogp.jpg"
First Use Case:
Inject the OpenGraphProtocolService in a controller and set metadata:
use Beyerz\OpenGraphProtocolBundle\Service\OpenGraphProtocolService;
class MyController extends AbstractController
{
public function show(OpenGraphProtocolService $ogpService)
{
$ogpService->setTitle('My Page Title');
$ogpService->setDescription('My page description');
$ogpService->setImage('/images/my-image.jpg');
return $this->render('my_template.html.twig');
}
}
Twig Integration:
Use the ogp Twig extension to render metadata in your layout:
<head>
{{ ogp('base') }}
</head>
Dynamic Metadata:
public function showPost(Post $post, OpenGraphProtocolService $ogpService)
{
$ogpService->setTitle($post->getTitle());
$ogpService->setDescription($post->getExcerpt());
$ogpService->setImage($post->getFeaturedImageUrl());
$ogpService->setUrl($this->generateUrl('post_show', ['id' => $post->getId()]));
// Add Twitter Card metadata
$ogpService->setTwitterCard('summary_large_image');
$ogpService->setTwitterSite('@your_handle');
$ogpService->setTwitterCreator('@author_handle');
return $this->render('post/show.html.twig');
}
Reusable Metadata: Create a base controller to handle common OGP logic:
abstract class BaseController extends AbstractController
{
protected function configureOgp(OpenGraphProtocolService $ogpService, $routeParams)
{
$ogpService->setSiteName('My Site');
$ogpService->setLocale('en_US');
$ogpService->setUrl($this->generateUrl('homepage'));
// Route-specific logic
if (isset($routeParams['id'])) {
$ogpService->setTitle("Item #{$routeParams['id']}");
}
}
}
Conditional Rendering:
<head>
{% if app.request.get('_route') == 'post_show' %}
{{ ogp('base', {'title': post.title, 'image': post.imageUrl}) }}
{% else %}
{{ ogp('base') }}
{% endif %}
</head>
Multiple Libraries:
<head>
{{ ogp('base') }}
{{ ogp('facebook', {'app_id': '123456'}) }}
{{ ogp('twitter') }}
</head>
Listen to kernel events to set OGP metadata globally:
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
class OgpSubscriber implements EventSubscriber
{
public function onKernelResponse(FilterResponseEvent $event)
{
$request = $event->getRequest();
$ogpService = $event->getContainer()->get('open_graph_protocol.service');
if ($request->attributes->get('_route') === 'post_show') {
$post = $request->attributes->get('post');
$ogpService->setTitle($post->getTitle());
// ... other metadata
}
}
public static function getSubscribedEvents()
{
return [
'kernel.response' => 'onKernelResponse',
];
}
}
For SPAs or API-first apps, expose OGP metadata via an endpoint:
public function getOgpData(OpenGraphProtocolService $ogpService, Request $request)
{
$ogpService->setTitle('API Data - ' . $request->query->get('query'));
$ogpService->setDescription('Search results for: ' . $request->query->get('query'));
return new JsonResponse($ogpService->getMetadata());
}
Symfony Version Compatibility:
v1.0 of the bundle (traits for container awareness).Default Values Override:
config.yml are merged with runtime values. Runtime values take precedence.default: true is set for base, but you call setTitle() in code, the code value wins.Image Paths:
https://example.com/image.jpg) or paths relative to the public directory (e.g., /images/image.jpg).kernel.project_dir in runtime calls; use the service's setImage() with relative paths.Metadata Inspection:
$ogpService->getMetadata() to debug current metadata before rendering:
dump($ogpService->getMetadata());
Twig Debugging:
{{ dump(ogp('base')) }} in a template.Missing Metadata:
config/bundles.php.config/packages/open_graph_protocol.yaml (e.g., default: true).var/log/dev.log).Custom Libraries:
Add new libraries in config.yml:
open_graph_protocol:
libraries:
linkedin:
default: false
title: "LinkedIn Title"
description: "LinkedIn Description"
image: "/images/linkedin-share.jpg"
url: "https://www.linkedin.com/sharing/"
Then render with {{ ogp('linkedin') }}.
Custom Metadata: Extend the service to add custom tags:
$ogpService->addCustomTag('<meta property="article:published_time" content="2023-01-01T00:00:00+00:00">');
Override Service:
Create a custom service class extending OpenGraphProtocolService to add methods like:
public function setArticleMetadata($publishedTime, $author, $section)
{
$this->addCustomTag(sprintf(
'<meta property="article:published_time" content="%s">',
$publishedTime
));
$this->addCustomTag(sprintf(
'<meta property="article:author" content="%s">',
$author
));
// ... other article-specific tags
}
Then bind your custom service in services.yaml:
services:
App\Service\CustomOpenGraphService:
decorates: 'open_graph_protocol.service'
arguments: ['@.inner']
Cache Metadata: For static pages, cache the rendered OGP HTML:
$cacheKey = 'ogp_' . md5($request->getUri());
$ogpHtml = $cache->get($cacheKey);
if (!$ogpHtml) {
$ogpHtml = $ogpService->render('base');
$cache->set($cacheKey, $ogpHtml, 3600); // Cache for 1 hour
}
Avoid Runtime Overhead: Set metadata as early as possible in the request lifecycle (e.g., in a subscriber) to minimize runtime processing.
Relative vs. Absolute URLs:
setImage() uses a relative path (e.g., /images/ogp.jpg), ensure the base URL is correctly set in the rendered output.https://example.com/images/ogp.jpg).Missing ogp Twig Function:
{{ ogp('base') }} fails, ensure the Twig extension is registered. Check for errors in var/log/dev.log.Facebook Debugger Issues:
og:image and `og:urlHow can I help you explore Laravel packages today?