Fun little summer holiday project I did while spending a peaceful week in the wonderful Odsherred.
While staring up in the blue (okay, mostly gray) sky, I was wondering: How would you make a simple Laravel website based on Markdown content?
Of course, this has been done a billion times before – and a million times in Laravel.
But zero times by me. So here goes:
No GUI interface, no CMS crud interface for creating content
None of that, please!
Therefore: No rich text editor - just plain Markdown support
However, rich text preview is reluctantly allowed 😁
No database, just static index and content files
Yeah, I might regret this later.
Some kind of folder / menu / category for posts
In order to have different sections, like "blog", "projects", "random-thoughts", etc.
Enable comments on selected pages
Using a third-party service, something like Disqus.
By "static website" I don't mean flat HTML-files, but merely a website without a database. You have your content in files and your files in your VCS.
Having content in your VCS is actually nice for a number of reasons (and probably bad for more, you tell me).
But it really comes down to that I wanted a website that's easy to update - and easy for me means not leaving my IDE environment.
What VIM really does to you
I was a VI-king (a term I read lately, meaning a passionate VIM user 😬) for a decade and a half - and only left this brilliant editor for JetBrains / PHPStorm, when I was happy with the IdeaVim plugin. What VIM really does to you - and I think you have to be a VIM user to fully understand this - is break your patience when typing in a non-VIM editor. It's a pain! And any web form in therefore a pain when typing anything longer than a search term.
Anyways, on top of this you get a lot of niceness from a static website:
draft- and it will be public by link, but hidden from the index pages and RSS feed.
Also - no database = no database content to backup. 👌
This is how I want my workflow to be, when creating a new post:
Do something like
php artisan blog:new
Enter title, let the system create a slug based on this, approve or edit it.
Also - enter page section (blog, projects, ...).
The Artisan command stores the page meta data and creates a new file for the blog post. Yes, one file for each page. Open this file and you're on your way.
And that's it.
I was actually considering - for a brief moment - to put the meta data in the top of the markdown file - in Yaml format. It would be nice to have it all in one file.
However, how to make a section index page - like a list of blog post? Read all files from disc and parse the content? Or process the files and create another index file. Hmmm, nah.
On top of this, I soon realized that the Markdown format actually do not have an official way to comment things out (you can do something like
[comment]: <> (this is commented out)) - so if I did put the meta data in the blog post file, I would have to read the content and process the Yaml data inside the comments?
Another way would be put a Yaml section in the top - end it with
---\n. This would be easier to parse, but is not hidden from the Markdown and would therefore have to be removed.
I knew that I had to do this some other way.
I decided to store the page meta data in a separate Yaml file.
Meta data attributes:
In Yaml format - using the slug as key.
hello-world: title: 'Hello World!' section: blog created_at: '2021-07-04'
How well will this perform without a database? Reading and parsing Yaml and Markdown files on the fly? Even when the view files are cached? Probably not great. Probably not that bad either.
Mental note for future bored me: Test static index with 1000 file entries
I wanted to organize the posts into section folders:
posts/ projects/ laravel-markdown-blog.md ... blog/ my-first-post.md ... about/ _index.md
Looking at this, I decided the create an index view file for each folder, to have a place to add additional content for the pages.
posts/ projects/ _index.md laravel-markdown-blog.md ... blog/ _index.md my-first-post.md ... about _index.md
Then I would be able to support slugs like
However, I wanted to blog post slugs to be more simple, so decided to construct my routes to support
/my-first-post even when this post in is the blog folder.
I knew from the start that I wanted to name the files with the slug and only the slug. It's quite common from similar projects to name a content file something like
YYYY-MM-DD-slug-goes-here.md. But why, when the timestamp is already in the index?
Where to put the blog post files?
But blog posts are not Blade views?
Nope, but now they are! 😉
Was considering thephpleague/commonmark and erusev/parsedown, but ended up with graham-campbell/markdown.
This enables you to parse Markdown:
@endmarkdowntag in view files
The Markdown parsing is then automatically executed when running the view helper function:
return view('posts.blog.hello-world', [ // View data ]);
Of course, what else?
Yes. I do have a crush on this CSS framework <3
For handling and retrieving data from the index file.
I will have to overwrite the basic model methods like
Ah, CSS class attributes are - of course - not supported by Markdown (and nope, not going down this Hackey Alley) . Inline HTML in Markdown in possible, but really not what I want.
But Tailwind is a utility-first framework, so what to do?
I ended up adding
@apply styles for the blog post HTML elements, which made it less Tailwind-ish, but still nicer than plain CSS.
So let's call this version 0.1 beta. In future updates I plan to:
And there is more on the wishlist.