Dates in blog post URL

I have a page /blog and sub-pages in there. I’d like the path to those sub-pages to be /blog/2020/title-slug.

Is there a way to add 2020 automatically to the URL or do I need to go the virtual page/creating subfolders route?

Thanks, as always, for your help!

1 Like

You can do this with two routes. Ideally, you would also overwrite the url() method in a page model.

Can you give me an example for the routes? I’m fairly new to that aspect of Kirby and would be very thankful.

You basically need two routes, one that picks up the original url and redirects to the new one (just in case) and another one that listens to the new pattern and then tries to find the page in the blog folder:

'routes' => [
    [
      'pattern' => 'blog/(:num)/(:any)',
      'action'  => function ($year, $slug) {
        // find the page with $slug in blog folder and return if can be found
      }
    ],
    [
      'pattern' => 'blog/(:any)',
      'action'  => function ($uid) {
         $page = page('blog')->children()->find($uid);
         if ( $page ) {
            // get the year from the page and redirect to new folder path
            return go('newPath');
         }
      }
    ]
  ]
1 Like

Thank you, that’s super helpful!

Just for others who are searching for a solution to this, so here are my files:

1. Model

In the blog post model (under site/models/blog-post.php – assuming your blog post blueprint/template is blog-post.yml/.php):

class BlogPostPage extends Page {
    public function url($options = null): string {

      // Add date to the returned URL. Note that this is simplified as 
      // it is save to assume that the parent (blog) page exists.

      return $this->url = $this->parent()->url() . '/' . 
             $this->date()->toDate('Y'). '/' . $this->uid();
  }
}

2. Config/Routes

This is basically what @pixelijn has described above, but with the blanks filled for my use case. This is the config.php content:

return [
    'debug' => true,
    'routes' => [
      [
        'pattern' => 'blog/(:num)/(:any)',
        'action'  => function ($year, $slug) {
          // find the page with $slug in blog folder and return if can be found
          return page('blog/'.$slug);
        }
      ],
      [
        'pattern' => 'blog/(:any)',
        'action'  => function ($uid) {
           $page = page('blog')->children()->find($uid);

           if ( $page ) {
              // get the year from the page and redirect to new folder path
              return go('blog/'.$page->date()->toDate('Y').'/'.$page->slug());
           } else {
             // If there is no page set, then go to next. This will make previews work again.
             $this->next();
           }
        }
      ]
    ]
];

Thanks again for setting me on the right path (pun unintended!)

Update 2021-03-23: To make previews work, the second route needed an update (adding $this->next()), and I did change the preview for my blog posts to:

options:
  preview: "{{ page.parent.url }}/{{ page.slug }}"
4 Likes