Resolvers
A resolver builds SEO metadata for your own content — an Eloquent model, a page class, anything. The package itself never needs to know what that content is.
Example
Take an Article model with a title, an excerpt and a published state.
1. Implement the contract:
php
use BasekitLaravel\BasekitLaravelSeo\Contracts\SeoResolver;
use BasekitLaravel\BasekitLaravelSeo\SeoData;
final class ArticleSeoResolver implements SeoResolver
{
public function supports(mixed $subject): bool
{
return $subject instanceof Article;
}
public function resolve(mixed $subject): ?SeoData
{
if (! $subject instanceof Article || ! $subject->isPublished()) {
return null;
}
return SeoData::make()
->withTitle($subject->title)
->withDescription($subject->excerpt)
->withCanonicalUrl(route('articles.show', $subject))
->withRobots($subject->isPublic() ? 'index, follow' : 'noindex');
}
}2. Register it by tagging the class in any service provider:
php
use BasekitLaravel\BasekitLaravelSeo\SeoManager;
public function register(): void
{
$this->app->tag(ArticleSeoResolver::class, SeoManager::RESOLVER_TAG);
}3. Point the manager at your content:
php
public function show(Article $article)
{
seo()->for($article);
return view('articles.show', ['article' => $article]);
}The head component now renders that metadata. You can still override individual values per request:
php
seo()->for($article)->title('Read this first')->data();How it works
supports()decides whether this resolver handles a given subject.resolve()returns the metadata, ornullwhen nothing applies (the resolver above skips unpublished articles).- The first resolver whose
supports()returnstruewins; tag order defines precedence. - Resolver values sit above the config defaults;
seo()calls override them. - Canonical and alternate URLs are validated before they are used (http/https only, no credentials or fragments).
The metadata you build is the same SeoData described on the Page metadata page — withDescription, withOpenGraph([...]), withTwitter([...]), etc.