Create Laravel Blog with Strapi v4
Tags: strapi, laravel, javascript, webdev
Published: 2021-12-20 09:08:12

---
title: Create Laravel Blog with Strapi v4
published: true
date: 2021-12-20 08:50:50 UTC
tags: Strapi,Laravel,Javascript,webdev
canonical_url: https://blog.shahednasser.com/create-laravel-blog-with-strapi-v4/
cover_image: https://blog.shahednasser.com/static/36dac6a27c1e3c4e16cc2c2e55053679/lum3n--RBuQ2PK_L8-unsplash-2.jpg
---

_This article was originally published [on my personal blog](https://blog.shahednasser.com/create-laravel-blog-with-strapi-v4/)._

[Laravel](https://laravel.com) is one of the most popular web frameworks that allow you to create secure and scalable websites. With Laravel, you can create almost any website, ranging from simple portfolio websites to full-fledged e-commerce solutions.

[Strapi](https://strapi.io) is a headless CMS framework that has been gaining a lot of attention. With Strapi, you can easily add CMS functionalities to your website, regardless of the technology being used. Strapi also removes the hassles of creating a database and models tailored to your CMS needs as it is customizable to the point that you can create your own models and entities, also called content types.

In this tutorial, you'll learn how to create a blog in Laravel using Strapi. The blog will have posts, tags, and allow users to post comments. You can find the code for this tutorial in [this GitHub repository](https://github.com/shahednasser/strapi-laravel-blog).

## Prerequisites 

Before you start, you need the following tools on your machine:

1. [Node.js](https://nodejs.org/en/). Only versions 12 and 14 are supported by Strapi, and 14 is recommended.
2. [PHP](https://www.php.net) >= v7.3
3. [Composer](https://getcomposer.org/download/)

Please note that this tutorial will be using Strapi v4 and Laravel v8.9

## Setup Strapi

Start by setting up Strapi. In your terminal, run the following command:

```
npx create-strapi-app@latest strapi --quickstart
```

Once the command is done, the server will start at [localhost:1337](http://localhost:1337) and a new page will open in a browser. It will be a registration form, where you need to create an admin account on Strapi.

Once you're done, you'll be redirected to the main dashboard.

![Create Laravel Blog with Strapi v4](https://backend.shahednasser.com/content/images/2021/12/Screen-Shot-2021-12-15-at-7.40.33-PM.png)

### Create Content-Types

Next, you'll create the content types necessary for the blog. You'll create content types for the posts and tags that the posts will use. You'll also create a comment content type which will be filled from our blog on Laravel.

Start by clicking on _Create your first Content type_ in the dashboard. This will open a new page to create content types. Under the Content Types Builder sidebar, click on _Create new collection type_ under  Collection type. This will open a pop-up where you can enter basic and advanced settings.

You'll first create the tags content type. Enter in the Display Name field in the popup `Tag`. This will automatically generate the singular and plural forms of the content type.

![Create Laravel Blog with Strapi v4](https://backend.shahednasser.com/content/images/2021/12/Screen-Shot-2021-12-15-at-7.45.23-PM.png)

Once you're done, click _Continue_. You can now choose the fields in this content type. tags will only have a `name` field other than their id. So, click on the _Text_ field type. Then, enter in the Name field `name`.

![Create Laravel Blog with Strapi v4](https://backend.shahednasser.com/content/images/2021/12/Screen-Shot-2021-12-15-at-7.47.28-PM.png)

In the Advanced Settings tab, check the Required checkbox to ensure that all tags have a name.

Since that's the only field you'll add for the Tag content type, click on _Finish_. Then, when the pop-up closes, click on the _Save_ button at the top right. This will restart the server. Every time you create a content type, the server is restarted.

Next, you'll create the Post content type. Again, click on _Create new collection type._ In the pop up that opens, enter for Display Name `Post`, then click on _Continue._

For posts, there will be fields for title, content, image, date posted, and tags that the post falls under.

For the `title` field, choose the Text field and make it required as we did earlier. Once done, click on _Add another field._

For the `content` field, choose the Rich text field, and make it required.

For the `image` field, choose the Media field, and choose for Type "Single media". In the Advanced Settings tab, change the allowed file types under "Select allowed types of media" to only Images. Make the field required as well.

For the `date_posted` field, choose the Date field, and choose for Type "datetime". Mark this field required as well.

Finally, for the `tags` field, choose the Relation field, then for the relation type choose "Post belongs to many Tags".

![Create Laravel Blog with Strapi v4](https://backend.shahednasser.com/content/images/2021/12/Screen-Shot-2021-12-15-at-7.59.01-PM.png)

Once you're done, click on _Finish_, then click on _Save_ at the top right. This will save the new content type and restart the server.

Finally, you need to create the Comment content type. Just like you did with the previous content types, create a new content type with the name `Comment`.

The `Comment` content type will have 3 fields. The first is an Email field with the name `field`. Make sure to set it as required.

The second field is a Rich text field with the name `content`. This is where the user's comment will go.

The third field is a Relation field between Comment and Post. The relation should be "Post has many Comments".

![Create Laravel Blog with Strapi v4](https://backend.shahednasser.com/content/images/2021/12/Screen-Shot-2021-12-16-at-8.13.33-PM.png)

Note that when you create this field a new field will be added automatically in Post called `comments`.

Once you're done, click on _Finish_, then click on _Save_ at the top right. This will save the new content type and restart the server.

Our content types are ready!

### Add Content

The next step would be to add content. Click on _Content Manager_ in the sidebar.  Start by adding a few tags by clicking on _Tag_ in the Content Manager sidebar, then click on _Add new entry_ at the top right.

When you create content, make sure you click _Publish_ after saving the content.

Next, add posts the same way. You can use [Lorem Ipsum Generator](https://loremipsum.io/generator/?n=5&t=p) if you want to create mock content.

### Change Permissions

The last step left is to make posts and tags public so that you can consume them in Laravel.

First, you'll create an API token to use for your requests. In the sidebar, click _Settings_, then _API Token_. Click on _Add Entry_ at the top right.

In this form, enter the name of the token. This is just useful to remember what your API tokens are for. You can also enter a description.

In the Token type field, choose Full Access.

![Create Laravel Blog with Strapi v4](https://backend.shahednasser.com/content/images/2021/12/Screen-Shot-2021-12-16-at-7.27.11-PM.png)

Once you're done, click on _Save_ at the top right. This will create a new API token and the API token will be shown to you only once when you create it. So, copy the API token and store it somewhere as you'll use it later.

Next, you'll modify the permissions for authenticated users to be able to query content types and add new entries.

On the sidebar, click _Settings_, then _Roles_ in the _Settings_ sidebar.

You'll see two roles: Authenticated and Public. Click on the pencil icon on the Authenticated row.

Scroll down and you'll see that for each content type you can select what this role can access. Check Select All for Post, Tag, and Comment, then click _Save_.

![Create Laravel Blog with Strapi v4](https://backend.shahednasser.com/content/images/2021/12/Screen-Shot-2021-12-16-at-7.37.10-PM.png)

## Setup Laravel

Now that Strapi is ready, you'll get started with Laravel.

Run the following command to create a new Laravel project:

```
composer create-project laravel/laravel blog
```

Once this command is done, change to the directory created:

```
cd blog
```

You can then start the server with the following command:

```
php artisan serve
```

This will start the server at `localhost:8000`.

### Add Environment Variables

Before you can make requests to Strapi, you need to add 2 environment variables. Add the following environment variables to `.env`:

```
STRAPI_URL=http://localhost:1337
STRAPI_API_TOKEN=
```

The first is the URL to Strapi. You can change it if it's not the same local URL. The second is the API token you created earlier. Paste it here after the `=` sign.

### Add Home Page

On the home page, you'll query all posts in Strapi and display them.

Run the following command in your terminal to create a new controller:

```
php artisan make:controller BlogController
```

Then, open `app/Http/Controllers/BlogController.php` and the following method in the class:

```
public function home () {
    //retrieve the posts from Strapi
    $response = Http::withToken(env('STRAPI_API_TOKEN'))->get(env('STRAPI_URL') . '/api/posts?populate=image,tags');
    $posts = [];

    if ($response->failed()) {
        if (isset($data['error'])) {
        	Log::error('Server error: ' . $data['error']['message']);
        } else {
        	Log::error('Request Failed');
        }
    } else {
        //get posts from response
        $posts = $response->json('data');
    }

    return view('home', ['posts' => $posts]);
}
```

First, you query Strapi using Laravel's [HTTP Client](https://laravel.com/docs/8.x/http-client). You use `withToken` to pass it the API token from `.env` using the `env` helper function. Then, you send a `get` request to the endpoint  `localhost:1337/api/posts?populate=image,tags`.

Notice that `localhost:1337` also is retrieved from `.env`. As for the endpoint path, Strapi has a conventional path for all its content types. When querying a collection, the pattern for the endpoint is `/api/{collection_name}`.

When you use Strapi's API, you can pass it a lot of [useful parameters](https://docs.strapi.io/developer-docs/latest/developer-resources/database-apis-reference/rest-api.html#api-parameters) that allow you to filter, sort, paginate the data, and more. Here, you use the `populate` parameter which allows you to retrieve a content type with its relations. You use it to retrieve the post with its image and tags.

After sending the request, you can check if the request failed using `$response->failed()`. If the request failed, you log the error. If not, you set `$posts` to the `data` parameter in the response body. Note that you can use the `json` method to retrieve the parameters from a JSON response, optionally passing it a parameter name as the first element.

Next, you need to add the `home` view. Create the file `resources/views/home.blade.php` with the following content:

```
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title>Blog</title>

        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
    </head>
    <body class="antialiased bg-light">
        <div class="container mt-4 py-3 mx-auto bg-white rounded shadow-sm">
            <div class="row">
                @forelse ($posts as $post)
                    <div class="col-2 col-md-4">
                        <div class="card">
                            <img src="{{ env('STRAPI_URL') . $post['attributes']['image']['data']['attributes']['formats']['medium']['url'] }}" 
                                class="card-img-top" alt="{{ $post['attributes']['image']['data']['attributes']['alternativeText'] }}">
                            <div class="card-body">
                                <h5 class="card-title">{{ $post['attributes']['title'] }}</h5>
                                <p class="card-text">{{ substr($post['attributes']['content'], 0, 50) }}...</p>
                                <a href="/post/{{ $post['id'] }}" class="btn btn-primary">Read More</a>
                            </div>
                            <div class="card-footer">
                                @if(count($post['attributes']['tags']['data']))
                                    @foreach ($post['attributes']['tags']['data'] as $tag)
                                        <span class="badge bg-success">{{ $tag['attributes']['name'] }}</span>
                                    @endforeach
                                @endif
                            </div>
                        </div>
                    </div>
                @empty
                    <div class="col">
                        <div class="card">
                            <div class="card-body">
                                This is some text within a card body.
                            </div>
                        </div>
                    </div>
                @endforelse
            </div>
        </div>
    </body>
</html>

```

This just displays the posts as [cards](https://getbootstrap.com/docs/5.1/components/card/) using [Bootstrap](https://getbootstrap.com). Notice that the content type entries that Strapi return has the following format:

```
{
	"data": {
    	"id",
        "attributes": {
        	"title",
            ...
       	}
    }
}
```

So, you'll find the content type's fields inside the `attributes` key of `data`.

Finally, change the current route in `routes/web.php` to the following:

```
Route::get('/', [\App\Http\Controllers\BlogController::class, 'home']);
```

Let's test it out. Make sure that both Laravel and Strapi's servers are running. Then, open `localhost:8000`. You'll see the posts you added as cards.

![Create Laravel Blog with Strapi v4](https://backend.shahednasser.com/content/images/2021/12/Screen-Shot-2021-12-16-at-7.48.15-PM.png)

### Add View Post Page

Next, you'll add the page to view a post. This page receives the post ID as a parameter, then queries the post's data from Strapi.

In `app/Http/Controllers/BlogController.php` add a new method:

```
public function viewPost ($id) {
        //retrieve the post from Strapi
        $response = Http::withToken(env('STRAPI_API_TOKEN'))->get(env('STRAPI_URL') . '/api/posts/' . $id . '?populate=image,tags,comments');

        if ($response->failed()) {
            if (isset($data['error'])) {
                Log::error('Server error: ' . $data['error']['message']);
            } else {
                Log::error('Request Failed');
            }

            return response()->redirectTo('/');
        }

        //get post from response
        $post = $response->json('data');

        return view('post', ['post' => $post]);
 }
```

In this method, you use the `$id` parameter, which is the post ID, to send a request to Strapi's single entry endpoint. The endpoint's pattern is `/api/{collection_name}/{id}`. Similar to the previous endpoint, you can also pass it parameters like `populate`.

If the request fails, you redirect the user to the home page and log the error. If the request is done successfully, you retrieve the post from the response's body and render the view `post`.

Now, create `resources/views/post.blade.php` with the following content:

```
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title>{{ $post['attributes']['title'] }} - Blog</title>

        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
    </head>
    <body class="antialiased bg-light">
        <div class="container mt-4 py-3 px-5 mx-auto bg-white rounded shadow-sm">
            <h1>{{ $post['attributes']['title'] }}</h1>
            <small class="text-muted d-block">{{ $post['attributes']['date_posted'] }}</small>
            <img src="{{ env('STRAPI_URL') . $post['attributes']['image']['data']['attributes']['formats']['medium']['url'] }}" 
                                class="img-fluid mx-auto d-block my-3" alt="{{ $post['attributes']['image']['data']['attributes']['alternativeText'] }}">
            @if(count($post['attributes']['tags']['data']))
              <div class="mb-3">
                @foreach ($post['attributes']['tags']['data'] as $tag)
                  <span class="badge bg-success">{{ $tag['attributes']['name'] }}</span>
                @endforeach
              </div>
            @endif
            <p class="content">
              {{ $post['attributes']['content'] }}
            </p>

            <hr />
            <form action="/post/{{ $post['id'] }}" method="POST">
              @csrf
              <h2>Add Your Comment</h2>
              <div class="mb-3">
                <label for="email" class="form-label">Email address</label>
                <input type="email" class="form-control" id="email" name="email" required>
              </div>
              <div class="mb-3">
                <label for="content" class="form-label">Your Comment</label>
                <textarea rows="5" class="form-control" id="content" name="content" required></textarea>
              </div>
              <button type="submit" class="btn btn-primary">Submit</button>
            </form>
        </div>
    </body>
</html>

```

This page just shows the post's details. Similar to how you extracted the post's data, the post's field is nested inside the `attributes` field.

This page also has a comments form at the end of it. You'll implement its functionality after this.

Finally, add the new route in `routes/web.php`:

```
Route::get('/post/{id}', [\App\Http\Controllers\BlogController::class, 'viewPost']);

```

Now, open the home page again and click on _Read More_ for one of the posts. A new page will open with the post's content.

![Create Laravel Blog with Strapi v4](https://backend.shahednasser.com/content/images/2021/12/Screen-Shot-2021-12-16-at-7.55.39-PM.png)

If you scroll down, you'll see a form to add your comment.

![Create Laravel Blog with Strapi v4](https://backend.shahednasser.com/content/images/2021/12/Screen-Shot-2021-12-16-at-8.10.42-PM.png)

### Add Comment Functionality

The last thing you'll do in this tutorial is add the commenting functionality. The form is already added, so you just need to add the `POST` route to add the comment.

Add the following method in `app/Http/Controllers/BlogController.php`:

```
public function addComment (Request $request, $id) {
        $data = [
            "data" => [
                'email' => $request->get('email'),
                'content' => $request->get('content'),
                'post' => $id
            ]
        ];

        $response = Http::withToken(env('STRAPI_API_TOKEN'))->post(env('STRAPI_URL') . '/api/comments', $data);

        if ($response->failed()) {
            if (isset($data['error'])) {
                Log::error('Server error: ' . $data['error']['message']);
            } else {
                Log::error('Request Failed');
            }

            return response()->redirectTo('/');
        }

        //successfully added
        return response()->redirectTo('/post/' . $id);
    }
```

You first format the data as Strapi likes it. When adding a content type entry, you should nest the data inside a `data` parameter. Here, you add the `email`, `content`, and `post` fields. Notice that we are skipping validation here for tutorial simplicity.

Then, you send a `POST` request to the endpoint `/api/comments`. Strapi's endpoint pattern for adding a content type entry is `/api/{collection_name}`. You pass the data as a second parameter to the `post` method.

If the request fails, the user is redirected to the home page. If it's successful, the user is redirected back to the post's page.

Next, add before the comment form in `resources/views/post.blade.php` the following:

```
<hr/>
@if (count($post['attributes']['comments']['data']))
    <div class="comments">
        <h2>Comments</h2>
        @foreach ($post['attributes']['comments']['data'] as $comment)
            <div class="card mb-3">
                <div class="card-body">
                	{{ $comment['attributes']['content'] }}
                </div>
                <div class="card-footer">
                	By {{ $comment['attributes']['email'] }}
                </div>
            </div>
        @endforeach
    </div>
@endif
```

This will show the comments if a post has any.

Finally, add the new route in `routes/web.php`:

```
Route::post('/post/{id}', [\App\Http\Controllers\BlogController::class, 'addComment']);
```

Let's test it out. Go to a post's page, then go to the comment form. Add your comment and click Submit. You'll be redirected back to the post's page, but you can see the comment below the post.

![Create Laravel Blog with Strapi v4](https://backend.shahednasser.com/content/images/2021/12/Screen-Shot-2021-12-16-at-8.10.34-PM.png)

## Conclusion

In this tutorial, you learned how to build a blog with Laravel and Strapi. Strapi is completely customizable, and that simplifies the process of adding content types, adding entries, and using its endpoints to query the content types or add new entries in them.