How to override component partials with October CMS
Within October CMS it is easy to override partial, page or layout twig files that are within a component of a plugin. In this article I will show you how to do that and also show the tricky parts and how to avoid them.
Services plugin
We assume a plugin with a component that presents cards with services (see also the frontpage of this website).
In the sample above we have 3 blocks with services. In the backend this looks like this:
With corresponding:
<div class="col-12 col-sm-4 pl-2 pr-2">
{% component 'apps' %}
</div>
<div class="col-12 col-sm-4 pl-2 pr-2">
{% component 'frameworks' %}
</div>
<div class="col-12 col-sm-4 pl-2 pr-2">
{% component 'plugins' %}
</div>
All three have the same base component Service Single
, but all have an unique alias ('plugins', 'frameworks', 'apps').
Definition of component within the plugin
Let's assume that the plugin can be found in plugins\enovision\services
and that the component contents are:
components\Single.php
components\single\default.htm
In the plugin the component is defined as following:
public function registerComponents()
{
return [
'Enovision\Services\Components\Single' => 'serviceSingle'
];
}
This means as much that the default unique alias for this component is: serviceSingle
and that it relates to the Single.php
component class.
The override (when only one unique alias)
First let's start easy. Assume you have defined a single instance of a component with an alias of 'serviceSingle' in our partial, page or layout.
Original (in the plugins folder under \components\single\default.htm)
{% set service = __SELF__.service %}
<div class="card text-center super-charger">
<div id="block-card-apps" class="card-block p-3">
<div class="icon sencha-color-dark pb-3">
<i class="{{ service.icon }} fa-3x"></i>
</div>
<h3 class="card-title">{{ service.title }}</h3>
<p class="card-text">{{ service.slogan }}</p>
<a href="{{ service.url|staticPage }}{% if service.anchor|length %}#{{ service.anchor }}{% endif %}" title="Read more" class="read-more">
{{ __SELF__.lang.read_more }}
<i class="fas fa-angle-double-right ml-2"></i>
</a>
</div>
</div>
Required override (in the folder \themes\\partials\serviceSinge\default.htm)
{% set service = _service.service %}
{% set pattern = _service.pattern|default(false) %}
<div class="card text-center {% if pattern %}card-pattern{% endif %}">
<div id="block-card-apps" class="card-block p-3">
<div class="icon sencha-color-dark pb-3">
<i class="{{ service.icon }} fa-3x"></i>
</div>
<h3 class="card-title">{{ service.title }}</h3>
<p class="card-text">{{ service.slogan }}</p>
<a href="{{ service.url|staticPage }}{% if service.anchor|length %}#{{ service.anchor }}{% endif %}" title="Read more" class="read-more">
{{ _service.lang.read_more }}
<i class="fas fa-angle-double-right ml-2"></i>
</a>
</div>
</div>
They are almost the same, only the original has a class 'supercharger' and we need a conditional class 'card-pattern'.
Now the tricky part
In our situation we have the same component 3 times in the same twig file and since a component must have an unique identifier (alias) within a partial, page or layout, we end up with 3 different aliases for 3 presentations of the same component.
[serviceSingle plugins]
key = "plugin"
pattern = 1
[serviceSingle frameworks]
key = "frameworks"
[serviceSingle apps]
key = "applications"
If we don't do anything and have just the \themes\<yourtheme>\partials\serviceSinge\default.htm
it will not do any override. The override is only done on the actual alias used (apps, plugins, frameworks).
That's another fine mess you have gotten me into, how to make that work?
Keep the files as described earlier and add for every alias the following file named \themes\<yourtheme>\partials\plugins\default.htm
:
(do this for frameworks and apps as well)
{% partial 'serviceSingle/default.htm' %}
Now that becomes a real mess, doesn't it?
This is the better way to avoid trouble
Make a small adjustment in the \themes\<yourtheme>\partials\serviceSinge\default.htm
file:
{% set service = __SELF__.service %}
becomes:
{% set service = _service.service %}
The layout where the component was present 3 times we change to:
<div class="col-12 col-sm-4 pl-2 pr-2">
{% partial 'serviceSingle/default.htm' _service=apps %}
</div>
<div class="col-12 col-sm-4 pl-2 pr-2">
{% partial 'serviceSingle/default.htm' _service=frameworks %}
</div>
<div class="col-12 col-sm-4 pl-2 pr-2">
{% partial 'serviceSingle/default.htm' _service=plugins %}
</div>
As you can see we have removed the component reference, although the components are still added to this page as it was before. What we do instead is passing the result as a variable to a partial.