Managing JSON-LD
Composing and rendering Schema.org structured data as JSON-LD
Managing JSON-LD
The JsonLd object holds a list of Schema.org graphs — each rendered as its own
<script type="application/ld+json"> block. A single record can carry several graphs, so a page can expose an
Article, a BreadcrumbList, and an Organization at once, following the
Google structured data guidelines.
Add the trait
use OiLab\OiLaravelMetadata\Concerns\HasJsonLd;
class Page extends Model
{
use HasJsonLd;
}The Schema builder
OiLab\OiLaravelMetadata\Support\Schema is a fluent builder for a single Schema.org node. Any method call sets
the matching schema.org property, and nested Schema nodes (or arrays of them) resolve recursively on render.
use OiLab\OiLaravelMetadata\Support\Schema;
$article = Schema::article()
->headline('About us')
->datePublished('2026-07-07')
->dateModified('2026-07-08')
->image('https://example.com/og/about.png')
->author(Schema::person()->name('Jane Doe'))
->publisher(
Schema::organization()
->name('Acme')
->logo(Schema::imageObject()->url('https://example.com/logo.png'))
);Named factories cover the common Google types — article(), newsArticle(), blogPosting(), webPage(),
webSite(), organization(), person(), imageObject(), breadcrumbList(), listItem(), product(),
offer(), faqPage(), question(), answer(). For any other type use Schema::type('Recipe').
For @-prefixed keywords that cannot be method names, use set() or the id() helper:
Schema::organization()
->id('https://example.com/#org') // sets @id
->set('@context', 'https://schema.org');Writing structured data
JsonLd::update() accepts a JsonLdData (several graphs), a single Schema, or a raw array. Writes use
updateOrCreate, so a parent keeps a single record.
use OiLab\OiLaravelMetadata\Data\JsonLdData;
use OiLab\OiLaravelMetadata\Facades\JsonLd;
use OiLab\OiLaravelMetadata\Support\Schema;
JsonLd::update($page, JsonLdData::make(
Schema::article()->headline('About us')->author(Schema::person()->name('Jane Doe')),
Schema::breadcrumbList()->itemListElement([
Schema::listItem()->set('position', 1)->name('Home')->item('https://example.com'),
Schema::listItem()->set('position', 2)->name('About')->item('https://example.com/about'),
]),
));The trait helper does the same, and accepts a single builder for the common one-graph case:
$page->syncJsonLd(Schema::webPage()->name('About us'));Rendering
Use the @jsonLd Blade directive — it accepts a model, a JsonLdData, a Schema, or a raw array:
<head>
@jsonLd($page)
</head>JsonLd::render($page); // Illuminate\Support\HtmlString
$page->renderJsonLd(); // stringEach graph becomes its own script block. A top-level @context (https://schema.org by default) is injected
when the graph does not declare one, and the JSON is encoded with JSON_HEX_TAG | JSON_HEX_AMP so <, >,
and & cannot break out of the <script> tag.
Called with no argument, @jsonLd renders the shared SEO subject (Seo::for($model)) or the current
route-bound model — see Rendering tags.
Render ad-hoc, non-persisted structured data straight from a builder:
@jsonLd(\OiLab\OiLaravelMetadata\Support\Schema::webSite()->name(config('app.name'))->url(url('/')))Reading structured data
$page->jsonLd; // JsonLd|null
$page->jsonLd?->graphs; // list<array>|null — cast from JSON
$data = JsonLd::toData($page); // JsonLdData
$data->graphs; // list<array<string, mixed>>Configuration & storage notes
graphsis stored as a JSON column and cast to an array.- The relation is
morphOne; the underlying table isjson_ldwith a unique index on(metable_type, metable_id). config('oi-laravel-metadata.json_ld.context')sets the injected@context(defaulthttps://schema.org).- Set
config('oi-laravel-metadata.json_ld.pretty')totrueto pretty-print the JSON while debugging.