featured image

Grav - Dynamically populating frontmatter


What is Grav?

If you don't know what is Grav then it's time to check it out! It's a blazing fast modern open source flat-file CMS. I can't recommend it enough if you want to build your next portfolio or any kind of website. If you love to hack with PHP then it's the right choose! (P.S. You are looking at a Grav based website right now!)


Frontmatter

In Grav you can add all kind of data through frontmatter. But in some situations you don't want to input all of them manually. You can use blueprints to define default values but sometimes those values needs to be dynamic.

Example

All of our posts have a header image which is always at the top of the page. This means, when the page is loaded the content is pushed downwards because of the image. To prevent this you need to specify a height for the img tag. You could type the height into an input field every time you update or create a new post... or just save it and let the code do it for you!

The Admin plugin

This plugin is a masterpiece. Though Grav is flat-file based, it would be horrible to manage it through a code editor. Call me lazy, but I think a lot of people will agree with me.

Let's dive into the admin plugin's source code and find the stuff we need to create our plugin!

Event: onAdminSave

What is the fuss about this event? Lucky us, it is fired before saving any kind of data so you can freely manipulate it! In our case we want to observe page save events. Change headers, change content, do whatever you want with that piece of page. Right now we will automagically add the page's header image's size to the frontmatter.

The code

Instead of wasting your time with more words, here is the code.

class HeaderImageSizeInjector extends \Grav\Common\Plugin {
  public static function getSubscribedEvents() {
    return [
      'onAdminSave' => ['addHeaderImageSizeToPostPageHeader', 0],
    ];
  }

  public function addHeaderImageSizeToPostPageHeader($event) {
    $page = $event['object'];

    if ($page instanceof \Grav\Common\Page\Page) {
      // We only want to do this for blog posts
      // The template for our blog posts is called 'item.md'
      if ($page->name() === 'item' . $page->extension()) {
        if (isset($page->header()->header_image_file)) {
          $path = $page->path() . DS . $page->header()->header_image_file;
          if (file_exists($path)) {
            $size = getimagesize($path);
            $page->header()->header_image_width = $size[0];
            $page->header()->header_image_height = $size[1];
          }
        }
      }
    }
  }
}

Code analysis

Nothing complex. It subscribes for the appropriate event and then we determine if the user is saving a page, more specifically a blog post page. When it is sure about that it determines the path of the header image and retrieves its image size and saves it to the page's header. Simple, right?

With a mere 30 lines we can solve an annoying repeating task, isn't that cool?

What now?

Now you can simply use the variables you just injected into your posts' header.

---
title: Example Post
process:
    markdown: true
    twig: true
header_image_file: header.jpg
---

{% set header_image = page.media.images[header_image_file] %}
<img src="{{ header_image.url }}" 
     width="{{ page.header.header_image_width }}" 
     height="{{ page.header.header_image_height }}" />

NOTE

This is a bad use case for injecting header variables. The image media files already contains the width and height as property.


Enjoying this post? Check out my other posts too! Feel free to comment below!