Creating custom condition types¶
Netgen Layouts is shipped with a couple of condition types which you can use to limit your layout mappings to certain conditions. One example condition type which is built into Netgen Layouts is the query parameter condition type which enables the mapping if some query parameter from the request matches the value stored in the condition. In case of Ibexa CMS integration, an example condition type which is built in is the siteaccess condition type that activates the mapping only if the siteaccess of the current request matches the condition.
If you wish to map your layouts to targets in some other conditions, like time of day, location of the user, IP address, you name it…, you can create your own condition types.
Warning
Custom condition types are one of the most complicated extension points in Netgen Layouts and creating a custom condition type involves creating configuration, templates, translations and quite a bit of code.
Implementing the condition type classes¶
Implementing a condition type in PHP requires creating two Symfony services:
Condition type itself
Symfony form mapper
Creating a condition type¶
A condition type is a PHP class implementing
Netgen\Layouts\Layout\Resolver\ConditionTypeInterface
(or extending
Netgen\Layouts\Layout\Resolver\ConditionType
abstract class to cut down on
boilerplate). This class will have a couple of purposes related to a condition
type:
Provide Symfony constraints that validate the value of the condition when adding it to a mapping
Deciding if the current request matches the provided value of the condition
Making sure that value of a condition is correctly preserved across systems (e.g. dev and prod) when exporting/importing mappings.
The first point is achieved by implementing getConstraints
method, which
should return the array of Symfony validator constraints which should validate
the value. For example, in Ibexa siteaccess condition type, these constraints
validate that all selected siteaccesses are non empty strings and that they
actually exist:
public function getConstraints(): array
{
return [
new Constraints\NotBlank(),
new Constraints\Type(['type' => 'array']),
new Constraints\All(
[
'constraints' => [
new Constraints\Type(['type' => 'string']),
new IbexaConstraints\SiteAccess(),
],
],
),
];
}
The second point is achieved by implementing the matches
method. This method
takes a request object and based on the data from the request decides if it
matches the provided value. For example, the matches
method of the
Ibexa siteaccess condition type returns true only if the siteaccess is provided
in the request and is equal to one of the stored values of the condition:
public function matches(Request $request, $value): bool
{
$siteAccess = $request->attributes->get('siteaccess');
if (!$siteAccess instanceof SiteAccess) {
return false;
}
if (!is_array($value) || count($value) === 0) {
return false;
}
return in_array($siteAccess->name, $value, true);
}
The third point is achieved by implementing export
and import
methods.
E.g. in order for your conditions to be correctly recognized in different
systems, you would want to use some form of a remote IDs when exporting them,
instead of using autogenerated database IDs. Example export
and import
methods would then look like this:
public function export($value)
{
return $this->loadById($value)->remoteId;
}
public function import($value)
{
return $this->loadByRemoteId($value)->id;
}
The one method that remains to be implemented is the getType
method, which
should return a unique identifier of the condition type.
Once this is done, we need to register the condition type in the Symfony DIC
with the netgen_layouts.condition_type
tag:
app.condition_type.my_condition:
class: App\Layout\Resolver\ConditionType\MyCondition
tags:
- { name: netgen_layouts.condition_type }
Creating the form mapper¶
To be able to add the condition to a mapping or edit the value of an existing
condition, you need to provide a form mapper which provides data for generating
Symfony form for your condition type. The mapper needs to implement
Netgen\Layouts\Layout\Resolver\Form\ConditionType\MapperInterface
and
there’s also a handy abstract class which you can extend to cut down the number
of methods to define to one: getFormType
, which returns which Symfony form
type should be used to edit the condition:
<?php
declare(strict_types=1);
namespace App\Layout\Resolver\Form\ConditionType\Mapper;
use Netgen\Layouts\Layout\Resolver\Form\ConditionType\Mapper;
use Symfony\Component\Form\Extension\Core\Type\TextType;
final class MyCondition extends Mapper
{
public function getFormType(): string
{
return TextType::class;
}
}
There are two other methods in the interface:
getFormOptions
which makes it possible to provide custom options to the form typehandleForm
which allows you to customize the form in any way you see fit
Finally, you need to register the mapper in the Symfony container with the correct tag and the identifier of the condition type:
app.layout.resolver.form.condition_type.mapper.my_condition:
class: App\Layout\Resolver\Form\ConditionType\Mapper\MyCondition
tags:
- { name: netgen_layouts.condition_type.form_mapper, condition_type: my_condition }
Implementing the condition type template¶
Condition type uses a single template in the value
view context of the
Netgen Layouts view layer to display the value of the condition in the admin
interface. Since the condition itself usually provides only the scalar
identifier as its value, this template usually needs some logic to display the
human readable value of the condition. For example, content type condition from
Ibexa CMS uses custom Twig functions to display content type names instead of
the identifiers:
{% set content_type_names = [] %}
{% for value in condition.value %}
{% set content_type_names = content_type_names|merge([nglayouts_ibexa_content_type_name(value)]) %}
{% endfor %}
{{ content_type_names|join(', ') }}
To register the template in the system, the following configuration is needed
(make sure to use the value
view context):
netgen_layouts:
view:
rule_condition_view:
value:
my_condition:
template: "@App/layout_resolver/condition/value/my_condition.html.twig"
match:
rule_condition\type: my_condition
Condition type translations¶
Each condition type uses one translation string in the nglayouts
catalog. This is
a generic string which should provide a human readable name of the condition
type and should be in the
layout_resolver.condition.<condition_type_identifier>
format: