Skip to main content
Version: Next

Precondition Filter

Precondition filters are used to filter the list of available element when a scheduled task is executed. This is useful if you want to limit the list of elements that should be processed by a scheduled task.

Implementing a Precondition Filter

To implement your own precondition filter you must extend AbstractPreconditionFilter. The abstract class implements the PreconditionFilterInterface and specifies an abstract method configureCondition(). You have to implement all the interface methods and the abstract method of the abstract class.

Example:

<?php
declare(strict_types=1);

namespace App\Services\PreconditionFilter;

use Pimcore\Bundle\CopilotBundl\AutomationAction\PreconditionFilter\AbstractPreconditionFilter;
use Pimcore\Bundle\CopilotBundl\AutomationAction\PreconditionFilter\ConditionObject;

class MyCustomFilter extends AbstractPreconditionFilter
{
// the name is used to display it in the config
public function getName(): string
{
return 'My Custom Filter';
}

// if you want the condition only to apply for a specific type
public function isValidForType(string $type): bool
{
return $type === 'object'; // or 'asset' or 'document'
}

// you must create a new ConditionObject here with first query and params as input params
// you have to either use placeholder ? or named params like in the example below:
protected function configureCondition(): void
{
$this->conditionObject = new ConditionObject('id = :id', ['id' => 42]);
}
}

Register a Precondition Filter

Every precondition filter must be registered with the pimcore.copilot.automation_action.precondition_filter tag, e.g. in services.yaml. Registering a class without extending the AbstractPreconditionFilter will result in an exception.

App\Services\PreconditionFilter\MyCustomFilter:
tags: ['pimcore.copilot.automation_action.precondition_filter']

Usage Examples for More Complex Conditions

In case you need a more complex query for your precondition, you can always inject other services into the precondition filter.

The following examples show a variety of query builder usages:

V8 Engines Only

You can use the query builder here to execute a query to get only object ids for which the car has a V8 engine. The result is then used in the condition like the following: id IN (17, 42, 73).

<?php
declare(strict_types=1);

namespace App\Services\PreconditionFilter;

use Doctrine\DBAL\Exception;
use Pimcore\Bundle\CopilotBundl\AutomationAction\PreconditionFilter\AbstractPreconditionFilter;
use Pimcore\Bundle\CopilotBundl\AutomationAction\PreconditionFilter\ConditionObject;
use Pimcore\Bundle\StaticResolverBundle\Db\DbResolverInterface;

class V8 extends AbstractPreconditionFilter
{
public function __construct(private readonly DbResolverInterface $dbResolver)
{
parent::__construct();
}

/**
* @throws Exception
*/
protected function configureCondition(): void
{
$builder = $this->dbResolver->getConnection()->createQueryBuilder();

$builder
->select('id')
->from('object_brick_query_Engine_CAR')
->where('cylinders = :v8')
->setParameter('v8', 8);

// execute the query builder and use the ids for a sub select
$ids = array_map(static fn($value) => $value, [...$builder->executeQuery()->iterateColumn()]);

$this->conditionObject = new ConditionObject(
'id IN (:ids)',
['ids' => $ids]
);
}

public function getName(): string
{
return 'V8 Engines';
}

public function isValidForType(string $type): bool
{
return $type === 'object';
}
}

Reworked Cars and Soon Available

If you do not want to execute a query beforehand, you can also use the query builder to get a SQL sub-query. In this case, you can add the parameters to the ConditionObject params. The sub-query is then executed when the listing is loaded.

<?php
declare(strict_types=1);

namespace App\Services\PreconditionFilter;

use Pimcore\Bundle\CopilotBundl\AutomationAction\PreconditionFilter\AbstractPreconditionFilter;
use Pimcore\Bundle\CopilotBundl\AutomationAction\PreconditionFilter\ConditionObject;
use Pimcore\Bundle\StaticResolverBundle\Db\DbResolverInterface;

class Reworked extends AbstractPreconditionFilter
{

public function __construct(private readonly DbResolverInterface $dbResolver)
{
parent::__construct();
}

protected function configureCondition(): void
{
$builder = $this->dbResolver->getConnection()->createQueryBuilder();

$builder
->select('id')
->from('object_brick_query_SaleInformation_CAR')
->where('`condition` = :condition AND (`availabilityType` = :availabilityTypeDays OR `availabilityType` = :availabilityTypeWeeks)');

// build sql with named parameters and inject named parameters into the condition object parameters
$this->conditionObject = new ConditionObject(
"id IN ({$builder->getSql()})",
[
'condition' => 'reworked',
'availabilityTypeDays' => 'couple-days',
'availabilityTypeWeeks' => 'couple-weeks'
]
);
}

public function getName(): string
{
return 'Reworked available in a couple of days/weeks';
}

public function isValidForType(string $type): bool
{
return $type === 'object';
}
}

(Not) Null Check

If you want to check if a field is (not) null, you can use the following example:

<?php
declare(strict_types=1);

namespace App\Services\PreconditionFilter;

use Pimcore\Bundle\CopilotBundl\AutomationAction\PreconditionFilter\AbstractPreconditionFilter;
use Pimcore\Bundle\CopilotBundl\AutomationAction\PreconditionFilter\ConditionObject;

final class NotNullCheck extends AbstractPreconditionFilter
{

public function __construct()
{
parent::__construct();
}

protected function configureCondition(): void
{
$this->conditionObject = new ConditionObject(
"title IS NOT NULL",
['title' => ''],
);
}

public function getName(): string
{
return 'Example implementation for a not null check.';
}

public function isValidForType(string $type): bool
{
return $type === 'object';
}
}