Add a headless CMS to Laravel in 5 minutes

In this short article, we will show you how you can use the headless CMS Storyblok in combination with the PHP Framework for Web Artisans “Laravel”. At the end of this article, you will have a Laravel Application which renders components according to the data of the API of Storyblok.

Storyblok integrates in laravel

Let me start with a short explanation of Storyblok first: Storyblok is a headless CMS in which you can create components nested in Stories (If you want to create a website you can go with - a story equals to a page). The key concepts behind Storyblok are highly inspired by the BEM methodology. The author of a story can create a nested tree of components and input content in an easy and fast responding interface. You can find more in our Introduction. You can then call the read-only CDN with the JSON tree and render your components in a loop. During this article, for example, we will use following JSON to render our first story:

/v1/cdn/stories/home
$ curl https://api.storyblok.com/v1/cdn/stories/home?token=akYA0RB4BzCPUoRfjIvUdQtt
/v1/cdn/stories/home
{
  "story": {
    "name": "home",
    "content": {
      "component": "root",
      "body": [
        {
          "component": "teaser",
          "headline": "Easy - isn't it?"
        }
      ]
    },
    "slug": "home",
    "full_slug": "home"
  }
}

Laravel

I’m sure that most of you are already familiar with Laravel and it’s basics - if not I would suggest you start with the Installation of Laravel. The Laravel framework has a few system requirements:

Start a new laravel project

You can add Storyblok to existing projects as well - for simplicity we will show how to add Storyblok to a completely fresh project - so a beginner to the world of Laravel can use Storyblok as their CMS as well because it’s API-based and only returns data for your application. Execute the following command so you’ve got a freshly created project ready to start with:

your command line
laravel new larablok

You can simply run your fresh application after executing:

composer install && php artisan serve

You can read more about the laravel setup in their documentation mentioned above.

Install the Storyblok PHP Client

Storyblok already provides a PHP client - so we won’t have to think about how we’re doing the API requests and receive data from the content delivery API – all we have to do is:

your command line
composer require storyblok/php-client

This will add the \Storyblok\Client to your composer.json.

Let's load our Story

In the routes/web.php, we will initialize the Storyblok Client and directly load the Story with the slug "home" – you can, of course, use a route parameter to load a story according to the slug which was received as an optional parameter. Don’t mind the strange random string we use to initialize the Storyblok client – It is the private token for the test space which provides the data – you can switch this private token with one of yours from your own space later.

routes/web.php
Route::get('/{slug?}', function ($slug = 'home') {
  $storyblok = new \Storyblok\Client('akYA0RB4BzCPUoRfjIvUdQtt');
  $data = $storyblok->getStoryBySlug($slug)->getStoryContent();
  return view('index', ['story' => (object)$data['story']]);
});

As you can see we’re missing the index.blade.php. I’ve prepared that for you – you can now directly paste this into your resources/views folder.

resources/views/index.blade.php
<!doctype html>
<html lang="{{ config('app.locale') }}">
  <head>
    <title>{{ $story->name }}</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0" />
    <meta http-equiv="X-UA-Compatible" content="IE=11"/>
    <meta name="generator" content="storyblok">
    <meta name="cms" content="https://www.storyblok.com">
    <link rel="icon" type="image/png" href="//app.storyblok.com/images/favicon-32x32.png" sizes="32x32">
    <link rel="stylesheet" href="{{ url('css/app.css') }}" media="all" />
  </head>
  <body>

	@include('components/' . $story->content['component'], ['blok' => $story->content])
  
	<script type="text/javascript" src="{{ url('js/app.js') }}"></script>

	<script type="text/javascript" src="//app.storyblok.com/storyblok-latest.js"></script>
	<script type="text/javascript">
		storyblok.init();
		storyblok.on('change', function() {
			window.location.reload(true);
		});
	</script>
  </body>
</html>

Let’s focus on one line by now:

resources/views/index.blade.php
@include('components/' . $story->content['component'], ['blok' => $story->content])

as you can see this one line, which uses build in functionality of the blade templating engine, includes a partial with a parameter passed to render that component.

If you now have a look at the JSON at the top of this article you will see that the first component in our story was called root. And that’s the next file we will have to create.

Create our first nested component

To still have some kind of overview in our *.blade.php files I’ve created a components folder inside the views so we know which components are used for those includes.

In the JSON above we can see that the object of the root component only has one array as property – body:

/v1/cdn/stories/home
...
"body": [
  {
    "headline": "Easy - isn't it?",
    "component": "teaser"
  }
]
...

All it’s here for us now is to iterate through all the components which are nested in that array - so the whole root component will look like:

resources/views/components/root.blade.php
@foreach ($blok['body'] as $blok)
  @include('components/' . $blok['component'], ['blok' => $blok])
@endforeach

Not much to do – but why should we over-engineer a simple include in a loop. The next component in our JSON was the teaser component.

resources/views/components/teaser.blade.php
<div class="teaser">
  <!--
  The _editable attribute makes the next
  DOM-element clickable so the sidebar can
  show the right component.
  -->
  {!! $blok['_editable'] or '' !!}
    <h1>
      <!--
      You can access every attribute you
      define in the schema in the blok variable
      -->
      {{$blok['headline']}}
    </h1>
    <h2>
        You can create new components like this - to create your own set of components.
    </h2>
  </div>
</div>

Congrats!

You’ve added Storyblok as a CMS into a laravel application.

Content Management for Laravel made easy.

But wait - there is something we didn’t mention yet – what about that line above the h1?

resources/views/components/teaser.blade.php
{!! $blok['_editable'] or '' !!}

This line of code will output the text included in the _editable property of an Storyblok component. If your application will be opened through the Storyblok SideBySide Editor (the place where the content of the teaser can be changed) we need some kind of match to your website so we can identify a component.

The content of the _editable property is actually nothing more than a simple HTML comment - with the Storyblok script we included in the index.blade.php we can enable frontend editing without touching your actual HTML – only placing an HTML comment before the Blok you want to edit – an headline for example. So Storyblok won’t change your DOM or has any side effects on your CSS (:first or :nth-child for example).

Configuring Storyblok

Now that you have successfully integrated Storyblok in your project let’s create a “Story” in your own Storyblok space.

Using the CLI:

  1. npm install -g storyblok
  2. storyblok quickstart

Using the Webinterface:

  1. Go to https://app.storyblok.com/#!/signup and do the signup
  2. Create a new Space.

Both ways will start with the quickstart which will guide you through your first time creating a component in Storyblok.

Follow the quickstart

Exchange the private token

Copy your private token to receive the draft version of your content. The private token is a read only token and can be found in the space dashboard. Insert your token in “routes/web.php”.

Route::get('/{slug?}', function ($slug = 'home') {
  $storyblok = new \Storyblok\Client('YOUR_ACCESS_TOKEN');
  $data = $storyblok->getStoryBySlug($slug)->getStoryContent();
  return view('index', ['story' => (object)$data['story']]);
});
Your own private token

Add your environment

After adding your own token from your Space to your project - we will have to also tell Storyblok where to find our dev environment. For this we will navigate to the Settings of a Space and add the URL http://localhost:8000/ as a new environment.

add a dev environment

Well done!

Try out to insert a text and click “Save”. Your component should now be updated with the new content. Next step is to repeat the steps above to configure more components. You can also create grid and column components which can nest other components with the schema type “components”. A root component is a component which has other components nested.

Endresult

Components in this Tutorial

You can create as many components and with as many fields as you want. You can even nest them as deep as you want them to. I would suggest you to read the Component Terminology before you start creating your own components.

Component NameComponent Use
rootholder for all nested components - only has a property of the typ "components".
teasercontains the property "headline" so the component teaser can be edited in the SideBySide Editor.

If you want to know how to create the space to this article you can just follow our youtube tutorial for this setup. You can also find the whole package we created during this tutorial on github.

Next: Learn about components

How thinking in components can increase your productivity? Components are units of code that are nestable, reusable and decoupled. They help you to don’t repeat yourself writing the same functionality or HTML tags multiple times. They ease the process of thinking because…

You can also visit our other stories to learn how to build a grid layout or headers.


More to read...

About the author

Dominik Angerer

A web performance specialist and perfectionist. After working for big agencies as a full stack developer he founded Storyblok. He is also an active contributor to the open source community and one of the organizers of Stahlstadt.js.