Ugrás a tartalomra

DEV Frissítve: #40 Forrás: GitHub

OG-kép generálás Laravelben — 523service/laravel-og-image

Lépésről lépésre: így kösd be és üzemeltesd a saját 523service/laravel-og-image csomagot egy Laravel-projektben. Aláírt URL, GD-vel rajzolt, fájlba cache-elt 1200×630-as Open Graph kép minden oldalhoz, headless böngésző nélkül, shared hostingon is.

1. Mire jó, mikor használd? Hivatkozás erre a szakaszra

A 523service/laravel-og-image minden oldalhoz saját, 1200×630-as Open Graph képet készít egy közös dizájnból. A képet az első kéréskor GD rajzolja meg, és fájlba menti, utána a webszerver statikus fájlként adja ki. Nem kell hozzá headless böngésző, ezért shared hostingon is működik. Akkor használd, ha nem akarsz minden oldalhoz kézzel megosztási képet gyártani, de azt szeretnéd, hogy Facebookon vagy LinkedInen minden link saját címmel jelenjen meg.

Hogyan működik:

  • Az oldal csak az URL-t írja ki: /og/{kulcs}.{png|jpg}?title=…&kicker=…&path=…. A kulcs a paraméterek HMAC-aláírása (titok: OG_IMAGE_SECRET, alapból az APP_KEY-ből), és egyben a fájlnév is. Hibás vagy hiányzó aláírásra 403 a válasz.
  • Első kérés (crawler vagy og:warm): a csomag ellenőrzi az aláírást, fájlzárral megrajzolja a képet, atomikusan a public/og/{kulcs}.{fmt} fájlba írja, és mellé tesz egy .htaccess-t hosszú Cache-Control-lal.
  • A további kéréseket már a webszerver szolgálja ki PHP nélkül (a Laravel alap public/.htaccess-e, nginx-nél try_files).
  • A route a web middleware nélkül fut (nincs session és cookie), és route:cache-sel is működik.
  • A képen: háttérszín vagy háttérfotó átmenetes sötétítéssel, logó (szöveg vagy képfájl), kicker (nagybetűs alcím a kiemelőszínnel), a cím (automatikusan 1–4 sorba tördelve, illesztett betűmérettel, túlcsordulásnál „…”-tal), lábléc bal oldalt a hosttal és az útvonallal (hosszú útvonal középen rövidítve), jobb oldalt egy tetszőleges szöveggel (pl. telefonszám).

Licenc: MIT. A mellékelt Montserrat betűtípus SIL OFL 1.1 licencű, a magyar ékezeteket rendben megjeleníti.

2. Követelmények Hivatkozás erre a szakaszra

  • PHP 8.2+
  • ext-gd FreeType- és JPEG/PNG-támogatással (WebP háttérképhez WebP-támogatás is kell)
  • Laravel 11 vagy 12

Ellenőrzés a szerveren:

Bash
php -r 'print_r(gd_info());'

A FreeType Support, JPEG Support és PNG Support értéke legyen 1 (WebP háttérképnél a WebP Support is).

3. Telepítés Hivatkozás erre a szakaszra

A csomag nincs fent a Packagisten, ezért VCS-repóként kell felvenni.

  1. Vedd fel a repót a composer.json-ba:

    Bash
    composer config repositories.og-image vcs https://github.com/523service/laravel-og-image
  2. Telepítsd:

    Bash
    composer require 523service/laravel-og-image:^1.0

    A service provider és az OgImage facade automatikusan regisztrálódik (package discovery).

  3. Publikáld a configot. Ez létrehozza a config/og-image.php fájlt:

    Bash
    php artisan vendor:publish --tag=og-image-config
  4. Tedd a legenerált képeket a .gitignore-ba:

    GITIGNORE
    /public/og/
  5. Opcionálisan állíts be változókat a .env-ben (csak a nevük, értéket ne commitolj):

    .env
    OG_IMAGE_SECRET=
    OG_IMAGE_VERSION=1
    OG_IMAGE_URL=

    Ha a .env-ben nincsenek beállítva: a titok az APP_KEY-ből származik, a verzió 1, a kép-URL-ek alapja az APP_URL.

Éles szerveren, ha nincs globális composer: php composer.phar ….

4. Konfiguráció Hivatkozás erre a szakaszra

Teljes példa-preset Hivatkozás erre a szakaszra

Szöveges logó, háttérfotó és telefonszám a láblécben, a config/og-image.php megfelelő kulcsai:

PHP
// JPG: fotós háttérnél kb. 120 KB, a PNG kb. 400 KB.
'default_format' => 'jpg',

// Háttérképek: kulcs => fájl. URL-ből csak a KULCS adható meg, útvonal soha.
'images' => [
    'hero' => public_path('img/hero.webp'),
],

// Ezt használja, ha az URL-ben nincs `p` paraméter (és ilyenkor a `p` ki is marad az URL-ből).
'default_preset' => 'brand',

'presets' => [
    'brand' => [
        'background' => '02243f',   // háttérszín (6 jegyű hex, # nélkül)
        'foreground' => 'ffffff',   // cím színe
        'accent' => 'bff230',       // kicker / kiemelés színe
        'muted' => 'd5dee8',        // lábléc, tagline színe
        'image' => 'hero',          // háttérkép KULCSA az `images` tömbből (null = csak szín)
        'overlay' => [              // átmenetes sötétítés a fotó fölött
            'color' => '02243f',
            'from' => 0.95,         // átlátszatlanság az egyik oldalon
            'to' => 0.62,           // ... és a másikon
        ],
        'logo' => [                 // szöveges logó
            'type' => 'text',
            'text' => 'ACME',
            'tagline' => "Sor 1\nSor 2",
        ],
        // vagy képes logó:
        // 'logo' => ['type' => 'image', 'path' => public_path('logo.png'), 'height' => 64],
        'host' => null,             // null = az APP_URL hostja „www.” nélkül
        'footer' => '+36 1 234 5678', // jobb alsó szöveg; ha változik, emeld a `version`-t!
    ],
],

Fontos kulcsok Hivatkozás erre a szakaszra

Kulcs Alapérték Mire való
secret OG_IMAGE_SECRET, ennek hiányában az APP_KEY-ből az URL-ek HMAC-aláírásának titka
version OG_IMAGE_VERSION, '1' dizájnváltáskor emeld: minden URL és kép új lesz. Akkor is, ha a képre égetett adat (pl. telefonszám a footer-ben) változik
url OG_IMAGE_URL, null = APP_URL a kép-URL-ek alapja (nem a kérés hostja, így queue-ból és CLI-ből is helyes)
route.prefix og a kép-route előtagja
route.middleware [] a kép-route middleware-jei
cache_path public_path('og') hová kerülnek a képek. A prefix public mappája legyen, különben a webszerver nem tudja statikusan kiszolgálni őket
cache_control public, max-age=31536000, immutable a mentett képek mellé írt .htaccess fejlécértéke
formats ['png', 'jpg'] engedélyezett formátumok
default_format png fotós háttérnél a jpg ajánlott
jpg_quality 88 JPEG-minőség
width / height 1200 / 630 képméret
limits title 140, kicker 40, path 160 karakter az url() a hosszabbat levágja
fonts null = mellékelt Montserrat ExtraBold / SemiBold statikus TTF kell, variable fonttal a GD rosszul rajzol
images [] háttérképek kulcs → útvonal (biztonsági okból URL-ben csak kulcs)
default_preset / presets default elnevezett dizájnok, lásd fent

5. Bekötés a layoutba Hivatkozás erre a szakaszra

Alap: Blade-komponens a <head>-ben Hivatkozás erre a szakaszra

Blade
<x-og-image::meta
    :title="$title"
    kicker="Blog"
    :path="request()->getPathInfo()"
/>

Ez kiírja az og:image-et (+ og:image:type, width, height, alt), valamint a twitter:card=summary_large_image és a twitter:image tagot.

A komponens további propjai: preset, alt, format, overrides (pl. ['accent' => 'ff6600']).

Fix (nem generált) képnél:

Blade
<x-og-image::meta :url="asset('kep.jpg')" :title="$title" />

PHP-ból Hivatkozás erre a szakaszra

PHP
og_image_url('Cím', kicker: 'Blog', path: '/blog/x');

OgImage::url(['title' => 'Cím', 'kicker' => 'Blog', 'p' => 'dark', 'accent' => 'ff6600'], 'png');

URL-paraméterek: title (kötelező), kicker, path, p (preset), bg, fg, accent (6 jegyű hex), img (háttérkép-kulcs).

Bevált minta: og_title / og_kicker szekciók Hivatkozás erre a szakaszra

Az oldalak opcionálisan megadják a képre kerülő szöveget:

Blade
{{-- resources/views/pages/szolgaltatasok.blade.php --}}
@extends('layouts.site')

@section('title', 'Szolgáltatásaink | Márka')
@section('og_kicker', 'Szolgáltatások')
@section('og_title', 'Minden, amire a projektednek szüksége van')

A layout ezekből, illetve a <title>-ből (a „ | Márka” utótag levágásával) és a canonical URL útvonalából építi a képet. Ha van adminban beállított fix OG-kép, az elsőbbséget kap:

Blade
{{-- resources/views/layouts/site.blade.php, <head> --}}
@php
    $pageTitle = trim($__env->yieldContent('title'));
    $ogTitle = trim($__env->yieldContent('og_title')) ?: \Illuminate\Support\Str::beforeLast($pageTitle, ' | ');
    $ogKicker = trim($__env->yieldContent('og_kicker')) ?: null;
    $canonical = url()->current();
@endphp

<link rel="canonical" href="{{ $canonical }}">

@if (filled($fixedOgImage ?? null)) {{-- pl. adminban feltöltött kép --}}
    <x-og-image::meta :url="$fixedOgImage" :title="$ogTitle" />
@else
    <x-og-image::meta
        :title="$ogTitle"
        :kicker="$ogKicker"
        :path="parse_url($canonical, PHP_URL_PATH) ?: '/'"
    />
@endif

A $fixedOgImage csak példanév; ott a saját admin-meződ értékét használd. Élő referencia: a reszecskeszuro projekt resources/views/layouts/site.blade.php fájlja.

Tesztelés a host projektben Hivatkozás erre a szakaszra

Tesztben a cache_path mutasson ideiglenes mappára, és a teszt végén takarítsd ki:

PHP
use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;

beforeEach(function () {
    config(['og-image.cache_path' => storage_path('framework/testing/og-'.Str::random(8))]);
});

afterEach(function () {
    File::deleteDirectory(config('og-image.cache_path'));
});

it('aláírt og:image URL-t ír ki', function () {
    $this->get('/')
        ->assertOk()
        ->assertSee('property="og:image"', false)
        ->assertSee('/og/', false);
});

6. Deploy és üzemeltetés Hivatkozás erre a szakaszra

  • Deploy után generáld le előre a képeket, így az első megosztás már kész fájlt talál:

    Bash
    php artisan og:warm                                          # APP_URL/sitemap.xml alapján
    php artisan og:warm https://example.com/sitemap.xml --limit=200

    A parancs minden sitemap-oldalt lekér, kiolvassa az og:image-et, és helyben megrajzolja (alapértelmezett limit: 500 oldal). Sitemap-indexet egy szint mélységig követ. Feltétele egy működő sitemap.xml.

  • Régi képek törlése:

    Bash
    php artisan og:clear                    # minden tárolt kép
    php artisan og:clear --older-than=90    # csak a 90 napja nem módosultak

    A törölt képek a következő kéréskor újra elkészülnek.

  • Dizájnváltás (szín, logó, háttér, footer-szöveg): emeld a version-t (OG_IMAGE_VERSION), majd futtasd: php artisan config:cache, og:clear, og:warm. Új URL nélkül a közösségi oldalak a régi képet mutatják.

  • Jogosultságok (shared hosting, suPHP/suEXEC): a csoport által írható fájlok 500-as hibát adnak. Deploy után futtasd a chmod -R go-w . parancsot, de a public/og a saját felhasználónak maradjon írható.

  • Git és deploy: a public/og ne kerüljön gitbe (.gitignore), és a deploy-szkript ne törölje.

7. Ellenőrzőlista — kész, ha… Hivatkozás erre a szakaszra

  • composer show 523service/laravel-og-image a v1.x verziót mutatja, és van config/og-image.php.
  • A .gitignore-ban szerepel a /public/og/.
  • Az oldal forrásában az og:image egy aláírt https://<domain>/og/<32 hex>.jpg?title=… URL (nem localhost, nem rossz www).
  • Az URL megnyitva 200-at ad image/jpeg (vagy image/png) típussal, és a kép jól néz ki (ékezetek, tördelés, logó).
  • Létrejött a public/og/<kulcs>.jpg és mellette a public/og/.htaccess; a második kérés már statikus fájl (curl -I mutatja a hosszú Cache-Control-t).
  • Egy aláírás nélküli vagy módosított URL 403-at ad.
  • A php artisan og:warm hiba nélkül lefut („… rendered, … already cached”).
  • A Facebook Sharing Debugger és a LinkedIn Post Inspector mutatja a képet.

8. Hibaelhárítás Hivatkozás erre a szakaszra

Tünet Ok Megoldás
403 a kép URL-jén hibás vagy hiányzó aláírás (kézzel módosított query, megváltozott OG_IMAGE_SECRET / APP_KEY), ismeretlen preset (p), érvénytelen szín (nem 6 jegyű hex), ismeretlen img-kulcs Az URL-t mindig a komponenssel vagy a helperrel generáld. Ellenőrizd a preset nevét, a színeket és az images kulcsait, titokváltás után pedig futtasd a config:cache és az og:warm parancsot.
500 a kép URL-jén jogosultság (suPHP: group-writable fájl, vagy nem írható public/og), hiányzó GD vagy FreeType chmod -R go-w, public/og írható a saját usernek. php -r 'print_r(gd_info());'. Nézd meg a storage/logs/laravel.log-ot.
Minden kérés PHP-n megy át, nincs statikus kiszolgálás a cache_path nem a route.prefix public mappája, vagy hiányzik a public/.htaccess rewrite-ja vagy az nginx try_files cache_path = public_path(<prefix>). Nginx: try_files $uri $uri/ /index.php?$query_string;
A közösségi oldal a régi képet mutatja a crawler cache-eli a régi URL-t Emeld a version-t (OG_IMAGE_VERSION), majd a Sharing Debuggerben nyomd meg a „Scrape Again” gombot.
localhost vagy rossz www a kép-URL-ben rossz APP_URL, vagy cache-elt config Állítsd be az APP_URL-t (vagy az OG_IMAGE_URL-t), utána php artisan config:cache.
Ékezetek helyén négyzetek, vagy rossz betűvastagság saját, ékezet nélküli vagy variable font Statikus TTF kell, vagy maradj a mellékelt Montserratnál (fonts => null).
Túl nagy kép (~400 KB) PNG fotós háttérrel 'default_format' => 'jpg' (~120 KB)
og:warm: „No pages found” nincs vagy üres a sitemap.xml Adj meg sitemap-URL-t argumentumként, vagy készíts sitemapet.
composer require: a csomag nem található nincs felvéve a VCS-repó Futtasd: composer config repositories.og-image vcs https://github.com/523service/laravel-og-image

9. Hivatkozások Hivatkozás erre a szakaszra

10. Claude Code prompt — bemásolható Hivatkozás erre a szakaszra

Ha a telepítést és a beállítást Claude Code-dal csinálod, nyisd meg a célprojektben, és másold be ezt a promptot. Előtte érdemes kitölteni a végén lévő három adatot (szín, logó, lábléc); ha üresen hagyod, a Claude rákérdez.

Szöveg
Task: install and configure the 523service/laravel-og-image package in THIS Laravel project, so that every public page gets its own signed, file-cached 1200x630 Open Graph image (rendered with GD, no headless browser).

Source of truth: https://github.com/523service/laravel-og-image (README.md, config/og-image.php, CHANGELOG.md). Step-by-step guide: https://523.hu/knowledge/laravel-og-image-beallitasa. Read the repo README and config before changing anything. Do not invent options, parameters or commands that are not in the package.

1. Investigate first (read-only):
   - Follow this project's CLAUDE.md / conventions (tests, formatter, docs, commit rules).
   - Check PHP >= 8.2, Laravel 11 or 12, and that ext-gd has FreeType + JPEG/PNG support (php -r "print_r(gd_info());"). Stop and tell me if something is missing.
   - Find the public layout(s) and how <title>, meta description and canonical URL are built. List any existing og:image / twitter:image tags and any admin field for a fixed share image.
   - Check whether a sitemap.xml exists (needed by og:warm).
   - Look for brand colours, logo files and contact data (phone) in the CSS, config and views.

2. Install (the package is NOT on Packagist, use the VCS repository):
   composer config repositories.og-image vcs https://github.com/523service/laravel-og-image
   composer require 523service/laravel-og-image:^1.0
   php artisan vendor:publish --tag=og-image-config
   Add /public/og/ to .gitignore.
   Add OG_IMAGE_VERSION=1 to .env.example (and OG_IMAGE_SECRET= / OG_IMAGE_URL= empty, with a short comment). Never write real secret values anywhere.

3. Configure config/og-image.php:
   - Create one preset for the brand and set it as default_preset: background, foreground, accent, muted (6-digit hex without #), optional background photo via the images key map (key => public_path(...), only keys in URLs), overlay, logo (text logo with optional tagline, or image logo with path + height), host (null = APP_URL host without www), footer (e.g. phone number).
   - Use default_format jpg if the preset has a photo background, otherwise png is fine.
   - Keep the bundled Montserrat fonts unless the project already ships STATIC TTF fonts (no variable fonts).
   - Keep cache_path as the public directory of route.prefix.

4. Wire it into the layout <head>:
   - Use <x-og-image::meta :title="..." :kicker="..." :path="..." />.
   - Pattern: pages may define @section('og_title') and @section('og_kicker'); otherwise the title comes from the page <title> with the " | Brand" suffix stripped, and the path from the canonical URL.
   - If the project has a fixed / admin-uploaded share image, it takes priority: <x-og-image::meta :url="..." :title="..." />.
   - Remove the old, duplicated og:image / twitter:image / twitter:card tags. Keep og:title, og:description etc.
   - Add og_kicker (and og_title where the page title is too long or too generic) to the main public pages.

5. Tests (in this project's test framework and style):
   - In tests, point og-image.cache_path to a temporary directory (e.g. storage_path('framework/testing/og-<random>')) and delete it afterwards.
   - Cover: the layout prints a signed /og/<32 hex>.<jpg|png> og:image URL and twitter:card=summary_large_image; og_title / og_kicker are used; the fixed image wins when set; the image URL returns 200 with the right content type and writes the file; a tampered URL returns 403.
   - Run only the affected test files, run the code formatter, and fix failures.

6. Finish:
   - Update the project docs/changelog if the project rules require it.
   - Do NOT commit, push or deploy unless I ask. Tell me the deploy steps instead:
     php artisan config:cache; php artisan og:warm (needs sitemap.xml); keep public/og writable and out of git, the deploy must not delete it; on suPHP/suEXEC shared hosting run chmod -R go-w after deploy (group-writable files give 500); bump OG_IMAGE_VERSION whenever the design or burned-in data (e.g. the footer phone number) changes, then og:clear + og:warm; check with the Facebook Sharing Debugger and LinkedIn Post Inspector.
   - Report: changed files, the chosen preset values, and anything you could not verify.

Stop and ask me if: the layout structure is unclear, there are several unrelated layouts, an existing OG image solution conflicts with this one, or brand values below are missing and cannot be found in the project.

Brand values (fill in or leave empty):
- Colours (background / foreground / accent / muted):
- Logo (text + tagline, or image file path):
- Footer text (e.g. phone number):