Эта статья была первоначально опубликована в моем личном блоге 30 декабря 2021 года.
Время от времени вы сталкиваетесь с ситуацией, когда вам нужно подтвердить ввод запроса как булево значение, а входное значение 'true'
или 'false'
(обратите внимание, что я заключил значения в одинарные кавычки, чтобы показать, что на самом деле это строки). Вы, как и я, ожидаете, что это будет работать из коробки, и валидатор Laravel будет рассматривать их как булевы значения. Но это не так, вместо этого вы получите красивое сообщение об ошибке 'The inputName field must be true or false.'
.
Чтобы понять, почему это происходит, давайте рассмотрим пример. Допустим, у нас есть запрос формы, который мы называем PostRequest
.
Запросы формы — это классы пользовательских запросов, которые инкапсулируют собственную логику валидации и авторизации.
— Laravel docs.
<?php
namespace AppHttpRequests;
use IlluminateFoundationHttpFormRequest;
class PostRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'is_published' => 'required|boolean',
];
}
}
PostRequest
, для простоты, имеет единственный вход запроса, который, во-первых, является обязательным, а во-вторых, булевым, и этот вход называется is_published
. Предположим также, что значение is_published
, приходящее к нам от клиента, равно 'true'
или 'false'
.
На этом этапе мы уже знаем, что валидация не пройдет. Итак, как мы можем справиться с этим?
Предварительно
Прежде чем погрузиться в эту тему, я хочу показать вам логику, которую Laravel использует для внутренней валидации булевых значений. Вы можете увидеть это в методе validateBoolean()
, расположенном в Illuminate/Validation/Concerns/ValidatesAttributes
.
<?php
namespace IlluminateValidationConcerns;
/**
* Validate that an attribute is a boolean.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function validateBoolean($attribute, $value)
{
$acceptable = [true, false, 0, 1, '0', '1'];
return in_array($value, $acceptable, true);
}
Мы также можем видеть это в данном модульном тесте.
<?php
use IlluminateValidationValidator;
// ...
public function testValidateBoolean()
{
$trans = $this->getIlluminateArrayTranslator();
// ...
$v = new Validator($trans, ['foo' => 'false'], ['foo' => 'Boolean']);
$this->assertFalse($v->passes());
$v = new Validator($trans, ['foo' => 'true'], ['foo' => 'Boolean']);
$this->assertFalse($v->passes());
// ...
}
Источник : https://github.com/laravel/framework/blob/8.x/tests/Validation/ValidationValidatorTest.php
Решения
Первый подход — использование метода prepareForValidation
Давайте начнем с более простого, на мой взгляд, подхода, который Laravel называет «подготовкой ввода к проверке»,
и делается это с помощью метода prepareForValidation()
. Как видно из названия, этот метод позволяет нам добавлять новые или обновлять существующие входы запроса перед прохождением правил валидации.
Поэтому в нашем небольшом примере мы попытаемся преобразовать значение is_published
в действительное булево значение и объединить его с исходным запросом.
<?php
namespace AppHttpRequests;
use IlluminateFoundationHttpFormRequest;
class PostRequest extends FormRequest
{
// ...
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'is_published' => 'required|boolean',
];
}
/**
* Prepare inputs for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'is_published' => $this->toBoolean($this->is_published),
]);
}
/**
* Convert to boolean
*
* @param $booleable
* @return boolean
*/
private function toBoolean($booleable)
{
return filter_var($booleable, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
}
}
FILTER_VALIDATE_BOOLEAN
пытается быть умным, распознавая такие слова как 'Yes'
, 'No'
, 'Off'
, 'On'
, 'true'
и 'false'
, и не чувствителен к регистру при проверке строк.
FILTER_VALIDATE_BOOLEAN
возвращает true для '1'
, 'true'
, 'on'
и 'yes'
. В противном случае возвращает false.
Когда установлен флаг FILTER_NULL_ON_FAILURE
, false возвращается ТОЛЬКО для значений '0'
, 'false'
, 'off'
, 'no'
и ''
, а null возвращается для всех небулевых значений.
Понимание того, как флаг FILTER_NULL_ON_FAILURE
влияет на функцию filter_var
, очень важно, особенно при рассмотрении второго подхода, как мы увидим позже.
Поэтому позвольте мне привести несколько примеров, демонстрирующих поведение метода toBoolean
в различных ситуациях.
$this->toBoolean('1'); // true
$this->toBoolean('true'); // true
$this->toBoolean('on'); // true
$this->toBoolean('yes'); // true
$this->toBoolean('0'); // false
$this->toBoolean('false'); // false
$this->toBoolean('off'); // false
$this->toBoolean('no'); // false
$this->toBoolean('not a boolean'); // null
До этого момента все логично: «истинные» булевы — это true
, «ложные» — false
, остальные — просто null
. Отлично!
$this->toBoolean(''); // false
Вот здесь становится интересно, этот последний случай использования может действительно запутать, я сам ожидал null
в качестве возвращаемого значения, но вместо этого мы получаем boolean
. (false
в данном случае).
Это приведет к ложной проверке, потому что пустая строка будет оценена как boolean
, что сделает проверку пройденной.
Обратите внимание, что в нашем примере этого не произойдет, потому что у нас есть правило required
, если входной запрос (is_published
) является пустой строкой, валидация будет провалена еще до того, как будет выполнено правило boolean
.
Я посчитал важным упомянуть об этом.
С учетом сказанного, давайте перейдем непосредственно ко второму подходу.
Второй подход — использование пользовательского правила валидации
Хотя первый подход отлично работает, существует «стильный» способ проверки ввода как boolean, и это создание пользовательских правил валидации с помощью объектов правил.
Laravel предоставляет множество полезных правил валидации, однако вы можете захотеть задать несколько своих собственных. Одним из методов регистрации пользовательских правил валидации является использование объектов правил. Чтобы создать новый объект правила, вы можете использовать команду make:rule Artisan.
— Laravel docs.
Давайте воспользуемся этой командой для создания правила, которое проверяет строковое значение true
и false
как булево.
php artisan make:rule Boolean
Laravel поместит новое правило в каталог
app/Rules
. Если этого каталога не существует, Laravel создаст его, когда вы выполните команду Artisan для создания правила.— Laravel docs.
Как и было обещано, класс Boolean
создан в пространстве имен app/Rules
, и вот как он выглядит:
<?php
namespace AppRules;
use IlluminateContractsValidationRule;
class Boolean implements Rule
{
/**
* Create a new rule instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
//
}
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return 'The validation error message.';
}
}
После создания правила Boolean
мы готовы определить его поведение.
Объект правила содержит два метода: passes
и message
. Метод passes получает значение и имя атрибута и должен возвращать true
или false
в зависимости от того, является ли значение атрибута действительным или нет. Метод message должен возвращать сообщение об ошибке валидации, которое должно использоваться при неудачной валидации.
Небольшой поворот — сделать глобальные вспомогательные функции
Но, прежде чем сделать это, может быть полезно извлечь toBoolean
из предыдущей функции в свою собственную функцию и сделать ее доступной глобально.
Простой и эффективный способ создания глобальных функций в Laravel — это автозагрузка непосредственно из Composer. Секция autoload в composer принимает массив files
, который автоматически загружается.
- Создайте файл
helpers.php
в любом удобном для вас месте. Я обычно держу свои глобальные помощники вapp/Support/helpers.php
. -
Добавьте свои функции-помощники, не нужно указывать пространство имен (поэтому нам не нужно использовать
use function
для их вызова).if (!function_exists('to_boolean')) { /** * Convert to boolean * * @param $booleable * @return boolean */ function to_boolean($booleable) { return filter_var($booleable, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); } }
-
В
composer.json
внутри секцииautoload
добавьте следующую строку"files": ["app/Support/helpers.php"]
."autoload": { "psr-4": { "App\": "app/", "Database\Factories\": "database/factories/", "Database\Seeders\": "database/seeders/" }, "files": ["app/Support/helpers.php"] }
-
Запустите
composer dump-autoload
.
Теперь наша функция to_boolean
может быть вызвана в любом месте нашего проекта.
Вернемся к нашему правилу Boolean
.
<?php
namespace AppRules;
use IlluminateContractsValidationRule;
class Boolean implements Rule
{
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
return is_bool(to_boolean($value));
}
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return __('validation.boolean');
}
}
Для нашего случая мы можем смело удалить конструктор.
Функция is_bool
— это встроенная функция php, она выясняет, является ли переменная булевой.
Функция __
— это помощник Laravel strings, она переводит заданную строку перевода или ключ перевода, используя ваши файлы локализации.
Все почти готово, теперь нам осталось обновить наш PostRequest
, чтобы реализовать объект пользовательского правила Boolean
следующим образом:
<?php
use AppRulesBoolean;
// ...
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'is_published' => ['required', new Boolean],
];
}
Заключение
Наконец, наш пост подошел к концу. Вкратце напомним, что мы описали два способа, если хотите, подхода к проверке 'true'
и 'false'
как булевых значений с помощью Laravel validator.
Первый подход заключается в подготовке ввода к валидации с помощью метода prepareForValidation
, предоставляемого нам FormRequest
.
Второй подход заключается в использовании пользовательских правил валидации, точнее, объектов правил, для чего мы создали собственный объект Boolean
для выполнения этой работы.
Я знаю, что говорил, что первый подход проще в реализации, но теперь, когда я чаще использую объект rule, я нахожу его более простым и чистым, абстракция в объекте rule более «дружелюбна к разработчику», так сказать, а первый подход, возможно, более многословен. В любом случае, полезно знать их оба, используйте тот, который подходит для вашего случая использования или ваших личных предпочтений.
Ссылки
- php.net — is_bool
- php.net — валидация фильтров
- php.net — filter_var — примечания, внесенные пользователем
- stackoverflow.com — Как сделать глобальные вспомогательные функции в laravel
- stackoverflow.com — Входное значение false для FILTER_VALIDATE_BOOLEAN
- github.com — Валидация булевых значений не принимает «true» и «false», но принимает «1», «0»
Первоначально опубликовано на https://echebaby.com 30 декабря 2021 года.