The HasSeo trait is the foundation of ArtisanPack UI SEO. This guide covers all available methods and customization options for integrating SEO with your Eloquent models.
Adding the Trait
<?php
namespace App\Models;
use ArtisanPackUI\Seo\Traits\HasSeo;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use HasSeo;
}
Relationship
The trait establishes a polymorphic relationship:
// Access the SEO meta record directly
$seoMeta = $post->seoMeta;
// The relationship is defined as:
public function seoMeta(): MorphOne
{
return $this->morphOne(SeoMeta::class, 'seoable');
}
Core Methods
getOrCreateSeoMeta()
Get the existing SEO meta record or create a new one:
$meta = $post->getOrCreateSeoMeta();
// Returns SeoMeta model instance
// Creates one if it doesn't exist
updateSeoMeta(array $data)
Update SEO metadata with the provided data:
$post->updateSeoMeta([
'meta_title' => 'Custom Title',
'meta_description' => 'Custom description.',
'og_title' => 'Social Title',
'noindex' => false,
]);
// Only updates provided fields
// Returns the updated SeoMeta model
getSeoData()
Get all SEO data as an array (with fallbacks applied):
$data = $post->getSeoData();
// Returns array with all SEO fields:
// [
// 'title' => 'Effective Title',
// 'description' => 'Effective Description',
// 'image' => 'https://...',
// 'og_title' => '...',
// 'og_description' => '...',
// 'twitter_card' => '...',
// 'schema_type' => '...',
// 'noindex' => false,
// 'nofollow' => false,
// ...
// ]
Title Methods
getSeoTitle()
Get the effective SEO title with fallbacks:
$title = $post->getSeoTitle();
// Fallback order:
// 1. meta_title from SEO meta
// 2. Model's 'title' attribute
// 3. Model's 'name' attribute
// 4. Model class name
getSeoTitleAttribute()
Override this method to customize title resolution:
protected function getSeoTitleAttribute(): ?string
{
return $this->headline ?? $this->title;
}
getSeoTitleWithSuffix()
Get title with site name appended:
$fullTitle = $post->getSeoTitleWithSuffix();
// Result: "Post Title | My Site"
// Custom suffix
$fullTitle = $post->getSeoTitleWithSuffix(' - Custom Site');
// Result: "Post Title - Custom Site"
Description Methods
getSeoDescription()
Get the effective SEO description with fallbacks:
$description = $post->getSeoDescription();
// Fallback order:
// 1. meta_description from SEO meta
// 2. Model's 'description' attribute
// 3. Model's 'excerpt' attribute
// 4. Truncated 'content' attribute
// 5. Site default from config
getSeoDescriptionAttribute()
Override this method to customize description resolution:
protected function getSeoDescriptionAttribute(): ?string
{
return $this->summary ?? $this->excerpt ?? $this->description;
}
Image Methods
getSeoImage()
Get the effective SEO image URL:
$image = $post->getSeoImage();
// Fallback order:
// 1. og_image from SEO meta
// 2. Image from og_image_id (Media Library)
// 3. Model's 'image' attribute
// 4. Model's 'featured_image' attribute
// 5. Default image from config
getSeoImageAttribute()
Override this method to customize image resolution:
protected function getSeoImageAttribute(): ?string
{
return $this->cover_image ?? $this->thumbnail;
}
URL Methods
getSeoUrl()
Get the canonical URL for the model:
$url = $post->getSeoUrl();
// Default implementation:
// Returns the model's 'url' attribute
// Or generates from route if RouteModelBinding is used
getSeoUrlAttribute()
Override to customize URL generation:
protected function getSeoUrlAttribute(): ?string
{
return route('posts.show', $this);
}
Indexing Methods
shouldBeIndexed()
Check if the page should be indexed by search engines:
if ($post->shouldBeIndexed()) {
// robots: index
}
// Returns true unless noindex is explicitly set
shouldBeFollowed()
Check if links should be followed:
if ($post->shouldBeFollowed()) {
// robots: follow
}
// Returns true unless nofollow is explicitly set
getRobotsDirective()
Get the full robots meta content:
$robots = $post->getRobotsDirective();
// Result: "index, follow" or "noindex, nofollow" etc.
Sitemap Methods
shouldBeInSitemap()
Check if the model should be included in sitemaps:
if ($post->shouldBeInSitemap()) {
// Include in sitemap
}
// Returns false if:
// - noindex is true
// - exclude_from_sitemap is true
getSitemapPriority()
Get the sitemap priority for this model:
$priority = $post->getSitemapPriority();
// Returns: 0.0 to 1.0
getSitemapChangefreq()
Get the sitemap change frequency:
$changefreq = $post->getSitemapChangefreq();
// Returns: always, hourly, daily, weekly, monthly, yearly, never
getSitemapLastmod()
Get the last modification date for sitemap:
$lastmod = $post->getSitemapLastmod();
// Returns: Carbon instance or null
Schema Methods
getSchemaType()
Get the Schema.org type for this model:
$type = $post->getSchemaType();
// Returns: Article, BlogPosting, Product, etc.
getSchemaData()
Get the custom schema data:
$schemaData = $post->getSchemaData();
// Returns: array of schema properties
buildSchema()
Build the complete schema markup:
$schema = $post->buildSchema();
// Returns: Complete JSON-LD ready array
Open Graph Methods
getOpenGraphData()
Get all Open Graph data:
$og = $post->getOpenGraphData();
// Returns array:
// [
// 'title' => '...',
// 'description' => '...',
// 'image' => '...',
// 'type' => '...',
// 'url' => '...',
// 'site_name' => '...',
// 'locale' => '...',
// ]
Twitter Card Methods
getTwitterCardData()
Get all Twitter Card data:
$twitter = $post->getTwitterCardData();
// Returns array:
// [
// 'card' => 'summary_large_image',
// 'title' => '...',
// 'description' => '...',
// 'image' => '...',
// 'site' => '@...',
// 'creator' => '@...',
// ]
Hreflang Methods
getHreflang()
Get hreflang URLs:
$hreflang = $post->getHreflang();
// Returns array:
// [
// 'en' => 'https://...',
// 'fr' => 'https://...',
// 'x-default' => 'https://...',
// ]
setHreflang(array $urls)
Set hreflang URLs:
$post->setHreflang([
'en' => 'https://example.com/post',
'fr' => 'https://example.fr/article',
]);
Analysis Methods
getSeoAnalysis()
Get SEO analysis results:
$analysis = $post->getSeoAnalysis();
// Returns array of analyzer results:
// [
// 'score' => 75,
// 'readability' => ['status' => 'pass', 'message' => '...'],
// 'keyword_density' => ['status' => 'warning', 'message' => '...'],
// ...
// ]
refreshSeoAnalysis()
Force refresh the SEO analysis:
$analysis = $post->refreshSeoAnalysis();
// Clears cache and re-runs all analyzers
Model Events
The trait automatically registers model observers:
// On model created
// - Creates default SEO meta record (if auto_create enabled)
// On model updated
// - Clears SEO cache
// - Updates sitemap entry
// On model deleted
// - Deletes associated SEO meta
// - Removes from sitemap
Customization Example
Here's a complete example of customizing SEO for a model:
<?php
namespace App\Models;
use ArtisanPackUI\Seo\Traits\HasSeo;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
use HasSeo;
// Custom title resolution
protected function getSeoTitleAttribute(): ?string
{
return $this->product_name . ' - ' . $this->brand->name;
}
// Custom description
protected function getSeoDescriptionAttribute(): ?string
{
return $this->short_description ?? substr($this->description, 0, 160);
}
// Custom image
protected function getSeoImageAttribute(): ?string
{
return $this->images->first()?->url ?? $this->category->default_image;
}
// Custom URL
protected function getSeoUrlAttribute(): ?string
{
return route('products.show', [
'category' => $this->category->slug,
'product' => $this->slug,
]);
}
// Custom schema type
public function getSchemaType(): string
{
return 'Product';
}
// Custom schema data
public function getSchemaData(): array
{
return [
'name' => $this->product_name,
'description' => $this->description,
'sku' => $this->sku,
'brand' => ['@type' => 'Brand', 'name' => $this->brand->name],
'offers' => [
'@type' => 'Offer',
'price' => $this->price,
'priceCurrency' => 'USD',
'availability' => $this->in_stock
? 'https://schema.org/InStock'
: 'https://schema.org/OutOfStock',
],
];
}
}
Next Steps
- Meta Tags - Meta tag management
- Social Media - Open Graph and Twitter Cards
- Schema.org - Structured data
- Models Reference - SeoMeta model documentation

