Skip to content

Integration: Laravel

Status: Produktiv seit 2026-05-20. Funktioniert mit Laravel 11+. Nutzt das eingebaute Laravel-Http-Facade — keine zusätzliche Composer-Dependency notwendig.

60-Sekunden-Setup

bash
# In deinem Laravel-Projekt:
npx @peacock/cli init laravel

Was passiert:

  1. Erkennung: artisan-Datei oder laravel/framework in composer.json.
  2. .env: Idempotente Erweiterung um PEACOCK_API_BASE_URL, PEACOCK_SPACE, PEACOCK_TOKEN.
  3. config/peacock.php wird angelegt — liest die env()-Werte ein.
  4. routes/peacock.php wird angelegt — Catch-All-Route, die unbekannte URLs als Peacock-Stories interpretiert.

bootstrap/app.php

Die generierte Route-Datei wird nicht automatisch in bootstrap/app.php eingebunden — das CLI darf in dieser sensiblen Datei nicht herumeditieren. Füge eine Zeile manuell ein:

php
return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
        then: function () {
            Route::middleware('web')->group(base_path('routes/peacock.php'));
        },
    )
    // ...

then: läuft nach den Standard-Routes — so haben deine eigenen Routes (/login, /dashboard) Vorrang vor dem Peacock-Catch-All.

Generierte Dateien

config/peacock.php

php
return [
    'api_base_url' => env('PEACOCK_API_BASE_URL'),
    'space' => env('PEACOCK_SPACE'),
    'token' => env('PEACOCK_TOKEN'),
];

Greife in jeder Klasse mit config('peacock.api_base_url') darauf zu.

routes/peacock.php

php
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Http;

Route::get('/{path?}', function (string $path = '') {
    $base = rtrim((string) config('peacock.api_base_url'), '/');
    $space = (string) config('peacock.space');
    $token = (string) config('peacock.token');

    $response = Http::withToken($token)
        ->acceptJson()
        ->timeout(5)
        ->get(sprintf('%s/cdn/spaces/%s/stories/by-path', $base, $space), [
            'path' => '/' . ltrim($path, '/'),
        ]);

    if ($response->status() === 404) {
        abort(404);
    }

    if (!$response->successful()) {
        abort(500, "Peacock fetch failed: " . $response->status());
    }

    $story = $response->json('data');

    return response()
        ->view('welcome', ['story' => $story])
        ->header('X-Peacock-Story', $story['uuid'] ?? '');
})->where('path', '.*');

Blade-Render-Beispiel

resources/views/welcome.blade.php:

blade
<!doctype html>
<html lang="de">
<head>
  <meta charset="utf-8">
  <title>{{ $story['content']['headline'] ?? config('app.name') }}</title>
</head>
<body>
  @foreach ($story['content']['body'] ?? [] as $block)
    @switch ($block['_component'])
      @case ('hero')
        <section class="hero">
          <h1>{{ $block['headline'] }}</h1>
          <p>{{ $block['lead'] ?? '' }}</p>
          @if (!empty($block['cta_url']))
            <a href="{{ $block['cta_url'] }}">{{ $block['cta_label'] ?? 'Mehr' }}</a>
          @endif
        </section>
        @break
      @case ('faq')
        <section class="faq">
          @foreach ($block['items'] ?? [] as $item)
            <details>
              <summary>{{ $item['question'] }}</summary>
              <p>{{ $item['answer'] }}</p>
            </details>
          @endforeach
        </section>
        @break
    @endswitch
  @endforeach
</body>
</html>

Caching mit Laravel Cache

Der CDN-Endpoint setzt zwar ETag-Header, aber Laravel's Http-Client cached nicht out-of-the-box. Für hochfrequentierte Routes wickle den Fetch in Cache::remember():

php
$story = Cache::remember(
    "peacock:story:{$path}",
    60, // Sekunden
    fn () => $response->json('data'),
);

Webhook-Invalidierung: Konfiguriere in Peacock-Admin einen Webhook auf story.published, der einen Endpoint hier hittet und Cache::forget("peacock:story:{$path}") ausführt.

Was noch fehlt

  • Dedicated webhoch-com/peacock-laravel Composer-Paket: Phase-9-Polish — wird Service-Provider, Blade-Component (<x-peacock-block :data="$block" />), und Artisan-Commands für Webhook-Setup mitbringen. Heute funktioniert das oben gezeigte Pattern vollständig ohne externes Paket.