{
  "slug": "consume-laravel",
  "title": "Consume from Laravel (PHP)",
  "description": "Read @webhouse/cms content from a Laravel application. Helper class, routes, Blade templates.",
  "category": "consumers",
  "order": 10,
  "locale": "en",
  "translationGroup": "a77b58cf-ebf1-4e7d-a9c3-67c1f5639f37",
  "helpCardId": null,
  "content": "## Setup\n\nPlace your Laravel project and @webhouse/cms content side by side:\n\n```\nmy-project/\n  cms.config.ts      # Content model\n  content/           # JSON documents (read by Laravel)\n  public/uploads/    # Media files (served by Laravel)\n  app/               # Your Laravel app\n  resources/views/   # Blade templates\n```\n\n## Helper class\n\nCreate `app/Services/Webhouse.php`:\n\n```php\n<?php\n\nnamespace App\\Services;\n\nuse Illuminate\\Support\\Facades\\File;\nuse Illuminate\\Support\\Collection;\n\nclass Webhouse\n{\n    public static function contentDir(): string\n    {\n        return base_path('content');\n    }\n\n    /**\n     * List all published documents in a collection.\n     *\n     * @param  string  $collection  e.g. 'posts'\n     * @param  string|null  $locale  e.g. 'en' — null means all locales\n     * @return Collection<int, array>\n     */\n    public static function collection(string $collection, ?string $locale = null): Collection\n    {\n        $dir = self::contentDir() . '/' . $collection;\n        if (!File::isDirectory($dir)) {\n            return collect();\n        }\n\n        return collect(File::files($dir))\n            ->filter(fn($f) => $f->getExtension() === 'json')\n            ->map(fn($f) => json_decode(File::get($f->getPathname()), true))\n            ->filter(fn($d) => ($d['status'] ?? null) === 'published')\n            ->when($locale, fn($c) => $c->filter(fn($d) => ($d['locale'] ?? 'en') === $locale))\n            ->sortByDesc(fn($d) => $d['data']['date'] ?? '')\n            ->values();\n    }\n\n    /**\n     * Load a single document by slug.\n     */\n    public static function document(string $collection, string $slug): ?array\n    {\n        $path = self::contentDir() . \"/{$collection}/{$slug}.json\";\n        if (!File::exists($path)) return null;\n        $doc = json_decode(File::get($path), true);\n        return ($doc['status'] ?? null) === 'published' ? $doc : null;\n    }\n}\n```\n\n## Routes\n\n```php\n// routes/web.php\nuse App\\Services\\Webhouse;\nuse Illuminate\\Support\\Facades\\Route;\n\nRoute::get('/', function () {\n    $posts = Webhouse::collection('posts', 'en');\n    return view('home', ['posts' => $posts]);\n});\n\nRoute::get('/blog/{slug}', function (string $slug) {\n    $post = Webhouse::document('posts', $slug);\n    abort_unless($post, 404);\n    return view('post', ['post' => $post]);\n});\n```\n\n## Blade template\n\n```blade\n{{-- resources/views/post.blade.php --}}\n@extends('layouts.app')\n\n@section('content')\n  <article>\n    <h1>{{ $post['data']['title'] }}</h1>\n    <time>{{ $post['data']['date'] ?? '' }}</time>\n    <div class=\"prose\">\n      {!! \\Illuminate\\Support\\Str::markdown($post['data']['content'] ?? '') !!}\n    </div>\n    @foreach (($post['data']['tags'] ?? []) as $tag)\n      <a href=\"/tags/{{ $tag }}\" class=\"tag\">#{{ $tag }}</a>\n    @endforeach\n  </article>\n@endsection\n```\n\n## Serving uploaded media\n\n`@webhouse/cms` stores uploaded media in `public/uploads/`. Laravel already serves the `public/` directory, so `/uploads/my-image.jpg` just works.\n\n## i18n: reading both locales\n\n```php\n// Get all Danish posts\n$daPosts = Webhouse::collection('posts', 'da');\n\n// Get all English posts\n$enPosts = Webhouse::collection('posts', 'en');\n\n// Find the translation of a specific post\n$post = Webhouse::document('posts', 'hello-world');\n$translationGroup = $post['translationGroup'] ?? null;\n$translation = Webhouse::collection('posts')\n    ->firstWhere(fn($d) => ($d['translationGroup'] ?? null) === $translationGroup && ($d['locale'] ?? null) !== $post['locale']);\n```\n\n## Caching\n\nFor production, cache parsed JSON:\n\n```php\nuse Illuminate\\Support\\Facades\\Cache;\n\npublic static function collection(string $collection, ?string $locale = null): Collection\n{\n    return Cache::remember(\"webhouse:{$collection}:{$locale}\", 60, function () use ($collection, $locale) {\n        // ... same logic as before\n    });\n}\n```\n\nClear the cache when content changes — either via a file watcher or a CMS admin webhook.\n\n## Next steps\n\n- See the [Laravel example](https://github.com/webhousecode/cms/tree/main/examples/consumers/laravel-blog)\n- Read about [i18n](/docs/i18n) for multi-language patterns\n- Learn about [Framework-Agnostic Architecture](/docs/framework-agnostic)",
  "excerpt": "Setup\n\nPlace your Laravel project and @webhouse/cms content side by side:\n\n\nmy-project/\n  cms.config.ts       Content model\n  content/            JSON documents (read by Laravel)\n  public/uploads/     Media files (served by Laravel)\n  app/                Your Laravel app\n  resources/views/    Blade ",
  "seo": {
    "metaTitle": "Consume from Laravel (PHP) — webhouse.app Docs",
    "metaDescription": "Read @webhouse/cms content from a Laravel application. Helper class, routes, Blade templates.",
    "keywords": [
      "webhouse",
      "cms",
      "laravel",
      "php",
      "consumer"
    ]
  },
  "createdAt": "2026-04-08T12:00:00.000Z",
  "updatedAt": "2026-04-08T12:00:00.000Z"
}