.

Creating my Blog with Laravel

In this post, we are going to walk through creating a Laravel powered blog that consumes markdown files.

First things first, create a fresh laravel project. If you've never done that before, checkout the docs for the latest version. Laravel Installation

To start things off, let's create the BlogController

php artisan make:controller BlogController

Next, create an index method in the controller and remove the default extends Controller

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class BlogController
{
    public function index()
    {
        // wip
    }
}

Now, let’s create our first post. We are going to store all the posts within storage/app/posts directory. Before creating anything we need to update the .gitignore so that once created git will track the files otherwise we will have to manaully add the files to git alter.

In storage > app > .gitignore add !posts/ & !posts/*. Your gitignore should look like this

*
!public/
!posts/
!posts/*
!.gitignore

Now that thats out of the way we'll create the posts directory within storage/app.

Now we can create our first post. My first post will be this one. I'll name it creating-my-blog-with-laravel.md

At the top of your markdown file we will include some metadata that we can use later. It should look like this. Notice the three dashes to start and end the metadata. This is important and will signify when the metadata starts and ends.

---
published: August 23 2020
title: Creating my blog with Laravel
slug: creating-laravel-powered-blog
---

In this post, we are going to walk through creating a laravel powered blog that consumes markdown files.

This is what your directory structure should look like:

storage
    app
        posts
            creating-my-blog-with-laravel.md

Now that we have the basic strucutre in place let's build the class that will prepare the posts for displaying. I'm calling this class PostPrepper

php artisan make:model PostPrepper

This command will generate the default scaffold

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class PostPrepper extends Model
{
    //
}

We don't need to extend Model right now, so we'll remove that. We are also going to be working with the Storage facade, and the Str helper, so let's add the use statements for both of those.

In addition to those we will need to also pull in an outside package for converting our markdown to html. I'm going to use The PHP League's commonmark package. Once installed add the GithubFlavoredMarkdownConverter to your class

$ composer require league/commonmark

This is what PostPrepper should look like so far:

<?php

namespace App;

use League\CommonMark\GithubFlavoredMarkdownConverter;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;

class PostPrepper
{
    //
}

Now let's get down to the nitty gritty. Next we need to write the code that will retrieve all the files, scrape the markdown metadata, and add it to cache for our BlogController to consume.

We are going to add four different methods to our class.

  • prep
  • getMetaArray
  • getMetaString
  • getContent

In addition to our methods we will create three private properties to store our data

  • metaArray
  • metaString
  • content

First, lets extract the meta data from the markdown and return it as a string

private function getMetaString(string $file) : string
{
    $matches = [];
    preg_match('/---[\s\S.]*?---/', $file, $matches);

    return $matches[0];
}

Then we will turn the string of metedata into an array

private function getMetaArray(string $file) : array
{
    $metaArray = [];
    $metaString = $this->getMetaString($file);
    foreach (explode("\n", trim(trim($metaString, '---'))) as $data) {
        $temp = explode(':', $data);
        $metaArray[$temp[0]] = trim($temp[1]);
    }

    return $metaArray;
}

We'll also create a method for returning the content of the post. The post should exclude the metadata, so we pass the file, and metadata to the method and trim the metadata from the content.

private function getContent(string $file, string $meta) : string
{
    return trim($file, $meta);
}

Now lets put all the pieces togheter. We do a few things here. We create a posts array that will be stored in cache. The post array includes title, published date, slug, and the content. The title is also the key of the array which will help us find it much easier later. From this point on the content will be stored as html and not markdown. Notice, we do that with the GithubFlavoredMarkdownConveter from The PHP League, which installed earlier.

public function prep() : void
{
    $posts = [];
    $converter = new GithubFlavoredMarkdownConverter();
    foreach (Storage::allFiles('posts') as $post) {
        $file = Storage::get($post);
        $posts[$this->getMetaArray($file)['slug']] = [
            'title' => $this->getMetaArray($file)['title'],
            'published' => $this->getMetaArray($file)['published'],
            'slug' => $this->getMetaArray($file)['slug'],
            'content' => $converter->convertToHtml($this->getContent($file, $this->getMetaString($file)))
        ];
    }

    cache(['posts' => $posts]);
}

Now, we need to instantiate our new class in our AppServiceProvider. Once this is done our post data will be avialable to us in our cache

public function boot()
{
    (new PostPrepper)->prep();
}

Let's finish up the work on our BlogController.

public function index()
{
    return view('blog.index', ['posts' => cache('posts')]);
}

We'll also include a show method for viewing the blog. We simply match the slug in the url with the post array key and if it matches we return the view with the proper data. If it doesn't match we'll return a 404.

public function show($slug)
{
    if (isset(cache('posts')[$slug])) {
        $post = cache('posts')[$slug];
        return view('blog.show', ['title' => $post['title'], 'content' => $post['content']]);
    }
    abort(404);
}

Your views directory should look like this

resources
    views
        blogs
            index.blade.php
            show.blade.php

Both views are pretty simple. I'm using tailwindui content compoent which really cleans it up and makes it readable and clean.

// show.blade.php
@extends('layout')

@section('content')
     <div class="prose prose-lg text-gray-500 mx-auto">
        <h1>{{$title}}</h1>
        {!! $content !!}
    </div>
@endsection
// index.blade.php
@extends('layout')

@section('content')
<div class="prose prose-lg text-gray-500 mx-auto text-center">
    @foreach ($posts as $post)
        <a href="blog/{{$post['slug']}}" class="text-3xl">{{ $post['title'] }}</a>
    @endforeach
</div>
@endsection

And that's a wrap! You should be able to write your blog posts in markdown and dynamically display an index page of all your writtings as well as display each post.

Thanks for tagging along!