Skip to main content
Version: Next

Custom Rule Definition

Add a custom rule definition in two steps:

  1. Create a PHP class for the rule definition.
  2. Register the class as a service.

Create a Custom Rule Definition PHP Class

The following example adds a Sample Check rule definition. A rule definition class extends AbstractRuleDefinition:

<?php
declare(strict_types=1);

namespace Foo\Bar;

use Pimcore\Bundle\DataQualityManagementBundle\Installer;
use Pimcore\Bundle\DataQualityManagementBundle\Model\RuleDefinition\AbstractRuleDefinition;
use Pimcore\Bundle\DataQualityManagementBundle\Model\ValidatorResponse;
use Pimcore\Model\DataObject;
use Pimcore\Model\DataObject\Concrete;
use Symfony\Contracts\Translation\TranslatorInterface;

class SampleCheck extends AbstractRuleDefinition
{
protected const FIELD_TYPE = 'sampleCheck';

public function __construct(
private readonly TranslatorInterface $translator
) {
parent::__construct($this->translator);
}

public function getType(): string
{
return self::FIELD_TYPE;
}

public function supports(string $type): bool
{
return $type === $this->getType();
}

public function getSuggestion(
array $rule, //the rule definition
string $locale,
Concrete $object, //the object the score was calculated for
array $ruleData //the data stored in the Data Object which are related to the current rule
): ?string
{
//return a string suggestion to display in the UI:
if (!isset($rule['yourSuggestionField'])) {
return null;
}

return $this->translator->trans(
$rule['yourSuggestionField'],
[],
Installer::TRANSLATION_DOMAIN,
$locale
);
}

public function forceRecalculation(): bool
{
return false;
//if true:
//the rule definition is recalculated for every saved Data Object (and its children)
//on every class definition save, even when the data has not changed.
//else:
//the rule definition is recalculated for every saved Data Object (and its children)
//only when its configuration changed (the same behavior as the Recommended Fields check).
}

public function calculateScore(DataObject\Concrete $dataObject, array $ruleDefinition): ValidatorResponse
{
//your logic here...
return new ValidatorResponse(
true, //true if the rule definition is valid, false otherwise
[], //array of invalid fields
[] //array of additional data (to be used for example in the suggestion)
);
}
}
info

The FIELD_TYPE constant is the value stored in the class definition's definitions array as type; see Configuring the rule in a class definition.

Building the suggestion from rule data

$ruleData holds the stored result of the last calculateScore() run for this rule, with the keys valid, invalidFields and messageProperties taken from the returned ValidatorResponse. Use it to append details to the translated message instead of returning the translation as is:

public function getSuggestion(
array $rule,
string $locale,
Concrete $object,
array $ruleData
): ?string
{
$message = $this->translator->trans(
'plugin_pimcore_dataqualitymanagement_fixed_suggestion',
[],
Installer::TRANSLATION_DOMAIN,
$locale
);

if (!empty($ruleData['invalidFields'])) {
// one invalid field per line, the Studio data quality tab preserves the line breaks
$message .= "\n" . implode("\n", $ruleData['invalidFields']);
}

return $message;
}

AbstractRuleDefinition already implements this behavior: its default getSuggestion() returns null when invalidFields is empty, and otherwise appends the invalid field names, one per line, to the plugin_pimcore_dataqualitymanagement_fixed_suggestion translation. Override the method only when the suggestion needs different wording or different data.

Register the New Class as a Service

Register the class as a service and tag it as pimcore.data_quality_management.rule_definition:

    ...

Foo\Bar\SampleCheck:
tags:
- { name: pimcore.data_quality_management.rule_definition }

...

Once tagged, the rule definition participates in score calculation like any built-in rule: it is available to bin/console dqm:score:recalculate, honors forceRecalculation(), and its getSuggestion() output appears in the Data Object editor's Data Quality Details tab.

Configuring the Rule in a Class Definition

Pimcore Studio's class editor currently ships a configuration form for the three built-in rule types only (Object Validation Check, Recommended Fields Check, Symfony Expression Check). There is no supported extension point yet for adding a class editor form for a custom rule type.

A custom rule definition still works: add its configuration directly to the Data Quality field's definitions array in the class definition, either by editing the class definition PHP export or by updating it programmatically. Each entry is a plain array read by key:

  • type - resolves the rule definition via the service tagged with a matching getType() (FIELD_TYPE above).
  • weight - validated as an integer between 1 and 100 for every entry, regardless of the other keys.
  • fieldsToCheck - a required array of key names, from this same entry, that must be present and non-empty for the class definition to save. The validator iterates it directly, so provide it even when it is empty. The built-in rules set it to ['title', 'weight'] plus their own fields.
  • fieldsToTranslate (optional) - an array of key names whose values are registered as translation keys in the dataQualityManagement domain when the class definition is saved.
  • any additional keys the custom calculateScore() and getSuggestion() implementation expects (yourSuggestionField in the example above).
'definitions' => [
[
'type' => 'sampleCheck',
'title' => 'Sample Check',
'weight' => 1,
'fieldsToCheck' => ['title', 'weight'],
'fieldsToTranslate' => ['title'],
'yourSuggestionField' => 'plugin_pimcore_dataqualitymanagement_custom_suggestion',
],
// ...other rule definitions
],
caution

weight is validated for every rule definition, built-in or custom, and must be an integer between 1 and 100. title (and any other field) is enforced as mandatory only when it is listed in that entry's own fieldsToCheck.