Callback
- um wrapper em torno de callables
Esta regra permite a validação do valor do atributo atual (mas não limitado a ele) com uma condição arbitrária dentro de um callable. A vantagem é que não há necessidade de criar uma regra e um manipulador personalizado separados.
Uma condição pode estar dentro de:
A desvantagem de usar funções independentes e métodos DTO
é a falta da capacidade de reutilização. Então eles são principalmente úteis
para algumas condições específicas não repetitivas. A reutilização pode ser alcançada com classes que podem ser chamadas, mas dependendo de outros
fatores (a necessidade de parâmetros adicionais, por exemplo), pode ser uma boa ideia criar um relatório completo
regra personalizada com um manipulador separado.
A sintaxe da função de retorno de chamada é a seguinte:
use Yiisoft\Validator\Result;
use Yiisoft\Validator\Rule\Callback;
use Yiisoft\Validator\ValidationContext;
function (mixed $value, Callback $rule, ValidationContext $context): Result;
onde:
$value
é o valor validado;$rule
é uma referência à regraCallback
original;$context
é o contexto da validação;- o valor retornado é uma instância do resultado da validação com ou sem erros.
Um exemplo de passagem de um callback autônomo para uma regra Callback
:
use Yiisoft\Validator\Result;
use Yiisoft\Validator\Rule\Callback;
use Yiisoft\Validator\ValidationContext;
new Callback(
static function (mixed $value, Callback $rule, ValidationContext $context): Result {
// The actual validation code.
return new Result();
},
);
A regra Callback
pode ser usada para adicionar validação ausente nas regras integradas para o valor de um único atributo. Abaixo está o
exemplo verificando se um valor é uma string YAML válida (requer adicionalmente a extensão PHP yaml
):
use Exception;
use Yiisoft\Validator\Result;
use Yiisoft\Validator\Rule\Callback;
new Callback(
static function (mixed $value): Result {
if (!is_string($value)) {
return (new Result())->addError('The value must be a string.');
}
$notYamlMessage = 'This value is not a valid YAML.';
try {
$data = yaml_parse($value);
} catch (Exception $e) {
return (new Result())->addError($notYamlMessage);
}
if ($data === false) {
return (new Result())->addError($notYamlMessage);
}
return new Result();
},
);
Nota: Processar entradas de usuários não confiáveis com
yaml_parse()
pode ser perigoso com certas configurações. Consulte a documentaçãoyaml_parse()
para mais detalhes.
No exemplo abaixo, os 3 ângulos são validados como graus para formar um triângulo válido:
use Yiisoft\Validator\Result;
use Yiisoft\Validator\Rule\Callback;
use Yiisoft\Validator\Rule\Integer;
use Yiisoft\Validator\Rule\Required;
use Yiisoft\Validator\ValidationContext;
$rules = [
'angleA' => [
new Required(),
new Integer(),
],
'angleB' => [
new Required(),
new Integer(),
],
'angleC' => [
new Required(),
new Integer(),
],
new Callback(
static function (mixed $value, Callback $rule, ValidationContext $context): Result {
$angleA = $context->getDataSet()->getPropertyValue('angleA');
$angleB = $context->getDataSet()->getPropertyValue('angleB');
$angleC = $context->getDataSet()->getPropertyValue('angleC');
$sum = $angleA + $angleB + $angleC;
if ($sum <= 0) {
return (new Result())->addError('The angles\' sum can\'t be negative.');
}
if ($sum > 180) {
return (new Result())->addError('The angles\' sum can\'t be greater than 180 degrees.');
}
return new Result();
}
),
];
Substituindo o código padrão por regras separadas e when
No entanto, alguns casos de uso de contexto de validação podem levar a um código padrão:
use Yiisoft\Validator\Result;
use Yiisoft\Validator\Rule\Callback;
use Yiisoft\Validator\ValidationContext;
static function (mixed $value, Callback $rule, ValidationContext $context): Result {
if ($context->getDataSet()->getPropertyValue('married') === false) {
return new Result();
}
$spouseAge = $context->getDataSet()->getPropertyValue('spouseAge');
if ($spouseAge === null) {
return (new Result())->addError('Spouse age is required.');
}
if (!is_int($spouseAge)) {
return (new Result())->addError('Spouse age must be an integer.');
}
if ($spouseAge < 18 || $spouseAge > 100) {
return (new Result())->addError('Spouse age must be between 18 and 100.');
}
return new Result();
};
Eles podem ser reescritos usando múltiplas regras e validação condicional, tornando o código mais intuitivo. Podemos usar regras embutidas sempre que possível:
use Yiisoft\Validator\Rule\BooleanValue;
use Yiisoft\Validator\Rule\Integer;
use Yiisoft\Validator\ValidationContext;
$rules = [
'married' => new BooleanValue(),
'spouseAge' => new Integer(
min: 18,
max: 100,
when: static function (mixed $value, ValidationContext $context): bool {
return $context->getDataSet()->getPropertyValue('married') === true;
},
),
];
Ao usar como um atributo PHP, defina o método de um objeto como um retorno de chamada:
use Exception;
use Yiisoft\Validator\Result;
use Yiisoft\Validator\Rule\Callback;
final class Config {
public function __construct(
#[Callback(method: 'validateYaml')]
private string $yaml,
) {
}
private function validateYaml(mixed $value): Result
{
if (!is_string($value)) {
return (new Result())->addError('The value must be a string.');
}
$notYamlMessage = 'This value is not a valid YAML.';
try {
$data = yaml_parse($value);
} catch (Exception $e) {
return (new Result())->addError($notYamlMessage);
}
if ($data === false) {
return (new Result())->addError($notYamlMessage);
}
return new Result();
}
}
A sintaxe é a mesma de uma função normal. Observe que não há restrições nos níveis de visibilidade e modificadores estáticos,
todos eles podem ser usados (public
, protected
, private
, static
).
Usar um argumento callback
em vez de um método
com atributos PHP é proibido devido à restrições da linguagem PHP atual
(um retorno de chamada não pode estar dentro de um atributo PHP).
Também é possível verificar todo o objeto:
use Exception;
use Yiisoft\Validator\Result;
use Yiisoft\Validator\Rule\Callback;
#[Callback(method: 'validate')]
final class Config {
public function __construct(
private int $yaml,
) {
}
private function validate(): Result
{
if (!is_string($this->yaml)) {
return (new Result())->addError('The value must be a string.');
}
$notYamlMessage = 'This value is not a valid YAML.';
try {
$data = yaml_parse($this->yaml);
} catch (Exception $e) {
return (new Result())->addError($notYamlMessage);
}
if ($data === false) {
return (new Result())->addError($notYamlMessage);
}
return new Result();
}
}
Observe o uso do valor da propriedade ($this->yaml
) em vez do argumento do método ($value
).
Uma classe que implementa __invoke()
também pode ser usada como callable:
use Exception;
use Yiisoft\Validator\Result;
use Yiisoft\Validator\Rule\Callback;
use Yiisoft\Validator\ValidationContext;
final class YamlCallback
{
public function __invoke(mixed $value): Result
{
if (!is_string($value)) {
return (new Result())->addError('The value must be a string.');
}
$notYamlMessage = 'This value is not a valid YAML.';
try {
$data = yaml_parse($value);
} catch (Exception $e) {
return (new Result())->addError($notYamlMessage);
}
if ($data === false) {
return (new Result())->addError($notYamlMessage);
}
return new Result();
}
}
A sintaxe é a mesma de uma função regular.
Usando em regras (observe que uma nova instância deve ser passada, não um nome de classe):
use Yiisoft\Validator\Rule\Callback;
$rules = [
'yaml' => new Callback(new YamlCallback()),
];
Ao usar com o validator e as configurações padrão da regra Callback
, uma declaração de regra pode ser omitida, portanto, apenas incluir um
callable é suficiente. Será normalizado automaticamente antes da validação:
use Exception;
use Yiisoft\Validator\Result;
use Yiisoft\Validator\Validator;
$data = [];
$rules = [
'yaml' => static function (mixed $value): Result {
if (!is_string($value)) {
return (new Result())->addError('The value must be a string.');
}
$notYamlMessage = 'This value is not a valid YAML.';
try {
$data = yaml_parse($value);
} catch (Exception $e) {
return (new Result())->addError($notYamlMessage);
}
if ($data === false) {
return (new Result())->addError($notYamlMessage);
}
return new Result();
},
];
$result = (new Validator())->validate($data, $rules);
Ou pode ser definido como um array de outras regras:
use Exception;
use Yiisoft\Validator\Result;
use Yiisoft\Validator\Rule\Required;
use Yiisoft\Validator\Validator;
$data = [];
$rules = [
'yaml' => [
new Required(),
static function (mixed $value): Result {
if (!is_string($value)) {
return (new Result())->addError('The value must be a string.');
}
$notYamlMessage = 'This value is not a valid YAML.';
try {
$data = yaml_parse($value);
} catch (Exception $e) {
return (new Result())->addError($notYamlMessage);
}
if ($data === false) {
return (new Result())->addError($notYamlMessage);
}
return new Result();
},
],
];
$result = (new Validator())->validate($data, $rules);