Piecing it together with Jigsaw

When I wanted to start this blog, I knew I wanted to use GitHub Pages to host it. Therefor, I started by looking at the tools that were available in order to build a static site/blog, and I immediately found Jekyll.

Now, don't get me wrong, Jekyll is awesome. And I've been in love with Ruby ever since I first saw the language written. But since I work on a daily basis with PHP (Magento & Laravel), I always wanted my blog to be built using PHP. I tried some of the other options out there, like Sculpin, but never felt quite good with them, so the blog stayed with Jekyll.

Enter Jigsaw

A few days ago, Jigsaw v1 was released, and it included a major new functionality: Collections. That was enough for me to install it, and take it for a spin. In less than an hour I had already decided to switch from Jekyll to Jigsaw. Not because it is inherently better or faster —I don't know and I don't care— but because it works just as well, and it feels like home because its written in PHP.

Mind you, being just as good as Jekyll is saying a lot.

What I'd really like to see, is a way to use the collections directly inside our config.php. That way, we could avoid having to pass the collection as a parameter to the helper functions (as you'll see on the examples below).

Also, I'd love maybe some event system of sorts to hook into the build process, that could allow for easier plugin building.

So, here I want to share a few bits of how I solved some things for my blog's meager (but rather common) functionality.

Categories

I wanted for my posts to use categories like tags, so: a post can have many categories.

And I wanted to have a list of all the categories, with the post count, on the sidebar. Each of these, should show a page with all the related posts.

Here is the front matter for a post, YAML accepts an array without any hassle:


---
extends: _layouts.post
section: content
title:  "Jigsaw"
date:   2017-04-26 15:56:19 -0300
categories: [jigsaw, php]
comments: true
---

Then, on mi sidebar, I iterate over this little function to get the list of categories:


// config.php
return [
    // ...
    'allCategories' => function ($page, $posts) {
        return $posts->pluck('categories')->flatten()->unique();
    },
    // ...
]

And to get each category's post count:


// config.php
return [
    // ...
    'countPostsInCategory' => function ($page, $posts, $category) {
        return $posts->reduce(function ($carry, $post) use ($category) {
            return $carry + collect($post->categories)->contains($category);
        });
    },
    // ...
]

Which you then use in your sidebar like so:


@foreach ($page->allCategories($posts) as $category)
    <a href="/categories/{{ $category }}">
        {{ $category }}
        <small>({{ $page->countPostsInCategory($posts, $category) }})</small>
    </a>
@endforeach

Now, the Jigsaw docs show an example of this, but only supporting a single category per post. Following the same principle and file structure, we can use this:


// config.php
return [
    // ...
    'getPostsWithCategory' => function ($page, $posts, $category) {
        return $posts->filter(function ($post) use ($category) {
            return collect($post->categories)->contains($category);
        });
    },
    // ...
]

Which you would then use like so:


<!-- _layouts/category.blade.php -->
@foreach ($page->getPostsWithCategory($posts, $page->getFilename()) as $post)
    <li>
        <h3>
            <a href="{{ $post->getUrl() }}">
                {{ $post->title }}
                <small>{{ $post->prettyDate() }}</small>
            </a>
        </h3>
    </li>
@endforeach

Dates

I fell in love with Carbon when I saw it used by Laravel. So I couldn't not use it here. Did you catch that $post->prettyDate() method call above? Here it is:


// config.php
return [
    // ...
    'collections' => [
        'posts' => [
            // ...
            'prettyDate' => function ($page) {
                return Carbon\Carbon::parse($page->date)->toFormattedDateString();
            },
            // ...
        ],
    ],
    // ...
]

You'll need to install Carbon first composer require nesbot/carbon

Pagination

Update!

2017-04-28

The fine folks at Tighten merged my PR, and the pagination bug is fixed. Pagination now works as expected everywhere: Yaay!

I'd like to give a shout-out to Keith, who was kind enough to reach out to me via Twitter. Thanks!

I'm leaving this here for historic purposes, and because I'm rather proud of it for making it into master

Here I found a small bug (I think) since I am trying to paginate the posts on my home page (index.blade.php). If you paginate on the home, when you visit the second page, the $paginate->previous call returns null. So you never get a proper link to the first page, nor the previous.

Here is the link to the PR with a fix (please do comment on that!)

In the mean time, if you want to paginate on the home page, you can do something like this:


<!-- index.blade.php -->

@if ($pagination->currentPage === 2) <!-- this is the fugly hack I had to use -->
    <a href="{{ $page->baseUrl }}">first</a>
    <a href="{{ $page->baseUrl }}">previous</a>
@else
    @if ($previous = $pagination->previous)
        <a href="{{ $page->baseUrl }}{{ $pagination->first }}">first</a>
        <a href="{{ $page->baseUrl }}{{ $previous }}">previous</a>
    @else
        <span>first</span>
        <span>previous</span>
    @endif
@endif

@foreach ($pagination->pages as $pageNumber => $path)
    @if ($pagination->currentPage == $pageNumber)
        <span>{{ $pageNumber }}</span>
    @else
        <a href="{{ $page->baseUrl }}{{ $path }}">{{ $pageNumber }}</a>
    @endif
@endforeach

@if ($next = $pagination->next)
    <a href="{{ $page->baseUrl }}{{ $next }}">next</a>
    <a href="{{ $page->baseUrl }}{{ $pagination->last }}">last</a>
@else
    <span>next</span>
    <span>last</span>
@endif


Anyways, that is my take on Jisgaw. I hope you like it too!

Have fun!