SeoService
Registered as service('seo'). Builds SEO metadata arrays and JSON-LD structured data for posts, pages, and other entities.
getMeta(object $entity): array
Builds a complete SEO meta array for a content entity.
$meta = service('seo')->getMeta($post);
Returns an associative array:
[
'title' => 'Post Title — Site Name',
'description' => '150-char excerpt from meta_description or post body',
'og_title' => 'Post Title',
'og_description' => 'Same description string',
'og_image' => 'https://example.com/uploads/featured.webp',
'og_url' => 'https://example.com/post/my-post-slug',
'canonical' => 'https://example.com/post/my-post-slug',
]
Source Priority
| Meta field | Priority order |
|---|---|
title | entity.meta_title → entity.title → site_name |
description | entity.meta_description → excerpt(entity.body, 155) → site_tagline |
og_image | entity.featured_image → OgImageService-generated PNG → null |
getJsonLd(object $post, ?object $authorProfile): string
Returns a JSON-encoded <script type="application/ld+json"> block for an Article entity.
$jsonLd = service('seo')->getJsonLd($post, $authorProfile);
Output structure (JSON-LD Article schema):
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "Post Title",
"description": "Meta description...",
"image": "https://example.com/uploads/featured.webp",
"author": {
"@type": "Person",
"name": "Author Name",
"url": "https://example.com/author/username"
},
"publisher": {
"@type": "Organization",
"name": "Site Name",
"logo": {"@type": "ImageObject", "url": "https://example.com/..."}
},
"datePublished": "2026-01-15T10:00:00+00:00",
"dateModified": "2026-01-20T14:30:00+00:00"
}
$authorProfile is the record from author_profiles table (or null). If null, the author block uses the post's username from the users join.
Usage in Theme Views
The meta array is passed to the theme layout by public controllers:
// In Blog::post() controller
$meta = service('seo')->getMeta($post);
return service('theme')->view('blog/post.tpl', [
'post' => $post,
'meta' => $meta,
'json_ld' => service('seo')->getJsonLd($post, $authorProfile),
]);
In the layout template:
<title>{{ meta.title }}</title>
<meta name="description" content="{{ meta.description }}">
<meta property="og:title" content="{{ meta.og_title }}">
<meta property="og:image" content="{{ meta.og_image }}">
{! json_ld | raw !}