This blog runs on Sculpin, a static site generator. The generator itself runs on Symfony, which for me makes it easy to extend. However, I find that if you want something special, it can usually be done, but it may take several hours to get it right. In the end though, the solution is often quite elegant.
A custom content type for events
One custom feature I wanted for this website was a list of events (conference talks, trainings, etc.). Sculpin’s documentation suggests using a custom content type for that. This allows you to create a directory with files, each of which will be considered an “event”.
Setting up a custom content type is usually quite easy:
# in app/config/sculpin_kernel.yml sculpin_content_types: events: permalink: event/:year/:month/:slug_title
The default configuration values that Sculpin calculates for this setup are good in this case (it will look in source/_events/
for the html or Markdown files describing the events; it will look for the _layouts/event.html
file for the layout of each event’s page, etc.). However, I wasn’t interested in how “detail” pages were generated for each event; I just wanted a list of all events. The html for the events page would have to look something like this:
---
layout: default
title: Events
use: [events] # load the collection of "events"
--- <h2>Events</h2> {% for event in data.events %} {{ event.content|raw }}
{% endfor %}
A sample event file would look something like this (front matter first, allowing some meta-data to be provided, then the content of the page):
# in source/_events/php-benelux-2020-workshop-decoupling-from-infrastructure.html ---
title: Decoupling from infrastructure
date: 2020-01-24
--- <p>Most application code freely mixes domain logic [...]</p>
This, again, just works great out-of-the-box.
Custom sorting
But now I wanted to change the sort order for the events. The default sorting is on date, descending. This doesn’t feel like a natural ordering for upcoming events, which would show events far in the future first.
I didn’t find a way to configure the sorting of events in app/config/sculpin_kernel.yml
, so I looked at the source code of the SculpinContentTypesExtension
class. I found out that the easiest thing to manually configure sorting would be to override the sorter service that Sculpin automatically defines for every content type:
services: # This sorter overrides the one Sculpin automatically configures for "events" sculpin_content_types.types.events.collection.sorter: class: Sculpin\Contrib\ProxySourceCollection\Sorter\MetaSorter arguments: - 'date' - 'desc' # I don't know why but "desc" works (I expected "asc")
The MetaSorter
ships with Sculpin. I figured out the name of the service by reading through the code in SculpinContentTypesExtension
. As you can see, I provide 'desc'
as the second argument, even though I would expect the correct value to be 'asc'
; 'desc'
had the desired effect of showing the earliest events first.
Creating two filtered collections: upcoming and past events
I then realised it would be useful to have a list of upcoming events, sorted by date, ascending, and a list of past events, sorted by date, descending (oldest last). Upon regenerating the site, past events should automatically move to the list of past events. I figured I’d have to define two content types now: upcoming_events
and past_events
. These collections should both load the files in source/_events/
, so the resulting configuration looks like this:
# in app/config/sculpin_kernel.yml sculpin_content_types: upcoming_events: permalink: event/:year/:month/:slug_title path: event/ layout: event past_events: permalink: event/:year/:month/:slug_title path: event/ # same path as "upcoming_events" layout: event
Which events end up in which collection should be determined by some kind of filter based on the current timestamp. Again, it turned out that Sculpin already has a built-in type for defining filters (FilterInterface
), but it doesn’t provide easy ways of setting it up for your custom content types.
The way I did it was write a compiler pass that modified Sculpin’s own f
Truncated by Planet PHP, read more at the original (another 4420 bytes)