Tagging your blog with Jigsaw

Sunday, 09 July 2017

Out of the box Jigsaw doesn't provide anything to allow you to tag your posts, however, as you can see from the bottom of my articles I have implemented a tagging system, and I will show you how I have done this.

It is all made possible by using Jigsaw Collections. Using this feature, any collections you add to your Jigsaw site, will be available as and Illuminate\Collection variable for your blade files, and this is what helps make it all possible

So starting by adding a basic blog collection in your config.php file

return [
  'collections' => [
    'blog' => [
      'path' => 'posts/{date|Y/m/d}/{filename}',
      'author' => 'Alan Holmes',
      'tags' => []
    ],
  ],
];

This means that in any of your pages, you will have access to a $blog variable, which will be a Illuminate\Collection of any pages you add within the _blog folder.

So something like this, will allow you to list your articles on an index page

foreach ($blog as $article)
  <h2><a href="{{ $article->getPath() }}">{{ $article->title }}</a></h2>
  <h3>By {{ $article->author }}</h3>
  {{ $article->getContent() }}
endforeach

(With @ before foreach and endforeach)

You may have noticed the empty tags array in the blog collection, this is to ensure that every article will have the tags array, so that we don't have to account for that later. Then all you need to do is set the tags in the front matter section for the article

extends: _layouts.blog
title: Sample Article
tags: [php, js]
section: content

On your article page you will already have access to the tags array within the $page variable, so you can output the tags for each article:

foreach ($page->tags as $tag)
  <a class="tag"  href="/tag/{{ $tag }}">#{{ $tag }}</a>
endforeach

(With @ before foreach and endforeach)

All this so far as be standard functionality within Jigsaw, now we can move on to creating our /tag/* landing pages, where we will display any articles that have been tagged with the provided tag.

Firstly, to filter the articles by a tag, we will need to add a function to the config.php.

return [
  'collections' => [
    'blog' => [
      'path' => 'posts/{date|Y/m/d}/{filename}',
      'author' => 'Alan Holmes',
      'tags' => []
    ],
  ],

  'getPostsForTag' => function ($page, $articles, $tag) {
      return $articles->filter(function ($article) use ($tag) {
        return collect($article->tags)->contains($tag);
    });
  },
];

The getPostsForTag is then available to call on the $page variable. By default Jigsaw passes $page through as the first parameter, so we only need to pass the $articles and $tag when calling.

So we would call it as such

$page->getPostsForTag($blog, 'php')

This would then return any articles that contain the tag php.

To use this, I have partial for listing blog posts (so both my blog list and filtered list have the same output), so I just override the $blog variable as such.

include('_layouts.blog.list', ['blog' => $page->getPostsForTag($blog, 'php')])

(With @ before include)

Now, for the landing page for each tag, I just create another collection in my config.php and then add a page for each tag in the _tags folder

So my config now looks like this

return [
  'collections' => [
    'blog' => [
      'path' => 'posts/{date|Y/m/d}/{filename}',
      'author' => 'Alan Holmes',
      'tags' => []
    ],

    'tags' => [
      'path' => 'tag/{filename}',
    ],
  ],

  'getPostsForTag' => function ($page, $articles, $tag) {
      return $articles->filter(function ($article) use ($tag) {
        return collect($article->tags)->contains($tag);
    });
  },
];

And the tag pages are all the same, and just contain

---
extends: _layouts.master
title: PHP
---
section('body')

  include('_layouts.blog.list', ['blog' => $page->getPostsForTag($blog, $page->getFilename())])

endsection

(With @ before include, section and endsection)

And that is it, you are now able to tag your articles. The same principle could be used to add categories or any other type of filter you might like.