SEO - v1.0.0
Model Integration
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