oi-lab / oi-laravel-metadata
Polymorphic SEO metadata and Open Graph management for Laravel applications
Requires
- php: ^8.2
- illuminate/contracts: ^11.0|^12.0|^13.0
- illuminate/database: ^11.0|^12.0|^13.0
- illuminate/http: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
- spatie/laravel-data: ^4.0
Requires (Dev)
- laravel/pint: ^1.29
- orchestra/testbench: ^9.0|^10.0|^11.0
- pestphp/pest: ^3.0|^4.0
- pestphp/pest-plugin-laravel: ^3.0|^4.0
README
OI Laravel Metadata
A Laravel package to attach polymorphic SEO metadata and Open Graph data to any Eloquent model —
one record of each per parent — with spatie/laravel-data DTOs, dedicated services, validation rules, head-tag
rendering, and host-application Setting model integration.
Features
- Polymorphic Metadata & Open Graph: a single
metadataandopenGraphrecord per model (morphOne) - Traits: opt in with
HasMetadata,HasOpenGraph, or the combinedHasMeta - DTOs: typed
MetadataData,OpenGraphData,OpenGraphImageData(spatie/laravel-data) - Services & Facades:
MetaService/OgService(Meta/Og) to read, write, and render - Head-tag Rendering:
Meta::render($model)andOg::render($model)emit escaped<meta>tags - Validation:
MetadataRequest/OpenGraphRequestform requests, plusIsoLanguageRule&RobotsRule - Setting Integration: seeds and resolves site-wide values from a host
Settingmodel when present
The Objects
A Metadata object:
| Field | Type |
|---|---|
title |
string |
description |
string |
keywords |
string[] |
author |
string |
copyright |
string |
language |
ISO code (e.g. fr, en) |
revisit_after |
string |
robots |
string |
googlebot |
string |
An Open Graph object:
| Field | Type |
|---|---|
type |
string |
title |
string |
description |
string |
url |
string |
image |
object (url, width, height) |
Both are polymorphic, with at most one per parent (enforced by a unique index on the morph columns).
Requirements
- PHP 8.2+
- Laravel 11.0+, 12.0+, or 13.0+
spatie/laravel-data^4.0
Installation
composer require oi-lab/oi-laravel-metadata
The package auto-discovers its service provider. Publish and migrate:
php artisan vendor:publish --tag=oi-laravel-metadata-migrations php artisan vendor:publish --tag=oi-laravel-metadata-config php artisan migrate
This creates the metadata and open_graphs tables.
Local Development
Inside the monorepo, add a path repository to your main project's composer.json:
{
"repositories": [
{ "type": "path", "url": "./packages/oi-lab/oi-laravel-metadata" }
]
}
Usage
Make a Model Meta-aware
use Illuminate\Database\Eloquent\Model; use OiLab\OiLaravelMetadata\Concerns\HasMeta; class Page extends Model { use HasMeta; // or HasMetadata / HasOpenGraph individually }
$page->metadata; // MorphOne — Metadata|null $page->openGraph; // MorphOne — OpenGraph|null
Write Values
use OiLab\OiLaravelMetadata\Data\MetadataData; use OiLab\OiLaravelMetadata\Data\OpenGraphData; use OiLab\OiLaravelMetadata\Data\OpenGraphImageData; use OiLab\OiLaravelMetadata\Facades\Meta; use OiLab\OiLaravelMetadata\Facades\Og; Meta::update($page, new MetadataData( title: 'About us', description: 'Who we are', keywords: ['team', 'company'], author: 'OI Lab', language: 'fr', robots: 'index, follow', )); Og::update($page, new OpenGraphData( type: 'website', title: 'About us', url: 'https://example.com/about', image: new OpenGraphImageData('https://example.com/og.png', 1200, 630), ));
The trait helpers $page->syncMetadata(...) and $page->syncOpenGraph(...) do the same. Writes use
updateOrCreate, so a parent never ends up with a second record.
Render <head> Tags
<head> {!! Meta::render($page) !!} {!! Og::render($page) !!} </head>
Meta::render() outputs description, keywords, author, copyright, language, revisit-after,
robots, and googlebot tags, plus google-site-verification / google verification tags resolved from
settings. Og::render() outputs the og:* tags plus og:locale, og:site_name, and fb:app_id from
settings. Empty values are omitted; all values are HTML-escaped.
Validation
use OiLab\OiLaravelMetadata\Http\Requests\MetadataRequest; use OiLab\OiLaravelMetadata\Http\Requests\OpenGraphRequest; public function update(MetadataRequest $request, Page $page) { Meta::update($page, MetadataData::from($request->validated())); }
The IsoLanguageRule and RobotsRule rules are reusable on their own.
Setting Model Integration
If your application has a key/value Setting model, seed the package defaults:
php artisan metadata:install-settings
This inserts the following keys (idempotently — existing keys are never overwritten):
| Key | Default |
|---|---|
METADATA_FACEBOOK_APP_ID |
"" |
METADATA_GOOGLE_SITE_VERIFICATION |
"" |
METADATA_GOOGLE_BOT |
"" |
METADATA_GOOGLE |
"" |
METADATA_ROBOTS |
index, follow |
METADATA_OG_LOCALE |
fr |
METADATA_OG_SITE_NAME |
"" |
METADATA_OG_TYPE |
website |
Configure the model class and columns in config/oi-laravel-metadata.php:
'settings' => [ 'model' => App\Models\Setting::class, 'key_column' => 'key', 'value_column' => 'value', 'defaults' => [ /* ... */ ], ],
When no Setting model is present, the resolver and installer no-op gracefully and fall back to config
defaults.
Overriding Models
Resolve models through OiMetadata so your overrides apply everywhere:
// config/oi-laravel-metadata.php 'models' => [ 'metadata' => App\Models\Metadata::class, // extends OiLab\OiLaravelMetadata\Models\Metadata 'open_graph' => App\Models\OpenGraph::class, // extends OiLab\OiLaravelMetadata\Models\OpenGraph ],
AI Assistant Skills
php artisan oi:skills
Testing
composer test
License
The MIT License (MIT). Please see the License File for more information.
Credits
Olivier Lacombe - Creator and maintainer
Olivier is a Product & Technology Director based in Montpellier, France, with over 20 years of experience innovating in UX/UI and emerging technologies. He specializes in guiding enterprises toward cutting-edge digital solutions, combining user-centered design with continuous optimization and artificial intelligence integration.
Projects & Resources:
- OI Dev Docs - Documentation for all Open Source OI Lab packages
- OnAI - Training courses and masterclasses on generative AI for businesses
- Promptr - Prompt engineering Management Platform
Support
For support, please open an issue on the GitHub repository.
