> ## Documentation Index
> Fetch the complete documentation index at: https://buttercms.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Laravel

> Build content-driven Laravel applications with ButterCMS. Covers setup, pages, collections, components, and blog integration using Laravel best practices.

## Overview

This integration guide shows you how to how to update your existing project to:

1. install the ButterCMS package
2. instantiate ButterCMS
3. create components to fetch and display each of the three ButterCMS content types: [Pages](../../core-concepts/content-types/pages), [Collections](../../core-concepts/content-types/collections), and [Blog Posts](../../core-concepts/content-types/blog-engine).

<Tip>
  In order for the snippets to work, you'll need to [setup your dashboard content schemas inside of ButterCMS](../buttercms-setup) first.
</Tip>

### Starter project

Or, you can jump directly to the starter project below, which will allow you to clone, install, run, and deploy a fully working
starter project that's integrated with content already inside of your ButterCMS account.

<Card title="Laravel Starter Project" icon="rocket" href="../starter-projects/laravel">
  Hit the ground running with a pre-configured Laravel + ButterCMS setup.
</Card>

## Installation

<Tabs>
  <Tab title="Composer">
    ```bash theme={null}
    composer require buttercms/buttercms-php
    ```
  </Tab>
</Tabs>

Add your API token to `.env`:

```bash theme={null}
BUTTERCMS_API_TOKEN=your_api_token
```

Add configuration to `config/services.php`:

```php theme={null}
// config/services.php
'buttercms' => [
    'api_token' => env('BUTTERCMS_API_TOKEN'),
],
```

## Initialize the client

<Tabs>
  <Tab title="Service Class">
    ```php theme={null}
    // app/Services/ButterCMS.php
    <?php

    namespace App\Services;

    use ButterCMS\ButterCMS as ButterClient;

    class ButterCMS
    {
        protected ButterClient $client;

        public function __construct()
        {
            $this->client = new ButterClient(config('services.buttercms.api_token'));
        }

        public function getPage(string $pageType, string $slug, array $params = [])
        {
            return $this->client->fetchPage($pageType, $slug, $params);
        }

        public function getPages(string $pageType, array $params = [])
        {
            return $this->client->fetchPages($pageType, $params);
        }

        public function getCollection(array $keys, array $params = [])
        {
            return $this->client->fetchContentFields($keys, $params);
        }

        public function getPosts(array $params = [])
        {
            return $this->client->fetchPosts($params);
        }

        public function getPost(string $slug)
        {
            return $this->client->fetchPost($slug);
        }

        public function searchPosts(string $query, array $params = [])
        {
            return $this->client->searchPosts($query, $params);
        }

        public function getCategories(array $params = [])
        {
            return $this->client->fetchCategories($params);
        }

        public function getTags(array $params = [])
        {
            return $this->client->fetchTags($params);
        }
    }
    ```

    Register in `AppServiceProvider`:

    ```php theme={null}
    // app/Providers/AppServiceProvider.php
    public function register()
    {
        $this->app->singleton(\App\Services\ButterCMS::class, function ($app) {
            return new \App\Services\ButterCMS();
        });
    }
    ```
  </Tab>

  <Tab title="Facade">
    ```php theme={null}
    // app/Services/ButterCMS.php
    <?php

    namespace App\Services;

    use ButterCMS\ButterCMS as ButterClient;
    use Illuminate\Support\Facades\Facade;

    class ButterCMSFacade extends Facade
    {
        protected static function getFacadeAccessor()
        {
            return 'buttercms';
        }
    }
    ```

    ```php theme={null}
    // app/Providers/ButterCMSServiceProvider.php
    <?php

    namespace App\Providers;

    use ButterCMS\ButterCMS;
    use Illuminate\Support\ServiceProvider;

    class ButterCMSServiceProvider extends ServiceProvider
    {
        public function register()
        {
            $this->app->singleton('buttercms', function ($app) {
                return new ButterCMS(config('services.buttercms.api_token'));
            });
        }
    }
    ```

    Add to `config/app.php`:

    ```php theme={null}
    'providers' => [
        // ...
        App\Providers\ButterCMSServiceProvider::class,
    ],

    'aliases' => [
        // ...
        'Butter' => App\Services\ButterCMSFacade::class,
    ],
    ```

    Usage:

    ```php theme={null}
    use Butter;

    $page = Butter::fetchPage('landing_page', 'home');
    ```
  </Tab>
</Tabs>

<Info>
  For complete SDK documentation including all available methods and configuration options, see the [PHP SDK Reference](../sdks/php-sdk).
</Info>

## Pages

<Tabs>
  <Tab title="Service Class">
    ```php theme={null}
    // app/Http/Controllers/PageController.php
    <?php

    namespace App\Http\Controllers;

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

    class PageController extends Controller
    {
        public function __construct(
            protected ButterCMS $butter
        ) {}

        public function show(string $slug = 'home')
        {
            try {
                $page = $this->butter->getPage('landing_page', $slug);
            } catch (\Exception $e) {
                abort(404);
            }

            return view('pages.landing', compact('page'));
        }

        public function list()
        {
            $response = $this->butter->getPages('landing_page');
            $pages = $response->getPages();

            return view('pages.list', compact('pages'));
        }
    }
    ```
  </Tab>

  <Tab title="Facade">
    ```php theme={null}
    // app/Http/Controllers/PageController.php
    <?php

    namespace App\Http\Controllers;

    use Butter;
    use Illuminate\Http\Request;

    class PageController extends Controller
    {
        public function show(string $slug = 'home')
        {
            try {
                $page = Butter::fetchPage('landing_page', $slug);
            } catch (\Exception $e) {
                abort(404);
            }

            return view('pages.landing', compact('page'));
        }

        public function list()
        {
            $response = Butter::fetchPages('landing_page');
            $pages = $response->getPages();

            return view('pages.list', compact('pages'));
        }
    }
    ```
  </Tab>
</Tabs>

```blade theme={null}
{{-- resources/views/pages/landing.blade.php --}}
@extends('layouts.app')

@section('title', data_get($page->getField('seo'), 'title') ?? $page->getField('headline'))

@section('content')
<main>
    <h1>{{ $page->getField('headline') }}</h1>
    <p>{{ $page->getField('subheadline') }}</p>

    @if($page->getField('hero_image'))
    <img src="{{ $page->getField('hero_image') }}" alt="{{ $page->getField('headline') }}" />
    @endif

    {!! $page->getField('body') !!}
</main>
@endsection
```

### Localization

If your ButterCMS account has [locales](https://buttercms.com/docs/api/#localization) configured, pass the
`locale` you want to fetch. Skip this if your account is single-locale — passing a locale that doesn't exist on the
account returns a `400` error.

```php theme={null}
// Map Laravel's app locale (or any value) to a ButterCMS locale code
$page = $this->butter->getPage('landing_page', $slug, [
    'locale' => app()->getLocale(),
]);
```

## Collections

<Tabs>
  <Tab title="Service Class">
    ```php theme={null}
    // app/Http/Controllers/BrandController.php
    <?php

    namespace App\Http\Controllers;

    use App\Services\ButterCMS;

    class BrandController extends Controller
    {
        public function __construct(
            protected ButterCMS $butter
        ) {}

        public function index()
        {
            $response = $this->butter->getCollection(['brands']);
            $brands = $response['brands'];

            return view('brands.index', compact('brands'));
        }
    }
    ```
  </Tab>

  <Tab title="Facade">
    ```php theme={null}
    // app/Http/Controllers/BrandController.php
    <?php

    namespace App\Http\Controllers;

    use Butter;

    class BrandController extends Controller
    {
        public function index()
        {
            $response = Butter::fetchContentFields(['brands']);
            $brands = $response['brands'];

            return view('brands.index', compact('brands'));
        }
    }
    ```
  </Tab>
</Tabs>

```blade theme={null}
{{-- resources/views/brands/index.blade.php --}}
@extends('layouts.app')

@section('content')
<main>
    <h1>Our Brands</h1>
    <ul>
        @foreach($brands as $brand)
        <li>
            <img src="{{ $brand['logo'] }}" alt="{{ $brand['name'] }}" />
            <h2>{{ $brand['name'] }}</h2>
            {!! $brand['description'] !!}
        </li>
        @endforeach
    </ul>
</main>
@endsection
```

## Dynamic components

### Component renderer

```php theme={null}
// app/View/Components/ButterComponent.php
<?php

namespace App\View\Components;

use Illuminate\View\Component;

class ButterComponent extends Component
{
    public function __construct(
        public array $component
    ) {}

    public function render()
    {
        $viewName = 'components.butter.' . $this->component['type'];

        if (!view()->exists($viewName)) {
            return '';
        }

        return view($viewName, ['fields' => $this->component['fields']]);
    }
}
```

### Example component

```blade theme={null}
{{-- resources/views/components/butter/hero.blade.php --}}
<section class="hero">
    <h1>{{ $fields['headline'] }}</h1>
    <p>{{ $fields['subheadline'] }}</p>

    @if($fields['button_label'] ?? null)
    <a href="{{ $fields['button_url'] }}" class="btn">{{ $fields['button_label'] }}</a>
    @endif

    @if($fields['image'] ?? null)
    <img src="{{ $fields['image'] }}" alt="{{ $fields['headline'] }}" />
    @endif
</section>
```

```blade theme={null}
{{-- resources/views/components/butter/features.blade.php --}}
<section class="features">
    <h2>{{ $fields['headline'] }}</h2>
    <div class="features-grid">
        @foreach($fields['items'] as $item)
        <div class="feature">
            @if($item['icon'] ?? null)
            <img src="{{ $item['icon'] }}" alt="{{ $item['title'] }}" />
            @endif
            <h3>{{ $item['title'] }}</h3>
            <p>{{ $item['description'] }}</p>
        </div>
        @endforeach
    </div>
</section>
```

```blade theme={null}
{{-- resources/views/components/butter/cta.blade.php --}}
<section class="cta">
    <h2>{{ $fields['headline'] }}</h2>
    <p>{{ $fields['subheadline'] }}</p>
    <a href="{{ $fields['button_url'] }}" class="btn">{{ $fields['button_label'] }}</a>
</section>
```

### Using in pages

<Tabs>
  <Tab title="Service Class">
    ```php theme={null}
    // app/Http/Controllers/ComponentPageController.php
    <?php

    namespace App\Http\Controllers;

    use App\Services\ButterCMS;

    class ComponentPageController extends Controller
    {
        public function __construct(
            protected ButterCMS $butter
        ) {}

        public function show(string $slug)
        {
            try {
                $page = $this->butter->getPage('component_page', $slug);
            } catch (\Exception $e) {
                abort(404);
            }

            $components = $page->getField('body') ?? [];

            return view('pages.component-page', compact('page', 'components'));
        }
    }
    ```
  </Tab>

  <Tab title="Facade">
    ```php theme={null}
    // app/Http/Controllers/ComponentPageController.php
    <?php

    namespace App\Http\Controllers;

    use Butter;

    class ComponentPageController extends Controller
    {
        public function show(string $slug)
        {
            try {
                $page = Butter::fetchPage('component_page', $slug);
            } catch (\Exception $e) {
                abort(404);
            }

            $components = $page->getField('body') ?? [];

            return view('pages.component-page', compact('page', 'components'));
        }
    }
    ```
  </Tab>
</Tabs>

```blade theme={null}
{{-- resources/views/pages/component-page.blade.php --}}
@extends('layouts.app')

@section('title', data_get($page->getField('seo'), 'title') ?? 'Page')

@section('content')
<main>
    @foreach($components as $component)
        <x-butter-component :component="$component" />
    @endforeach
</main>
@endsection
```

## Blog

<Tabs>
  <Tab title="Blog List">
    <Tabs>
      <Tab title="Service Class">
        ```php theme={null}
        // app/Http/Controllers/BlogController.php
        <?php

        namespace App\Http\Controllers;

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

        class BlogController extends Controller
        {
            public function __construct(
                protected ButterCMS $butter
            ) {}

            public function index(Request $request)
            {
                $page = $request->get('page', 1);
                $response = $this->butter->getPosts([
                    'page' => $page,
                    'page_size' => 10,
                ]);

                $posts = $response->getPosts();
                $meta = $response->getMeta();

                return view('blog.index', compact('posts', 'meta'));
            }

            public function category(string $slug, Request $request)
            {
                $page = $request->get('page', 1);
                $response = $this->butter->getPosts([
                    'category_slug' => $slug,
                    'page' => $page,
                    'page_size' => 10,
                ]);

                $posts = $response->getPosts();
                $meta = $response->getMeta();

                return view('blog.index', compact('posts', 'meta'));
            }
        }
        ```
      </Tab>

      <Tab title="Facade">
        ```php theme={null}
        // app/Http/Controllers/BlogController.php
        <?php

        namespace App\Http\Controllers;

        use Butter;
        use Illuminate\Http\Request;

        class BlogController extends Controller
        {
            public function index(Request $request)
            {
                $page = $request->get('page', 1);
                $response = Butter::fetchPosts([
                    'page' => $page,
                    'page_size' => 10,
                ]);

                $posts = $response->getPosts();
                $meta = $response->getMeta();

                return view('blog.index', compact('posts', 'meta'));
            }

            public function category(string $slug, Request $request)
            {
                $page = $request->get('page', 1);
                $response = Butter::fetchPosts([
                    'category_slug' => $slug,
                    'page' => $page,
                    'page_size' => 10,
                ]);

                $posts = $response->getPosts();
                $meta = $response->getMeta();

                return view('blog.index', compact('posts', 'meta'));
            }
        }
        ```
      </Tab>
    </Tabs>

    ```blade theme={null}
    {{-- resources/views/blog/index.blade.php --}}
    @extends('layouts.app')

    @section('content')
    <main>
        <h1>Blog</h1>
        <ul>
            @foreach($posts as $post)
            <li>
                <h2><a href="{{ route('blog.show', $post->getSlug()) }}">{{ $post->getTitle() }}</a></h2>
                <p class="meta">
                    By {{ $post->getAuthor()->getFirstName() }} {{ $post->getAuthor()->getLastName() }}
                    on {{ \Carbon\Carbon::parse($post->getPublished())->format('F j, Y') }}
                </p>
                <p>{{ $post->getSummary() }}</p>
            </li>
            @endforeach
        </ul>

        @if($meta['next_page'])
        <a href="{{ route('blog.index', ['page' => $meta['next_page']]) }}">Next Page</a>
        @endif
    </main>
    @endsection
    ```
  </Tab>

  <Tab title="Blog Post">
    <Tabs>
      <Tab title="Service Class">
        ```php theme={null}
        // app/Http/Controllers/BlogController.php
        public function show(string $slug)
        {
            try {
                $response = $this->butter->getPost($slug);
                $post = $response->getPost();
            } catch (\Exception $e) {
                abort(404);
            }

            return view('blog.show', compact('post'));
        }
        ```
      </Tab>

      <Tab title="Facade">
        ```php theme={null}
        // app/Http/Controllers/BlogController.php
        public function show(string $slug)
        {
            try {
                $response = Butter::fetchPost($slug);
                $post = $response->getPost();
            } catch (\Exception $e) {
                abort(404);
            }

            return view('blog.show', compact('post'));
        }
        ```
      </Tab>
    </Tabs>

    ```blade theme={null}
    {{-- resources/views/blog/show.blade.php --}}
    @extends('layouts.app')

    @section('title', $post->getTitle())
    @section('description', $post->getSummary())

    @section('content')
    <article>
        <h1>{{ $post->getTitle() }}</h1>
        <p class="meta">
            By {{ $post->getAuthor()->getFirstName() }} {{ $post->getAuthor()->getLastName() }}
            on {{ \Carbon\Carbon::parse($post->getPublished())->format('F j, Y') }}
        </p>

        @if($post->getFeaturedImage())
        <img src="{{ $post->getFeaturedImage() }}" alt="{{ $post->getFeaturedImageAlt() }}" />
        @endif

        {!! $post->getBody() !!}

        @if(count($post->getCategories()))
        <div class="categories">
            Categories:
            @foreach($post->getCategories() as $category)
            <a href="{{ route('blog.category', $category->getSlug()) }}">{{ $category->getName() }}</a>
            @endforeach
        </div>
        @endif

        <a href="{{ route('blog.index') }}">Back to Posts</a>
    </article>
    @endsection
    ```
  </Tab>
</Tabs>

## Routes

```php theme={null}
// routes/web.php
use App\Http\Controllers\PageController;
use App\Http\Controllers\BlogController;
use App\Http\Controllers\BrandController;
use App\Http\Controllers\ComponentPageController;

// Pages
Route::get('/', [PageController::class, 'show'])->name('home');
Route::get('/page/{slug}', [PageController::class, 'show'])->name('page.show');
Route::get('/landing/{slug}', [ComponentPageController::class, 'show'])->name('landing.show');

// Collections
Route::get('/brands', [BrandController::class, 'index'])->name('brands.index');

// Blog
Route::get('/blog', [BlogController::class, 'index'])->name('blog.index');
Route::get('/blog/category/{slug}', [BlogController::class, 'category'])->name('blog.category');
Route::get('/blog/{slug}', [BlogController::class, 'show'])->name('blog.show');
```

## Preview mode

```php theme={null}
// app/Http/Controllers/PageController.php
public function show(string $slug = 'home', Request $request)
{
    $preview = $request->get('preview') === 'true';

    try {
        $params = [];
        if ($preview) {
            $params['preview'] = 1;
        }

        $page = $this->butter->getPage('landing_page', $slug, $params);
    } catch (\Exception $e) {
        abort(404);
    }

    return view('pages.landing', compact('page', 'preview'));
}
```

## Caching

```php theme={null}
// app/Services/ButterCMS.php
use Illuminate\Support\Facades\Cache;

public function getPage(string $pageType, string $slug, array $params = [])
{
    $cacheKey = "butter_page_{$pageType}_{$slug}_" . md5(serialize($params));

    // Skip cache in preview mode
    if (isset($params['preview']) && $params['preview']) {
        return $this->client->fetchPage($pageType, $slug, $params);
    }

    return Cache::remember($cacheKey, 3600, function () use ($pageType, $slug, $params) {
        return $this->client->fetchPage($pageType, $slug, $params);
    });
}

public function getPosts(array $params = [])
{
    $cacheKey = "butter_posts_" . md5(serialize($params));

    return Cache::remember($cacheKey, 3600, function () use ($params) {
        return $this->client->fetchPosts($params);
    });
}

public function clearCache(string $pattern = 'butter_*')
{
    // For Redis
    if (config('cache.default') === 'redis') {
        $keys = Cache::getRedis()->keys(config('cache.prefix') . $pattern);
        foreach ($keys as $key) {
            Cache::forget(str_replace(config('cache.prefix'), '', $key));
        }
    } else {
        Cache::flush();
    }
}
```

### Webhook cache invalidation

```php theme={null}
// app/Http/Controllers/WebhookController.php
<?php

namespace App\Http\Controllers;

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

class WebhookController extends Controller
{
    public function __construct(
        protected ButterCMS $butter
    ) {}

    public function handle(Request $request)
    {
        $payload = $request->all();

        // Clear relevant cache based on webhook event
        if (str_contains($payload['webhook']['event'] ?? '', 'page')) {
            $this->butter->clearCache('butter_page_*');
        }

        if (str_contains($payload['webhook']['event'] ?? '', 'post')) {
            $this->butter->clearCache('butter_posts_*');
        }

        return response()->json(['status' => 'ok']);
    }
}
```

## SEO

```blade theme={null}
{{-- resources/views/layouts/app.blade.php --}}
<!DOCTYPE html>
<html>
<head>
    <title>@yield('title', config('app.name'))</title>
    <meta name="description" content="@yield('description', '')">

    <!-- Open Graph -->
    <meta property="og:title" content="@yield('og_title', View::yieldContent('title', config('app.name')))">
    <meta property="og:description" content="@yield('og_description', View::yieldContent('description', ''))">
    @hasSection('og_image')
    <meta property="og:image" content="@yield('og_image')">
    @endif

    <!-- Twitter -->
    <meta name="twitter:card" content="summary_large_image">
    <meta name="twitter:title" content="@yield('title', config('app.name'))">
    <meta name="twitter:description" content="@yield('description', '')">
</head>
<body>
    @yield('content')
</body>
</html>
```

```blade theme={null}
{{-- resources/views/pages/landing.blade.php --}}
@extends('layouts.app')

@section('title', data_get($page->getField('seo'), 'title') ?? $page->getField('headline'))
@section('description', data_get($page->getField('seo'), 'description') ?? '')
@section('og_title', data_get($page->getField('seo'), 'og_title') ?? data_get($page->getField('seo'), 'title') ?? $page->getField('headline'))
@section('og_description', data_get($page->getField('seo'), 'og_description') ?? data_get($page->getField('seo'), 'description') ?? '')
@if(data_get($page->getField('seo'), 'og_image'))
@section('og_image', data_get($page->getField('seo'), 'og_image'))
@endif

@section('content')
<!-- Page content -->
@endsection
```

## Resources

<CardGroup cols={2}>
  <Card title="Laravel Starter" icon="rocket" href="../starter-projects/laravel">
    Pre-configured starter project
  </Card>

  <Card title="PHP SDK" icon="php" href="../sdks/php-sdk">
    Complete SDK reference
  </Card>

  <Card title="GitHub Repository" icon="github" href="https://github.com/ButterCMS/buttercms-php">
    View source code
  </Card>

  <Card title="Webhooks" icon="bell" href="../../api/concepts/webhooks-overview">
    Set up content webhooks
  </Card>
</CardGroup>
