1. Go to this page and download the library: Download shammaa/laravel-seo library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
shammaa / laravel-seo example snippets
'site' => [
'name' => 'Your Site Name',
'description' => 'Your site description',
'url' => 'https://yoursite.com',
'logo' => 'path/to/logo.jpg',
],
use Shammaa\LaravelSEO\Services\SEOService;
// In AppServiceProvider or a dedicated SEOServiceProvider
public function boot(): void
{
SEOService::setSiteDataResolver(function () {
$settings = cache()->remember('site_settings', 86400, function () {
return \App\Models\Setting::first();
});
return [
'name' => $settings->site_name ?? config('app.name'),
'description' => $settings->site_description ?? '',
'logo' => $settings->site_logo ?? null,
'publisher' => $settings->publisher_name ?? $settings->site_name,
// Social media links for sameAs in Organization schema
'same_as' => array_filter([
$settings->facebook_url,
$settings->twitter_url,
$settings->instagram_url,
$settings->youtube_url,
$settings->linkedin_url,
$settings->telegram_url,
]),
];
});
}
// In your Settings controller after update:
cache()->forget('site_settings');
cache()->forget('seo_site_data_' . app()->getLocale());
use Shammaa\LaravelSEO\Facades\SEO;
public function show(Post $post)
{
SEO::post($post)->set();
return view('post.show', compact('post'));
}
public function index()
{
SEO::home()->set();
return view('home');
}
public function show(Post $post)
{
SEO::post($post)->set();
return view('post.show', compact('post'));
}
public function show(Category $category)
{
SEO::category($category)->set();
return view('category.show', compact('category'));
}
public function show(Product $product)
{
SEO::product($product)->set();
return view('product.show', compact('product'));
}
public function search(Request $request)
{
SEO::search(['query' => $request->get('q')])->set();
return view('search.results');
}
public function show(Tag $tag)
{
SEO::tag($tag)->set();
return view('tag.show', compact('tag'));
}
public function show(Author $author)
{
SEO::author($author)->set();
return view('author.show', compact('author'));
}
public function archive(string $date)
{
SEO::archive($date)->set();
return view('archive.show', compact('date'));
}
public function show(Page $page)
{
SEO::page($page)->set();
return view('page.show', compact('page'));
}
public function show(CustomModel $model)
{
SEO::for('custom', $model)->set();
return view('custom.show', compact('model'));
}
public function show(Post $post)
{
// 1. Just set the author model (Auto-detects URL/Image)
SEO::post($post)->withAuthor($user)->set();
// 2. Override Author URL and Image Field Name
SEO::post($post)
->withAuthor(
$user,
url: 'https://mysite.com/authors/custom-url', // Custom URL
image: 'avatar_url' // Custom image field name in User model
)
->set();
// 3. Override Author URL and Image URL directly
SEO::post($post)
->withAuthor(
$user,
url: 'https://mysite.com/authors/me',
image: 'https://mysite.com/images/me.jpg' // Direct Image URL
)
// 4. Use Route Name (Recommended)
// Automatically calls route('authors.show', $user)
SEO::post($post)
->withAuthor($user, 'authors.show')
->set();
return view('post.show', compact('post'));
}
SEO::post($post)->set()->addVideo([
'video_url' => 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
'title' => 'My Video Title', // Optional, defaults to page title
'description' => 'Video description', // Optional, defaults to page description
'image' => 'https://example.com/thumb.jpg', // Optional, defaults to page image
'uploadDate' => '2024-01-01', // Optional
]);
// In Post Model
public function getVideoUrlAttribute()
{
// Return Youtube/Vimeo URL
return $this->meta['video_link'] ?? null;
}
SEO::post($article)->set()->addFAQ([
[
'question' => 'What is this about?',
'answer' => 'This article explains...'
],
[
'question' => 'How does it work?',
'answer' => 'It works by...'
],
]);
// In your Model
class Post extends Model
{
use HasSEO;
public function faqs()
{
return $this->hasMany(FAQ::class);
}
}
// In Controller - Automatic!
SEO::post($post)->set(); // Automatically detects and adds FAQs
SEO::post($tutorial)->set()->addHowTo(
name: 'How to Cook Kabsa',
steps: [
'Wash the rice thoroughly',
'Cook the meat with spices',
'Mix rice with meat',
'Cook on low heat for 30 minutes'
],
description: 'A simple guide to cooking traditional Kabsa',
image: '/images/kabsa.jpg'
);
SEO::post($tutorial)->set()->addHowTo(
name: 'How to Build a Website',
steps: [
[
'name' => 'Choose a Domain',
'text' => 'Select and register your domain name',
'image' => '/images/step1.jpg',
'url' => '/steps/1'
],
[
'name' => 'Set Up Hosting',
'text' => 'Choose a hosting provider and set up your account',
'image' => '/images/step2.jpg'
],
]
);
// In your Model
class Post extends Model
{
use HasSEO;
public function steps()
{
return $this->hasMany(TutorialStep::class)->orderBy('order');
}
}
// In Controller - Automatic!
SEO::post($post)->set(); // Automatically detects and adds HowTo steps
SEO::post($review)->set()->addReview(
itemName: 'iPhone 15',
ratingValue: 4.5,
bestRating: 5.0,
reviewBody: 'A comprehensive review of the iPhone 15...',
authorName: 'John Doe',
datePublished: '2024-01-15'
);
// In your Model
class Post extends Model
{
use HasSEO;
public function review()
{
return $this->hasOne(Review::class);
}
}
// In Controller - Automatic!
SEO::post($post)->set(); // Automatically detects and adds Review
// In your Model
class Post extends Model
{
use HasSEO;
public function event()
{
return $this->hasOne(Event::class);
}
}
// In Controller - Automatic!
SEO::post($post)->set(); // Automatically detects and adds Event
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Shammaa\LaravelSEO\Traits\HasSEO;
class Post extends Model
{
use HasSEO;
// Define relationships
public function writer()
{
return $this->belongsTo(Writer::class);
}
public function categories()
{
return $this->belongsToMany(Category::class);
}
public function tags()
{
return $this->belongsToMany(Tag::class);
}
public function faqs()
{
return $this->hasMany(FAQ::class)->orderBy('order');
}
public function steps()
{
return $this->hasMany(TutorialStep::class)->orderBy('order');
}
public function review()
{
return $this->hasOne(Review::class);
}
public function event()
{
return $this->hasOne(Event::class);
}
}
public function show(Post $post)
{
// That's it! The library does everything automatically
SEO::post($post)->set();
return view('posts.show', compact('post'));
}
class Post extends Model
{
use HasSEO;
/**
* Define field names in your Model
*/
protected function getSEOFieldMap(): array
{
return [
'title' => ['title', 'name', 'headline'], // Try these fields in order
'description' => ['content', 'text', 'description', 'excerpt'],
'image' => ['photo', 'image', 'thumbnail', 'cover'],
'published_at' => ['created_at', 'published_at', 'publish_date'],
'modified_at' => ['updated_at', 'modified_at', 'last_updated'],
];
}
/**
* Define relationship names in your Model
*/
protected function getSEORelationships(): array
{
return [
'writer' => 'author', // If relationship name is 'author' instead of 'writer'
'categories' => 'categories',
'tags' => 'tags',
'faqs' => 'faqs',
'steps' => 'tutorial_steps', // If relationship name is different
'review' => 'product_review',
'event' => 'event_data',
];
}
}
class Article extends Model
{
use HasSEO;
protected function getSEOFieldMap(): array
{
return [
'title' => ['headline', 'article_title'], // Different fields
'description' => ['summary', 'article_summary'],
'image' => ['cover_image', 'featured_image'],
'published_at' => ['publish_date', 'published_on'],
'modified_at' => ['last_modified', 'updated_on'],
];
}
protected function getSEORelationships(): array
{
return [
'writer' => 'author', // Different relationship name
'categories' => 'sections', // Different relationship name
'tags' => 'keywords', // Different relationship name
'faqs' => 'questions', // Different relationship name
'steps' => 'instructions', // Different relationship name
'review' => 'rating', // Different relationship name
'event' => 'event_info', // Different relationship name
];
}
}
class Post extends Model
{
use HasSEO;
/**
* (Optional) Customize relationships to be loaded automatically
*/
public function getSEORelationshipsToLoad(): array
{
$relationships = parent::getSEORelationshipsToLoad();
// Example: If Post is tutorial type, ensure steps are loaded
if ($this->type === 'tutorial') {
$relationships[] = 'steps';
}
// Example: If Post is review type, ensure review is loaded
if ($this->type === 'review') {
$relationships[] = 'review';
}
return array_unique($relationships);
}
}
class Post extends Model
{
use HasSEO;
// Customize FAQ extraction
public function getSEOFAQs(): array
{
return $this->faqs()
->where('is_active', true)
->orderBy('order')
->get()
->map(function($faq) {
return [
'question' => $faq->question,
'answer' => $faq->answer,
];
})
->toArray();
}
// Customize HowTo Steps extraction
public function getSEOHowToSteps(): array
{
return $this->steps()
->where('published', true)
->orderBy('order')
->get()
->map(function($step) {
return [
'name' => $step->title,
'text' => $step->content,
'image' => $step->getImageUrl(), // Custom method
];
})
->toArray();
}
// Customize Review extraction
public function getSEOReview(): ?array
{
if (!$this->review) {
return null;
}
return [
'itemName' => $this->review->product->name,
'ratingValue' => $this->review->rating,
'bestRating' => 5.0,
'reviewBody' => $this->review->content,
'authorName' => $this->writer->name,
'datePublished' => $this->created_at->toIso8601String(),
];
}
}
use Shammaa\LaravelSEO\Builders\MultilingualBuilder;
// In AppServiceProvider or a custom Service Provider
public function boot(): void
{
MultilingualBuilder::urlGeneratorUsing(function (string $locale, $model, string $currentUrl): string {
// If your model exposes a localized URL, prefer that:
if ($model && method_exists($model, 'getLocalizedUrl')) {
return $model->getLocalizedUrl($locale);
}
// Example fallback that rewrites the path segment
return str_replace('/en/', "/{$locale}/", $currentUrl);
});
}
// ⚠️ WARNING: This will break 'php artisan config:cache'
'url_generator' => function($locale, $model, $currentUrl) {
if ($model && method_exists($model, 'getLocalizedUrl')) {
return $model->getLocalizedUrl($locale);
}
return str_replace('/en/', "/{$locale}/", $currentUrl);
},
'reading_time' => [
'enabled' => true,
'words_per_minute' => 200, // Average reading speed
'translations' => [
'en' => ':minutes min read',
'ar' => ':minutes دقيقة قراءة',
'fr' => ':minutes min de lecture',
'es' => ':minutes min de lectura',
'de' => ':minutes Min. Lesezeit',
'it' => ':minutes min di lettura',
'pt' => ':minutes min de leitura',
'ru' => ':minutes мин. чтения',
'zh' => ':minutes 分钟阅读',
'ja' => ':minutes 分で読める',
// Add your custom translations
'custom_locale' => ':minutes custom text',
],
],
use Shammaa\LaravelSEO\Services\SEOService;
// In your AppServiceProvider or a dedicated service provider
public function boot(): void
{
SEOService::ampUrlGeneratorUsing(function ($model): string {
if (is_object($model) && method_exists($model, 'route')) {
return $model->route() . '/amp';
}
return route('amp.post', $model->slug ?? '');
});
}
public function show(Tag $tag)
{
// Your pagination logic
$articles = $tag->posts()->paginate(15);
SEO::tag($tag)
->withPagination($articles) // 👈 Pass paginator here
->set();
return view('tag.show', compact('tag', 'articles'));
}
'pagination' => [
'enabled' => true,
// Canonical URL for paginated pages points to self (?page=2)
// /articles?page=2 → canonical: /articles?page=2
'canonical_to_first' => false,
// Paginated pages get "noindex, follow" to prevent duplicate content
'noindex_pagination' => false, // Set to true if you want noindex on /page/2
],
class Post extends Model
{
public function previous()
{
return static::where('id', '<', $this->id)
->orderBy('id', 'desc')
->first();
}
public function next()
{
return static::where('id', '>', $this->id)
->orderBy('id', 'asc')
->first();
}
}
'image_rendering' => [
'loading' => 'lazy', // lazy, eager
'decoding' => 'async', // async, sync, auto
'fetchpriority' => null, // high, low, auto (for important images)
],
use Shammaa\LaravelSEO\Contracts\SiteSettingsContract;
class Setting extends Model implements SiteSettingsContract
{
public static function getSeoSettings(): array
{
$settings = static::first();
return [
'name' => $settings->site_name,
'description' => $settings->site_description,
'logo' => $settings->logo,
];
}
}
use Shammaa\LaravelSEO\Services\SEOService;
public function boot(): void
{
SEOService::resolveSiteUsing(fn() => \App\Models\Setting::getSeoSettings());
}