Reputation: 307
I'm having some difficulties validating a I18N field in CakePHP3.
The translate behavior is setup like this:
$this->addBehavior('Translate', [
'fields' => ['name', 'body', 'slug'],
'validator' => 'default'
]);
Like advertised here: https://book.cakephp.org/3.0/en/orm/behaviors/translate.html#validating-translated-entities
The core validation is working properly. I have a validation rule in validationDefault function that checks if name is not empty and it works fine. Though, I also would like to add some application rules on top of this validation. The "name" field should have a unique value. I don't want to allow multiple entities with the same translated name.
This piece of code apparently doesn't work. CakePHP docs also are quite silent about this matter.
public function buildRules(RulesChecker $rules) {
// prevent duplicate creation
$rules->add($rules->isUnique(['name']));
return $rules;
}
Is this actually possible? Thanks
Upvotes: 1
Views: 566
Reputation: 60483
What you are doing there is creating a rule for the name
field on the main model, this won't affect translations. There is no built-in functionality for that, the behavior only assists with validation rules by making use of the validationTranslated()
method in case it exists on your model class, it won't help with application rules.
You'd have to create a custom application rule that checks the translation table, by matching against the field
, locale
, model
and content
fields, something along the lines of this:
$rules->add(
function (EntityInterface $entity) {
$behavior = $this->behaviors()->get('Translate');
$association = $this->association($behavior->getConfig('translationTable'));
$result = true;
foreach ($entity->get('_translations') as $locale => $translation) {
$conditions = [
$association->aliasField('field') => 'name',
$association->aliasField('locale') => $locale,
$association->aliasField('content') => $translation->get('name')
];
if ($association->exists($conditions)) {
$translation->setErrors([
'name' => [
'uniqueTranslation' => __d('cake', 'This value is already in use')
]
]);
$result = false;
}
}
return $result;
}
);
Note that this uses the association object rather then the target table, this will ensure that further conditions like the model name are being applied automatically.
Also this requires to set the errors on the entity manually, as they are nested, which isn't supported by the rules checker, currently it can only set errors on the first level entity (see the errorField
option).
It should also be noted that it would be possible to modify the rules checker for the translation table association (via the Model.buildRules
event), however this would result in the errors being set on new entities that will be put in a separate property (_i18n
by default) on the main entity, where the form helper won't find the error, so one would then have to read the error manually, which is a little annoying.
See also
Upvotes: 2