Skip to content

Laravel Integration

Generate OG images for Laravel applications.

Setup

1. Configuration

bash
# .env
OG_IMAGE_API_KEY=og_your_api_key_here
php
// config/services.php
return [
    // ...
    'og_image' => [
        'api_key' => env('OG_IMAGE_API_KEY'),
        'base_url' => 'https://ogimageapi.io',
    ],
];

2. Create Service Class

php
<?php
// app/Services/OGImageService.php

namespace App\Services;

use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;

class OGImageService
{
    protected string $apiKey;
    protected string $baseUrl;
    
    public function __construct()
    {
        $this->apiKey = config('services.og_image.api_key');
        $this->baseUrl = config('services.og_image.base_url');
    }
    
    public function generate(array $params): ?string
    {
        $response = Http::withHeaders([
            'X-API-Key' => $this->apiKey,
        ])->post("{$this->baseUrl}/api/generate", array_merge([
            'template' => 'default',
            'theme' => 'dark',
        ], $params));
        
        if ($response->successful()) {
            return $response->body();
        }
        
        logger()->error('OG Image generation failed', [
            'status' => $response->status(),
            'body' => $response->body(),
        ]);
        
        return null;
    }
    
    public function generateAndStore(array $params, string $path): ?string
    {
        $imageData = $this->generate($params);
        
        if (!$imageData) {
            return null;
        }
        
        Storage::disk('public')->put($path, $imageData);
        
        return Storage::disk('public')->url($path);
    }
    
    public function generateForModel($model, string $template = 'blog'): ?string
    {
        $params = $this->getParamsFromModel($model, $template);
        $path = "og-images/{$model->getTable()}/{$model->id}.png";
        
        return $this->generateAndStore($params, $path);
    }
    
    protected function getParamsFromModel($model, string $template): array
    {
        return match($template) {
            'blog' => [
                'title' => $model->title,
                'subtitle' => $model->excerpt ?? '',
                'author_name' => $model->author?->name,
                'author_avatar_url' => $model->author?->avatar_url,
                'template' => 'blog',
            ],
            'product' => [
                'title' => $model->name,
                'product_image_url' => $model->image_url,
                'price' => '$' . number_format($model->price, 2),
                'original_price' => $model->compare_price ? '$' . number_format($model->compare_price, 2) : null,
                'rating' => $model->average_rating,
                'template' => 'product',
            ],
            default => [
                'title' => $model->title ?? $model->name,
                'subtitle' => $model->description ?? '',
                'template' => 'default',
            ],
        };
    }
}

3. Register Service Provider

php
<?php
// app/Providers/AppServiceProvider.php

namespace App\Providers;

use App\Services\OGImageService;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->singleton(OGImageService::class);
    }
}

Usage in Controllers

php
<?php
// app/Http/Controllers/PostController.php

namespace App\Http\Controllers;

use App\Models\Post;
use App\Services\OGImageService;
use Illuminate\Http\Request;

class PostController extends Controller
{
    public function __construct(
        protected OGImageService $ogImageService
    ) {}
    
    public function store(Request $request)
    {
        $post = Post::create($request->validated());
        
        // Generate OG image
        $ogImageUrl = $this->ogImageService->generateForModel($post, 'blog');
        
        if ($ogImageUrl) {
            $post->update(['og_image' => $ogImageUrl]);
        }
        
        return redirect()->route('posts.show', $post);
    }
    
    public function regenerateOG(Post $post)
    {
        $ogImageUrl = $this->ogImageService->generateForModel($post, 'blog');
        
        if ($ogImageUrl) {
            $post->update(['og_image' => $ogImageUrl]);
        }
        
        return back()->with('success', 'OG image regenerated');
    }
}

Model Observer

php
<?php
// app/Observers/PostObserver.php

namespace App\Observers;

use App\Models\Post;
use App\Services\OGImageService;

class PostObserver
{
    public function __construct(
        protected OGImageService $ogImageService
    ) {}
    
    public function created(Post $post)
    {
        $this->generateOGImage($post);
    }
    
    public function updated(Post $post)
    {
        // Regenerate if title or excerpt changed
        if ($post->wasChanged(['title', 'excerpt'])) {
            $this->generateOGImage($post);
        }
    }
    
    protected function generateOGImage(Post $post)
    {
        dispatch(function () use ($post) {
            $ogImageUrl = $this->ogImageService->generateForModel($post, 'blog');
            
            if ($ogImageUrl) {
                $post->updateQuietly(['og_image' => $ogImageUrl]);
            }
        })->afterResponse();
    }
}

Register the observer:

php
// app/Providers/AppServiceProvider.php
public function boot()
{
    Post::observe(PostObserver::class);
}

Blade Component

php
<?php
// app/View/Components/OGMeta.php

namespace App\View\Components;

use Illuminate\View\Component;

class OGMeta extends Component
{
    public function __construct(
        public string $title,
        public ?string $description = null,
        public ?string $image = null,
        public string $type = 'website'
    ) {}
    
    public function render()
    {
        return view('components.og-meta');
    }
}
blade
{{-- resources/views/components/og-meta.blade.php --}}
<title>{{ $title }}</title>
<meta name="description" content="{{ $description }}">

<meta property="og:title" content="{{ $title }}">
<meta property="og:description" content="{{ $description }}">
<meta property="og:image" content="{{ $image ?? asset('images/default-og.png') }}">
<meta property="og:type" content="{{ $type }}">
<meta property="og:url" content="{{ url()->current() }}">

<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="{{ $title }}">
<meta name="twitter:description" content="{{ $description }}">
<meta name="twitter:image" content="{{ $image ?? asset('images/default-og.png') }}">

Usage:

blade
{{-- resources/views/posts/show.blade.php --}}
@extends('layouts.app')

@section('head')
    <x-og-meta
        :title="$post->title"
        :description="$post->excerpt"
        :image="$post->og_image"
        type="article"
    />
@endsection

@section('content')
    <article>
        <h1>{{ $post->title }}</h1>
        {!! $post->content !!}
    </article>
@endsection

API Route for Dynamic Images

php
<?php
// routes/api.php

use App\Services\OGImageService;
use Illuminate\Http\Request;

Route::get('/og', function (Request $request, OGImageService $ogImage) {
    $params = $request->validate([
        'title' => 'required|string|max:200',
        'subtitle' => 'nullable|string|max:300',
        'template' => 'nullable|string',
        'theme' => 'nullable|in:dark,light',
    ]);
    
    $imageData = $ogImage->generate($params);
    
    if (!$imageData) {
        abort(500, 'Failed to generate image');
    }
    
    return response($imageData)
        ->header('Content-Type', 'image/png')
        ->header('Cache-Control', 'public, max-age=86400');
});

Artisan Command

php
<?php
// app/Console/Commands/GenerateOGImages.php

namespace App\Console\Commands;

use App\Models\Post;
use App\Services\OGImageService;
use Illuminate\Console\Command;

class GenerateOGImages extends Command
{
    protected $signature = 'og:generate {--model=post} {--id=}';
    protected $description = 'Generate OG images for models';
    
    public function handle(OGImageService $ogImage)
    {
        $modelClass = match($this->option('model')) {
            'post' => Post::class,
            // Add other models...
            default => throw new \InvalidArgumentException('Unknown model'),
        };
        
        $query = $modelClass::query();
        
        if ($id = $this->option('id')) {
            $query->where('id', $id);
        }
        
        $models = $query->get();
        $bar = $this->output->createProgressBar($models->count());
        
        foreach ($models as $model) {
            $url = $ogImage->generateForModel($model, $this->option('model'));
            
            if ($url) {
                $model->updateQuietly(['og_image' => $url]);
            }
            
            $bar->advance();
        }
        
        $bar->finish();
        $this->newLine();
        $this->info('Done!');
    }
}

Usage:

bash
php artisan og:generate --model=post
php artisan og:generate --model=post --id=123

Best Practices

  1. Queue image generation — Don't block requests
  2. Use observers — Automatic regeneration
  3. Store locally — Faster than API every time
  4. Create commands — Batch operations
  5. Cache responses — 24h cache headers

Generate stunning Open Graph images programmatically.