Zum Inhalt springen
← Zurück zu den Projekten

Seoquent

#Seoquent

All-in-one SEO for Laravel 10+. One line in your controller, one directive in your blade — done.

#The Problem

SEO in Laravel is a mess. You end up with:

  • Meta tags scattered across every Blade view
  • Open Graph and Twitter Cards copy-pasted and forgotten
  • No structured data (JSON-LD), so Google shows plain blue links instead of rich results
  • Sitemap and robots.txt managed manually or with separate packages
  • Every model needs its own SEO logic, duplicated everywhere

You spend more time wiring SEO tags than building your actual app.

#The Solution

Seoquent handles everything from one place. Set your SEO in the controller, drop one directive in your layout — the package generates all the meta tags, Open Graph, Twitter Cards, JSON-LD, sitemap, and robots.txt automatically.

// Controller
Seo::title('My Post')->description('...')->image('...');
<!-- Layout -->
<head>
    @seoHead
</head>

That's it. No more scattered tags. No more forgetting Open Graph. No more missing structured data.

#Features

  • Meta Tags — title, description, keywords, robots, canonical, author
  • Open Graph — auto-generated from meta tags, fully customizable
  • Twitter Cards — auto-generated from Open Graph, fully customizable
  • JSON-LD Structured Data — Article, Product, FAQ, Breadcrumbs, Organization, LocalBusiness, and custom schemas
  • XML Sitemap — auto-generated from static URLs and Eloquent models, cached, served at /sitemap.xml
  • Dynamic robots.txt — configurable allow/disallow rules, served at /robots.txt
  • Per-Model SEOHasSeo trait to store and load SEO data from the database for any model
  • Blade Directives@seoHead and @seoJsonLd, zero config rendering
  • Smart Fallbacks — Twitter falls back to Open Graph, Open Graph falls back to meta tags
  • Fluent API — chainable, readable, easy to use
  • Laravel 10, 11, 12 — fully compatible

#Install

composer require ibrahim-eng12/seoquent

#3 Steps to Full SEO

#1. Set SEO in your controller

use IbrahimEng12\Seoquent\Facades\Seo;

public function show(Post $post)
{
    Seo::title($post->title)
        ->description($post->excerpt)
        ->image($post->cover_image);

    return view('posts.show', compact('post'));
}

#2. Add the directive to your layout

<head>
    @seoHead
</head>

#3. That's it. This is what gets rendered:

<title>My Post Title | App Name</title>
<meta name="description" content="Post excerpt here...">
<meta name="robots" content="index, follow">
<link rel="canonical" href="https://yoursite.com/posts/my-post">

<meta property="og:title" content="My Post Title">
<meta property="og:description" content="Post excerpt here...">
<meta property="og:type" content="website">
<meta property="og:url" content="https://yoursite.com/posts/my-post">
<meta property="og:image" content="https://yoursite.com/cover.jpg">

<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="My Post Title">
<meta name="twitter:description" content="Post excerpt here...">
<meta name="twitter:image" content="https://yoursite.com/cover.jpg">

All from 3 lines of code.


#What's Included

Feature Auto-generated Route
Meta tags (title, description, robots, canonical) Yes -
Open Graph tags Yes (falls back to meta) -
Twitter Cards Yes (falls back to meta) -
JSON-LD structured data Yes -
XML Sitemap Yes /sitemap.xml
Dynamic robots.txt Yes /robots.txt
Per-model SEO (database) Optional -

#API Reference

#Basic SEO

Seo::title('Page Title');
Seo::description('Page description');
Seo::keywords(['keyword1', 'keyword2']);
Seo::image('https://example.com/image.jpg');
Seo::canonical('https://example.com/page');
Seo::author('Ibrahim');
Seo::robots('index, follow');
Seo::noIndex();

All methods are chainable:

Seo::title('About Us')
    ->description('Learn more about our company')
    ->keywords('company, about, team')
    ->image('https://example.com/about.jpg')
    ->author('Ibrahim');

#Title Customization

// Default output: "Page Title | App Name"
Seo::title('Page Title');

// Remove suffix: "Page Title"
Seo::meta()->withoutTitleSuffix();

// Change separator: "Page Title - App Name"
Seo::meta()->titleSeparator(' - ');

#Open Graph

Falls back to your meta title/description automatically. Override when needed:

Seo::openGraph()
    ->title('Custom OG Title')
    ->description('Custom OG Description')
    ->type('article')
    ->image('https://example.com/og.jpg', 1200, 630, 'Alt text')
    ->siteName('My Site')
    ->locale('en_US');

#Twitter Cards

Falls back to Open Graph, then meta tags. Override when needed:

Seo::twitter()
    ->card('summary_large_image')
    ->site('@mysite')
    ->creator('@author')
    ->title('Custom Twitter Title')
    ->image('https://example.com/twitter.jpg', 'Alt text');

#JSON-LD Structured Data

Add @seoJsonLd to your layout (before </body>), then use any of these:

// Article
Seo::jsonLd()->article(
    title: 'My Article',
    author: 'Ibrahim',
    datePublished: '2025-01-01',
    dateModified: '2025-01-15',
    image: 'https://example.com/article.jpg',
);

// Organization
Seo::jsonLd()->organization('Company Name', 'https://example.com', 'https://example.com/logo.png');

// Breadcrumbs
Seo::jsonLd()->breadcrumbs([
    ['name' => 'Home', 'url' => '/'],
    ['name' => 'Blog', 'url' => '/blog'],
    ['name' => 'Current Post'],
]);

// FAQ
Seo::jsonLd()->faq([
    ['question' => 'What is this?', 'answer' => 'A Laravel SEO package.'],
    ['question' => 'Is it free?', 'answer' => 'Yes, MIT licensed.'],
]);

// Product
Seo::jsonLd()->product(
    name: 'Product Name',
    description: 'Description',
    brand: 'Brand',
    price: 29.99,
    currency: 'USD',
    availability: 'InStock',
);

// Local Business
Seo::jsonLd()->localBusiness(
    name: 'My Business',
    address: '123 Main St',
    phone: '+1234567890',
);

// Website with search action
Seo::jsonLd()->website('My Site', 'https://example.com', 'https://example.com/search?q=');

// Any custom schema
Seo::jsonLd()->addSchema([
    '@context' => 'https://schema.org',
    '@type' => 'Event',
    'name' => 'My Event',
    'startDate' => '2025-06-01',
]);

#XML Sitemap

Register URLs in a service provider's boot() method:

use IbrahimEng12\Seoquent\Facades\Seo;

// Static pages
Seo::sitemap()
    ->add(url('/'), now()->toIso8601String(), 'daily', 1.0)
    ->add(url('/about'), null, 'monthly', 0.8);

// From Eloquent models (auto-generates URLs)
Seo::sitemap()->addModel(
    modelClass: \App\Models\Post::class,
    routeName: 'posts.show',
    routeKey: 'slug',
    changefreq: 'weekly',
    priority: 0.8,
    queryCallback: fn ($query) => $query->where('published', true),
);

Served at /sitemap.xml. Cached by default.

#Per-Model SEO (Database)

Publish the migration first:

php artisan vendor:publish --tag=seoquent-migrations
php artisan migrate

Add the trait to your model:

use IbrahimEng12\Seoquent\Traits\HasSeo;

class Post extends Model
{
    use HasSeo;
}

Save SEO data:

$post->setSeo([
    'title' => 'Custom SEO Title',
    'description' => 'Custom description',
    'og_image' => 'https://example.com/image.jpg',
    'keywords' => ['laravel', 'seo'],
]);

Apply it in your controller:

public function show(Post $post)
{
    $post->applySeo(); // loads all stored SEO data into Seoquent

    return view('posts.show', compact('post'));
}

Delete SEO data:

$post->deleteSeo();

#Blade Directives

@seoHead      {{-- meta + open graph + twitter cards --}}
@seoJsonLd    {{-- JSON-LD structured data --}}

Or use Blade components:

<x-seoquent::meta />
<x-seoquent::jsonld />

#Configuration

php artisan vendor:publish --tag=seoquent-config

All defaults can be set in config/seoquent.php:

'defaults' => [
    'title'           => env('APP_NAME', 'Laravel'),
    'title_separator' => ' | ',
    'title_suffix'    => env('APP_NAME', 'Laravel'),
    'description'     => '',
    'robots'          => 'index, follow',
],

'open_graph' => [
    'type'      => 'website',
    'site_name' => env('APP_NAME'),
    'image'     => null,  // default OG image for all pages
],

'twitter' => [
    'card'    => 'summary_large_image',
    'site'    => null,  // @username
    'creator' => null,  // @username
],

'sitemap' => [
    'enabled'        => true,
    'cache_enabled'  => true,
    'cache_duration' => 3600,
],

'robots' => [
    'enabled'  => true,
    'allow'    => ['/'],
    'disallow' => [],
],

'database' => [
    'enabled'    => true,
    'table_name' => 'seo_meta',
],

#Publishing

php artisan vendor:publish --tag=seoquent-config       # Config
php artisan vendor:publish --tag=seoquent-migrations    # Migration
php artisan vendor:publish --tag=seoquent-views         # Blade views

#Requirements

  • PHP 8.1+
  • Laravel 10, 11, or 12

#License

MIT - See LICENSE

Neue Version verfügbar.