Problem/Motivation
In order to add validation constraints to all user role configuration
📌
Add validation constraints to user.role.*
Needs review
, it was necessary to add a new public static function (getAllValidPermissions(
) it was necessary to add a convenience callback to be used as a choice validator that only called one method on a Drupal service:
return array_keys(\Drupal::service('user.permissions')->getPermissions());
This isn't the only place where will need to create additional functions that only return valid choices. If we want to make all configuration in Drupal fully validatable then there are many places where we need to do something similar.
For instance, the same thing was done in /core/modules/editor/src/Entity/Editor.php
:
/**
* Computes all valid choices for the "image_upload.scheme" setting.
*
* @see editor.schema.yml
*
* @return string[]
* All valid choices.
*
* @internal
*/
public static function getValidStreamWrappers(): array {
return array_keys(\Drupal::service('stream_wrapper_manager')->getNames(StreamWrapperInterface::WRITE_VISIBLE));
}
We don't have many other examples at this point, but there is still of a lot of configuration to validate and there may be more places where we need to add these functions that might not be useful for anything other than constraint validation.
Proposed resolution
As discussed with @xjm, @wim-leers, @carsoncho, and @jcorrao, we thought it might be useful to override Symfony's Choice Constraint doing something like this pseudo code:
namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Symfony\Component\Validator\Constraints\Choice;
#[Constraint(
id: 'Choice',
label: new TranslatableMarkup('Choice', [], ['context' => 'Validation'])
)]
class ChoiceConstraint extends Choice {
public $callbackArgs = [];
}
and extend the ChoiceValidator:
namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraints\ChoiceValidator;
/**
* Validates complex data.
*/
class ChoiceConstraintValidator extends ChoiceValidator {
/**
* {@inheritdoc}
*/
public function validate(mixed $value, Constraint $constraint) {
assert($constraint instanceof ChoiceConstraint);
if ($constraint->callbackArgs) {
$callback = function() {
// @todo;
};
}
$constraint->callback = $callback;
parent::validate($value, $constraint);
}
}
Remaining tasks
Decide if this would be useful.
User interface changes
None.
API changes
New Choice validator that would allow us to do something like this is /core/modules/user/config/schema/user.schema.yml
:
user.role.*:
...
mapping:
...
permissions:
sequence:
type: string
label: 'Permission'
+ constraints:
+ Choice:
+ callback: user.permissions:getPermissions
+ transform: array_keys