How to override component properties in OctoberCMS


In the journey to create an editable website for a client, I came across the need to override a component property in OctoberCMS. After searching for this issue on the web for a long time, I couldn’t find an easy solution for this, so I decided to write this post to help my OctoberCMS bro’s out.

Why overriding a component property?

First off, let’s start with the why. Why would you have the need to override a component property? Couldn’t you just make a new CMS page, invoke the component there with a different property and be done with it? Like so?

title = "Running"
url = "/sports/running"
layout = "default"
is_hidden = 0
child_of = "sports"
hide_crumb = 0
remove_crumb_trail = 0
crumb_disabled = 0

[blogPosts]
pageNumber = "{{ :page }}"
categoryFilter = "running"
postsPerPage = 3
noPostsMessage = "No posts found"
sortOrder = "published_at desc"
categoryPage = "nieuws/category"
postPage = "nieuws/post"

==

{% set blogPosts = __SELF__.blogPosts %}

<div class="container-fluid px-md-5 my-5">

<div class="row row-eq-height">
    {% for post in posts %}
    {% set image = post.featured_images[0] %}
    <a href="{{ post.url }}">
        <div>
            <p class="post-text-overlay">{{ post.title }}</p>
        </div>
    </a>
    {% endfor %}
</div>

</div>
title = "Soccer"
url = "/sports/soccer"
layout = "default"
is_hidden = 0
child_of = "sports"
hide_crumb = 0
remove_crumb_trail = 0
crumb_disabled = 0

[blogPosts]
pageNumber = "{{ :page }}"
categoryFilter = "soccer"
postsPerPage = 3
noPostsMessage = "No posts found"
sortOrder = "published_at desc"
categoryPage = "nieuws/category"
postPage = "nieuws/post"

==

{% set blogPosts = __SELF__.blogPosts %}

<div class="container-fluid px-md-5 my-5">

<div class="row row-eq-height">
    {% for post in posts %}
    {% set image = post.featured_images[0] %}
    <a href="{{ post.url }}">
        <div>
            <p class="post-text-overlay">{{ post.title }}</p>
        </div>
    </a>
    {% endfor %}
</div>

</div>

Yes this would work. But, consider this, if you need to make changes to either page, you are in for a lot of trouble. You are duplicating your code to change a few lines to it in either place. This works for small websites, but is not maintainable for larger ones AND is just a lot of work to make changes to it either way.

My next solution was the following, I extracted the blogPost (or any other component) loop to a partial. This would make our code DRY (Do not Repeat Yourself) so this would be better.

title = "Running"
url = "/sports/running"
layout = "default"
is_hidden = 0
child_of = "sports"
hide_crumb = 0
remove_crumb_trail = 0
crumb_disabled = 0

[blogPosts]
pageNumber = "{{ :page }}"
categoryFilter = "running"
postsPerPage = 3
noPostsMessage = "No posts found"
sortOrder = "published_at desc"
categoryPage = "nieuws/category"
postPage = "nieuws/post"

==

{% partial "misc/relatedposts" blogPosts=blogPosts.posts %}

Indeed, this is slightly better. We have extracted our code so we don’t have to copy paste it anymore. But let’s not stop there! I would like to make sure my Client can add this partial himself, choose which blogPosts to show without touching any code. How would we do this?

In short:

  • You want to keep your code as DRY as possible by elevating OctoberCMS’s partials, snippets, …
  • You want to make your component editable for your client

Other approaches

Override a component property from the php code section.

You can access all components on the current page by using

$this->page->components;

You can use this in CMS pages, but because the way partials are processed in OctoberCMS, this doesn’t work.

[blogPosts]
pageNumber = "{{ :page }}"
postsPerPage = 3
noPostsMessage = "No posts found"
sortOrder = "published_at desc"
categoryPage = "nieuws/category"
postPage = "nieuws/post"
==
function onStart()
{
    // Override the previous setting of 9 products per page.
    $this->page->components['blogPosts']->setProperty('categoryFilter', running);
}
==

{% partial "misc/relatedposts" blogPosts=blogPosts.posts %}

The solution

For maximum extensibility and user experience I would do the following:

  1. Create a static page instead of CMS pages
  2. Create a snippet out of our partial
  3. Use the snippet with a dropdown (or other input element) so the client can choose which category of blogposts to show
  4. Pass the chosen variable to the snippet

This is where I became stuck (and the reason for this blogpost). I knew what I wanted to accomplish, but I had to dig deep into the source code of OctoberCMS to find out how partials / snippets / CMS pages / static pages were parsed.

If you just started to use OctoberCMS you know you can use the configuration section to define variables (or the PHP section), but how do they differ from each other? What works / doesn’t work where.

Note: How I use the block system to build up pages, is for another post.

Create a static page instead of a CMS page

This is step one of what we want. An editable page for our client. The Static pages plugin is a good start for this. We create a new static page through the backend of OctoberCMS.

For me, this results in a new file with the following code. (This depends on the title you gave your page):

[viewBag]
title = "Lopen"
url = "/sport/lopen"
layout = "static"
is_hidden = 0
==

Create a partial

I created a new partial file I can add to my static page.

{% partial "misc/relatedposts" filter=data.section_rlc_category %}

Use the partial with a dropdown (or other input element) so the client can choose which category of blogPosts to show.

Pass the chosen variable to the snippet

[blogPosts]
pageNumber = "{{ :page }}"
categoryFilter = "{{ filter }}"
postsPerPage = 3
noPostsMessage = "No posts found"
sortOrder = "published_at desc"
categoryPage = "nieuws/category"
postPage = "nieuws/post"
==

{% set blogPosts = __SELF__.blogPosts %}

// BLOGPOST CODE

This is the result:

Editable variable for the user.

So that’s how you can override component properties in OctoberCMS!