GSD

How to Build a Blog with Laravel (& Send Slack Notifications)

Posted by Shahed Nasser on October 11, 2023

Laravel is one of the most popular frameworks to create a variety of websites. You can create portfolio websites, admin dashboards, e-commerce websites, and more. Most importantly, you can create blogs, a process central to this Laravel blog tutorial. Typically this, however, would require setting up a database and an admin dashboard to be able to enter the posts and their content and then display them.

An easier way to do this is to use a headless CMS solution like ButterCMS. With ButterCMS, you don’t need to go through the hassles of creating and configuring your database or a dashboard to create posts. ButterCMS provides all that for you and more. You can create pages, blog posts, or your own entity types with fields that you choose based on your use cases. Then, all you need to do is retrieve that content with ButterCMS’s APIs, or SDKs, and display them in Laravel.

In this tutorial, you will learn how to create a blog with Laravel and ButterCMS. Then, you’ll add integration into ButterCMS’ webhooks that will send notifications directly to your Slack channel of choice whenever a new post is published.

You can find the code used in this tutorial at this GitHub repository.

banner-cta-laravel-blue.webp

Why use Laravel?

Laravel is a full-stack PHP framework that can be integrated with powerful front-end frameworks such as React or Vue.js. With Laravel, you’ll be creating websites and apps that are scalable and capable of growth.

In particular, Laravel is a good choice when you need to create a blog. Not only can you easily integrate it with third-party tools such as a headless CMS, but you can also add a ton of other functionalities like authentication, commenting, and more. You can also integrate with popular platforms like Slack, as you’ll see in this tutorial. While following along with this tutorial, we recommend that you refer to our list of Laravel best practices to make sure your application runs smoothly. 

Laravel Blog Tutorial Prerequisites

Before you start, make sure you have at least version 7.3 of PHP installed. Also, you need to have Composer installed.

Step One: Create a ButterCMS account

Start by creating a ButterCMS account if you don’t have one already. You can create one with a social media account or your email. You’ll also get the first 30 days free!

After you create an account, you can start creating posts. In the sidebar, click on Blog Posts.

ButterCMS blog post interface button

After that, click on New Post at the top right. You’ll be able to enter the post’s title, content, image, and more.

Sample of a new blog post in the ButterCMS blog engine

Once you’re done, click on Publish at the top right.

Try to add a couple of posts to see them later in your Laravel blog app.

Step Two: Create a Laravel project

In this section, you’ll create a Laravel project and add the dependencies needed for this tutorial.

Open a terminal and run the following command:

composer create-project laravel/laravel butter-blog

This will create a new Laravel project in a new directory called butter-blog.

Change to the newly created directory:

cd butter-blog

Next, you’ll install ButterCMS’s PHP API client:

composer require buttercms/buttercms-php

In order to connect Laravel with ButterCMS using their APIs, you need to grab your account’s API token. You can find it on your dashboard by scrolling down to the “Integrate Butter into your app” section.

After copying your API token, open .env and add the following new key:

BUTTER_API_KEY=

Make sure to set the value to your API token.

You’re now all set to start showing your posts in Laravel!

Step Three: Make a posts page

You’ll now create a controller that will grab all posts from ButterCMS and display them in cards on the home page.

To create a controller, run the following command:

php artisan make:controller BlogController

This will create the file app/Http/Controllers/BlogController.php. Open the file, and add the following new method:

public function home() {
        $butterClient = new ButterCMS(env('BUTTER_API_KEY'));
        $postsResponse = $butterClient->fetchPosts();

        return view('home', ['posts' => $postsResponse->getPosts()]);
}

First, you create a new client for ButterCMS using the SDK you installed earlier. Notice that you pass to the constructor the API token. You retrieve the API token from .env using the env helper function.

Then, you can retrieve posts with fetchPosts on the client. This method returns a PostsResponse instance. You can then use that instance to retrieve the posts with the method getPosts. You pass the posts to the view home which you’ll create next.

Create the view home in resources/views/home.blade.php with the following content:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Butter 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>
  <nav class="navbar navbar-light bg-light">
    <div class="container-fluid">
      <a class="navbar-brand" href="/">Butter Blog</a>
    </div>
  </nav>
  <div class="container my-4">
    <div class="row">
      @forelse ($posts as $post)
        <div class="card col-2 col-md-4 me-2 p-0">
          <img src="{{ $post->getFeaturedImage() }}" class="card-img-top" alt="{{ $post->getFeaturedImageAlt() }}">
          <div class="card-body">
            <h5 class="card-title">{{ $post->getTitle() }}</h5>
            <p class="card-text">{{ $post->getSummary() }}</p>
            <a href="#" class="btn btn-primary">Read More</a>
          </div>
        </div>
      @empty
        <div class="card">
          <div class="card-body">
            <p class="card-text">There are no posts!</p>
          </div>
        </div>
      @endforelse
    </div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
</body>
</html>

This will show the posts passed from the controller in a row of cards. Please notice that you are linking to Bootstrap’s CDN for easy styling. If there are no posts, a message on a card saying that there are no posts will be shown.

Finally, you need to link the home route to the new method in the controller we just created. In routes/web.php replace the current / route with the following:

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

Your home page is now ready. In your terminal, run the server:

php artisan serve

This will run your blog at localhost:8000. Open it, and you’ll see your posts displayed in a row.

A blog homepage with two cards built with ButterCMS

This shows the blog posts you have added. Notice that the “Best Blog with Butter CMS” is a default blog post that you’ll find once you create an account at ButterCMS. I have only changed the image.

Notice that the “Read More” buttons currently don’t work. You’ll add a page to view a post next.

Step Four: Make a single post page

In app/Http/Controllers/BlogController.php add a new method to show a single post:

public function showPost (Request $request, $slug) {
        $butterClient = new ButterCMS(env('BUTTER_API_KEY'));
        try {
            $postResponse = $butterClient->fetchPost($slug);
        } catch (RequestException $error) {
            //in case the post is not found
            return redirect('/');
        }

        return view('post', ['post' => $postResponse->getPost()]);
    }

Similar to the previous method we added, you first create a new ButterCMS client using the SDK. Then, to fetch a single post you can use the fetchPost method on the client. This method accepts a parameter, which is the post’s slug. This slug will be passed as a parameter to the page’s URL.

If a post with the slug passed does not exist, the API will return a 404 response and a RequestException will be thrown. So, you wrap the method call in a try-catch block. For simplicity, in the code block above you just redirect to the home page if the post does not exist.

If the post exists, the method will return a PostResponse instance. You can use the getPost method on this instance to retrieve the post. You pass it to the post view which you’ll create next.

Create the file resources/views/posts.blade.php with the following content:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Butter 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">
  <meta name="description" content="{{ $post->getMetaDescription() }}" />
</head>
<body>
  <nav class="navbar navbar-light bg-light">
    <div class="container-fluid">
      <a class="navbar-brand" href="/">Butter Blog</a>
    </div>
  </nav>
  <div class="container my-4">
    <h1 class="mb-3">{{ $post->getTitle() }}</h1>
    <p class="text-muted">{{ $post->getSummary() }}</p>
    <div class="w-50 my-3">
      <img src="{{ $post->getFeaturedImage() }}" alt="{{ $post->getFeaturedImageAlt() }}" class="img-fluid" />
    </div>
    <div class="overflow-hidden">
      {!! $post->getBody() !!}
    </div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
</body>
</html>

On this page, you’re showing the post’s title, summary, image, and content. Notice that since the content entered in ButterCMS can contain HTML elements like links or videos, you display it using Blade’s unescaped directive {!! !!}.

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

Route::get('/post/{slug}', [App\Http\Controllers\BlogController::class, 'showPost'])->name('post');

Finally, change the link of the buttons in home.blade.php to link to this new route:

<a href="{{ route('post', ['slug' => $post->getSlug()]) }}" class="btn btn-primary">Read More</a>

Run the server again if it isn’t still running and open the website again. If you click on the “Read More” button now, you should be taken to the post’s page where you can view its content.

Full blog post on a blog built with ButterCMS

banner-cta-laravel-blue.webp

Step Five: Integrate Slack

In this section of our laravel blog example, you’ll learn how to integrate Slack and send notifications to a Slack channel whenever a new post is published from ButterCMS.

To do that, you will need to register a webhook receiver on your ButterCMS account so that whenever a new post is published, a POST request is sent to a certain URL. This URL will be a route in your Laravel blog. When the blog instance receives the POST request, it will send a notification to the Slack channel that a new post has been published.

Create Your Slack App

To integrate Slack, you need a Slack channel. If you don’t have one, please go ahead and create one.

After that, you need to create an app. To do that, after you sign in to Slack, go to your app directory. Then, click on “Create New App” at the top right.

Slack "create new app" button

In the pop-up that shows up, click on “From scratch”. You then need to enter App Name and a workspace that this app will be connected to.

Once you’re done, you’ll be redirected to the app’s page. After that, you need to enable Socket Mode for your app. This will allow you to receive payloads using web sockets. When you open the page, just enable the switch.

Enable socket mode in Slack toggle

When you click on the switch, a pop-up will open and you’ll need to enter a name for the token to be generated. Enter “butter blog” then click Generate.

Next, you need to activate incoming webhooks. Go to Incoming Webhooks in the sidebar and enable the switch.

The last thing you need to do is install the app into your workspace. On your app’s dashboard, click on the “Install your app” accordion, then click on “Install to Workspace”. You can then choose a channel to post the messages to from the drop-down. Once you’re done, click on “Allow”.

In order to integrate Laravel with your Slack app, you need to retrieve your app’s webhook URL. Go to the Incoming Webhooks page again from the sidebar. If you scroll down, you’ll see a Webhook URL to a channel in your workspace. 

webhook url

Copy this URL, as you’ll need to use it later.

Now, back in our project, you need to install slack-notification-channel:

composer require laravel/slack-notification-channel

After that, in .env add the webhook URL you copied earlier:

SLACK_WEBHOOK=

To submit notifications from Laravel to Slack, you need to submit notifications to notifiable entities. This tutorial does not have users, but you’ll simulate that in a simple manner.

You’ll use Laravel’s user factory to create a user in your database. In database/seeders/DatabaseSeeder.php you’ll see a commented line inside the run method. Replace it with the following:

\App\Models\User::factory(1)->create();

Next, you need to prepare the user entity to receive notifications on Slack by adding the following method in app/Models/User.php:

public function routeNotificationForSlack($notification)
{
        return env('SLACK_WEBHOOK');
}

This retrieves the webhook URL you added in .env.

After that, you need to create a Notification class to send the slack notifications to. In your terminal, run the following command:

php artisan make:notification NewPost

This will create a new notification class in app/Notifications/NewPost.php with the base methods needed. First, change the __constructor method to the following:

public function __construct($postTitle, $postUrl)
    {
        $this->postTitle = $postTitle;
        $this->postUrl = $postUrl;
    }

This will add 2 class fields: postTitle for the title of the post and postUrl for the URL of the post.

Then, change the via method to the following:

public function via($notifiable)
    {
        return ['slack'];
    }

This indicates which notification methods are allowed for this notification.

Finally, add the following method to send messages to Slack:

public function toSlack($notifiable)
    {
        return (new SlackMessage)
                    ->content('New post ' . $this->postTitle . ' at ' . $this->postUrl);
    }

This will create a new SlackMessage instance and will pass content using the postTitle and postUrl fields.

Add Webhook Receiver

The integration is now ready. Next, we need to create the route that will receive the webhook POST request from ButterCMS whenever a post is published.

In app/Http/Controllers/BlogController.php add the following new method:

public function sendNotification (Request $request) {
        $butterClient = new ButterCMS(env('BUTTER_API_KEY'));
        //get slug from request
        $slug = $request->get('data')['id'];
        //get post
        $postResponse = $butterClient->fetchPost($slug);
        //get all users
        $users = User::all();
        if ($users->count()) {
            //send notification to slack
            Notification::sendNow($users, new NewPost($postResponse->getPost()->getTitle(), route('post', ['slug' => $postResponse->getPost()->getSlug()])));
        }
    }

The webhook POST request sends data of the following format:

{
  "data": {
      "url": "http://www.example.com/blog/this-is-a-blog-post",
      "created": "2015-06-12T13:59:32.441289Z",
      "published": "2015-06-12T00:00:00Z",
      "author": {
        "first_name": "API",
        "last_name": "Test",
        "email": "apitest@buttercms.com",
        "slug": "api-test",
        "bio": "This is my bio.",
        "title": "API",
        "linkedin_url": "https://www.linkedin.com/in/API",
        "facebook_url": "https://www.facebook.com/API",
        "twitter_handle": "buttercmsapi",
        "profile_image": "https://buttercms.com/api.png"
      },
      "categories": [
        {
          "name": "test category",
          "slug": "test-category"
        }
      ],
      "tags": [
        {
          "name": "test tag",
          "slug": "test-tag"
        }
      ],
      "featured_image": null,
      "featured_image_alt": "",
      "slug": "this-is-a-blog-post",
      "title": "This is a blog post",
      "body": "<p class=\"\">This is a blog post to test the API.</p>",
      "summary": "This is a blog post to test the API.",
      "seo_title": "This is a blog post",
      "meta_description": "This is a blog post to test the API.",
      "status": "published",
      "rank": 0.5
    }, 
  "webhook": {
    "event": "post.all", 
    "target": "http://yoururl.com/process_webbook"
  }
}


So, in the new method you just added, you get the slug from id in data, then retrieve the post details using $butterClient->fetchPost passing it the slug. Finally, you send the Slack notification to all the users using the NewPost class you created earlier.

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

Route::post('/new-post', [App\Http\Controllers\BlogController::class, 'sendNotification']);

The last thing we’ll do is add an exception for this route for CSRF token validation. Usually, you should create a key that the sender of the POST request should add in the header. However, for the tutorial’s simplicity, we’ll just add an exception for this route.

In app/Http/Middleware/VerifyCsrfToken.php add the route to the $except array:

protected $except = [
        //
        '/new-post'
    ];

 

Add Webhook in ButterCMS

The last step is to add the webhook receiver URL in ButterCMS’ settings. However, before you can do that, you need to deploy your app publicly to be accessed by ButterCMS.

Deploy Blog

In this section, you’ll be deploying the blog on Heroku’s free tier hosting. This will be a simple walkthrough. If you already have your blog deployed, then you can skip this section.

First, you need to download and install Heroku’s CLI. You also need to create an account at Heroku.

Then, in your terminal, you need to first log in with Heroku:

heroku login

This will open your default browser and ask you to log in.

After you log in, you can start creating your Heroku website to deploy the blog to.

By default, Heroku will launch your website from the root directory. However, in Laravel, the website should be launched from the public directory. Heroku can be configured to do that using a Procfile.

In your terminal, run the following to create a Procfile:

echo "web: vendor/bin/heroku-php-apache2 public/" > Procfile

Now, create a new Heroku app:

heroku apps:create <APP_NAME>

Make sure to replace <APP_NAME> with the name of the app you want to create. For example, butter-blog. Heroku will create an app and show you a URL and a Git URL. Copy the Git URL as you’ll be using it next.

Now, link your code to the Git repository that Heroku created:

git remote add origin <GIT_URL>
git add .
git commit -m “initial commit”
git push origin master

Make sure to replace <GIT_URL> with the Git URL you copied earlier. This will deploy the website with the code to the app that Heroku created.

Next, you need to set some configurations. Start by opening your .env file and copying the value for APP_KEY.

Then, in your terminal run the following:

heroku config:set APP_KEY=<APP_KEY>

Make sure to replace <APP_KEY> with the value you copied from .env.

Next, copy the value of BUTTER_API_KEY from .env and set it with Heroku:

heroku config:set BUTTER_API_KEY=<BUTTER_API_KEY>

After that, copy the value of SLACK_WEBHOOK from .env and set it with Heroku:

heroku config:set SLACK_WEBHOOK=<SLACK_WEBHOOK>

Next, you need to create a database that will store the tables and fake users. You’ll use the JawsDB MySQL AddOn, which is free.

To add the AddOn to your Heroku app, run the following command:

heroku addons:create jawsdb

Then, you need to retrieve the database URL. You can do it with the following command:

heroku config:get JAWSDB_URL

This will output the URL of the MySQL database. It will be in the following format:

mysql://<USERNAME>:<PASSWORD>@<HOST>:<PORT>/<DATABASE>

You need to copy each of these elements and set the values in Heroku’s configurations with the following commands:

heroku config:set DB_CONNECTION=mysql
heroku config:set DB_DATABASE=<DATABASE>
heroku config:set DB_HOST=<HOST>
heroku config:set DB_USERNAME=<USERNAME>
heroku config:set DB_PASSWORD=<PASSWORD>
heroku config:set DB_PORT=<PORT>

The last thing you need to do is migrate and seed the database. To do that, open a bash connection into the Heroku server with the following command:

heroku run bash

When the bash connection to the server is open, first run the following:

composer install --dev

This will install the development dependencies required for seeding.

Then, migrate the database:

php artisan migrate --force

Then, seed the database:

php artisan db:seed --force

The Heroku server is now done and ready to receive webhook requests.

Setup Webhook in ButterCMS

In your ButterCMS account, put your mouse over the avatar at the top right then click Settings in the dropdown that shows up.

ButterCMS settings location within the interface

Then, from the tabs, choose “Webhooks”. You’ll need to fill in the row that shows the target URL as your Heroku URL followed by /new-post. In the Event dropdown, choose post.published. Once you’re done click Save Changes.

Webhooks tab in ButterCMS settings section

The integration is now ready! Go create a new blog post for this laravel blog project in ButterCMS and publish it. You should see a new notification for the new post in the channel you chose for integration in Slack! I myself created a new post entitled "How to Send a POST Request in PHP."

New post notification in Slack

And the post is now available on the laravel blog website as well.

New card on blog homepage after publishing

Conclusion

In this tutorial, you learned how to build a blog with Laravel and ButterCMS. You learned how you can use the SDK to show all posts or a single post. You also learned how to integrate webhooks between ButterCMS and Laravel, and use them to send Slack notifications whenever you publish a new Laravel blog post.

As you can see from this tutorial, ButterCMS saves you the time you’d spend building the CMS functionalities into Laravel as it provides a clean and easy-to-use interface that can get you started with your blog in seconds. ButterCMS’s APIs and SDKs then allow you to integrate it into any platform of your choice.

Receive the freshest Butter product updates and tutorials directly in your inbox.
    
Shahed Nasser

Shahed is a full-stack developer passionate about learning something new every day, then sharing her knowledge with the community.

ButterCMS is the #1 rated Headless CMS

G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award

Don’t miss a single post

Get our latest articles, stay updated!