Overview
This integration guide shows you how to how to update your existing project to:- install the ButterCMS package
- instantiate ButterCMS
- create components to fetch and display each of the three ButterCMS content types: Pages, Collections, and Blog Posts.
In order for the snippets to work, you’ll need to setup your dashboard content schemas inside of ButterCMS first.
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.Laravel Starter Project
Hit the ground running with a pre-configured Laravel + ButterCMS setup.
Installation
- Composer
composer require buttercms/buttercms-php
.env:
BUTTERCMS_API_TOKEN=your_api_token
config/services.php:
// config/services.php
'buttercms' => [
'api_token' => env('BUTTERCMS_API_TOKEN'),
],
Initialize the client
- Service Class
- Facade
// 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);
}
}
AppServiceProvider:// app/Providers/AppServiceProvider.php
public function register()
{
$this->app->singleton(\App\Services\ButterCMS::class, function ($app) {
return new \App\Services\ButterCMS();
});
}
// 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';
}
}
// 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'));
});
}
}
config/app.php:'providers' => [
// ...
App\Providers\ButterCMSServiceProvider::class,
],
'aliases' => [
// ...
'Butter' => App\Services\ButterCMSFacade::class,
],
use Butter;
$page = Butter::fetchPage('landing-page', 'home');
For complete SDK documentation including all available methods and configuration options, see the PHP SDK Reference.
Pages
- Service Class
- Facade
// 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 {
$response = $this->butter->getPage('landing-page', $slug, [
'locale' => app()->getLocale(),
]);
$page = $response->getData();
} catch (\Exception $e) {
abort(404);
}
return view('pages.landing', compact('page'));
}
public function list()
{
$response = $this->butter->getPages('landing-page');
$pages = $response->getData();
return view('pages.list', compact('pages'));
}
}
// 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 {
$response = Butter::fetchPage('landing-page', $slug, [
'locale' => app()->getLocale(),
]);
$page = $response->getData();
} catch (\Exception $e) {
abort(404);
}
return view('pages.landing', compact('page'));
}
public function list()
{
$response = Butter::fetchPages('landing-page');
$pages = $response->getData();
return view('pages.list', compact('pages'));
}
}
{{-- resources/views/pages/landing.blade.php --}}
@extends('layouts.app')
@section('title', $page->fields->seo->title ?? $page->fields->headline)
@section('content')
<main>
<h1>{{ $page->fields->headline }}</h1>
<p>{{ $page->fields->subheadline }}</p>
@if($page->fields->hero_image)
<img src="{{ $page->fields->hero_image }}" alt="{{ $page->fields->headline }}" />
@endif
{!! $page->fields->body !!}
</main>
@endsection
Collections
- Service Class
- Facade
// 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->getData()->brands;
return view('brands.index', compact('brands'));
}
}
// 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->getData()->brands;
return view('brands.index', compact('brands'));
}
}
{{-- 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
// app/View/Components/ButterComponent.php
<?php
namespace App\View\Components;
use Illuminate\View\Component;
class ButterComponent extends Component
{
public function __construct(
public object $component
) {}
public function render()
{
$viewName = 'components.butter.' . $this->component->type;
if (!view()->exists($viewName)) {
return '';
}
return view($viewName, ['fields' => $this->component->fields]);
}
}
Example component
{{-- 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>
{{-- 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>
{{-- 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
- Service Class
- Facade
// 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 {
$response = $this->butter->getPage('landing-page', $slug);
$page = $response->getData();
} catch (\Exception $e) {
abort(404);
}
$components = $page->fields->body ?? [];
return view('pages.component-page', compact('page', 'components'));
}
}
// app/Http/Controllers/ComponentPageController.php
<?php
namespace App\Http\Controllers;
use Butter;
class ComponentPageController extends Controller
{
public function show(string $slug)
{
try {
$response = Butter::fetchPage('landing-page', $slug);
$page = $response->getData();
} catch (\Exception $e) {
abort(404);
}
$components = $page->fields->body ?? [];
return view('pages.component-page', compact('page', 'components'));
}
}
{{-- resources/views/pages/component-page.blade.php --}}
@extends('layouts.app')
@section('title', $page->fields->seo->title ?? 'Page')
@section('content')
<main>
@foreach($components as $component)
<x-butter-component :component="$component" />
@endforeach
</main>
@endsection
Blog
- Blog List
- Blog Post
- Service Class
- Facade
// 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->getData();
$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->getData();
$meta = $response->getMeta();
return view('blog.index', compact('posts', 'meta'));
}
}
// 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->getData();
$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->getData();
$meta = $response->getMeta();
return view('blog.index', compact('posts', 'meta'));
}
}
{{-- 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->slug) }}">{{ $post->title }}</a></h2>
<p class="meta">
By {{ $post->author->first_name }} {{ $post->author->last_name }}
on {{ \Carbon\Carbon::parse($post->published)->format('F j, Y') }}
</p>
<p>{{ $post->summary }}</p>
</li>
@endforeach
</ul>
@if($meta->next_page)
<a href="{{ route('blog.index', ['page' => $meta->next_page]) }}">Next Page</a>
@endif
</main>
@endsection
- Service Class
- Facade
// app/Http/Controllers/BlogController.php
public function show(string $slug)
{
try {
$response = $this->butter->getPost($slug);
$post = $response->getData();
} catch (\Exception $e) {
abort(404);
}
return view('blog.show', compact('post'));
}
// app/Http/Controllers/BlogController.php
public function show(string $slug)
{
try {
$response = Butter::fetchPost($slug);
$post = $response->getData();
} catch (\Exception $e) {
abort(404);
}
return view('blog.show', compact('post'));
}
{{-- resources/views/blog/show.blade.php --}}
@extends('layouts.app')
@section('title', $post->seo_title ?? $post->title)
@section('description', $post->meta_description ?? $post->summary)
@section('content')
<article>
<h1>{{ $post->title }}</h1>
<p class="meta">
By {{ $post->author->first_name }} {{ $post->author->last_name }}
on {{ \Carbon\Carbon::parse($post->published)->format('F j, Y') }}
</p>
@if($post->featured_image)
<img src="{{ $post->featured_image }}" alt="{{ $post->title }}" />
@endif
{!! $post->body !!}
@if(count($post->categories))
<div class="categories">
Categories:
@foreach($post->categories as $category)
<a href="{{ route('blog.category', $category->slug) }}">{{ $category->name }}</a>
@endforeach
</div>
@endif
<a href="{{ route('blog.index') }}">Back to Posts</a>
</article>
@endsection
Routes
// 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
// app/Http/Controllers/PageController.php
public function show(string $slug = 'home', Request $request)
{
$preview = $request->get('preview') === 'true';
try {
$params = ['locale' => app()->getLocale()];
if ($preview) {
$params['preview'] = 1;
}
$response = $this->butter->getPage('landing-page', $slug, $params);
$page = $response->getData();
} catch (\Exception $e) {
abort(404);
}
return view('pages.landing', compact('page', 'preview'));
}
Caching
// 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
// 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
{{-- 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>
{{-- resources/views/pages/landing.blade.php --}}
@extends('layouts.app')
@section('title', $page->fields->seo->title ?? $page->fields->headline)
@section('description', $page->fields->seo->description ?? '')
@section('og_title', $page->fields->seo->og_title ?? $page->fields->seo->title ?? $page->fields->headline)
@section('og_description', $page->fields->seo->og_description ?? $page->fields->seo->description ?? '')
@if($page->fields->seo->og_image ?? null)
@section('og_image', $page->fields->seo->og_image)
@endif
@section('content')
<!-- Page content -->
@endsection
Resources
Laravel Starter
Pre-configured starter project
PHP SDK
Complete SDK reference
GitHub Repository
View source code
Webhooks
Set up content webhooks