Как-то услышал один чудак, что можно уменьшить количество дублей при написании правил валидации с помощью великого знания - DRY, и решил он отправиться на поиски...
Его идеальное решение должно было учитывать несколько основных особенностей:
иметь более-менее лаконичный интерфейс;
давать возможность, при необходимости, редактировать или переписывать правила валидации для полей;
давать возможность на месте добавлять дополнительные поля;
поля в качестве имен могут иметь только строковые значения, но имя не может содержать в себе точки - они нужны для разделения уровней вложенности;
какие-то поля группировались по сущностям БД, какие-то были сами по себе.
Задача была поставлена, герою предстояло отправиться на поиски. Конечно, все хотелось сделать хорошо и быстро, поэтому первым делом он решился исследовать близлежащую деревеньку "Верхние интернеты", с целью раздобыть хоть какой-нибудь транспорт, но, ничего там не отыскав, он понял, что единственным решением для него остается собрать велосипед из подручных средств. Что же, взглянем что у нас имеется...
Чтобы не изобретать колеса, было решено использовать механизм хелперов, который уже присутствовал в Laravel. Не то чтобы очень сложно создать один файл в глобальной области видимости, но, все же, хорошо когда есть штатные средства, как-то на душе приятнее.
Следующим шагом было бы неплохо собрать раму. А так как это у нас заправский ду ит ёрселф, будем обходиться тем что есть - воображением и палками! Главное, чтобы ни то ни другое не попало в колеса.
Итак, первым делом сигнатура - это будет наш руль:
function get_validation_rules(array $fields, array $additionalRules = []): array
Здесь ничего сложного, берем массив с названиями полей и ассоциативный массив с дополнительными правилами для них. Пару слов о первом параметре: он может содержать как простое перечисления полей (индексированный массив), так и структуру типа название поля => правила валидации (ассоциативный массив). Сделано это для того, чтобы можно было перезаписывать правила валидации для отдельных полей.
Дальше, собственно, само тело, чтобы не мучить никого обрывками, вставляю всю функцию целиком:
<?php if (!function_exists('get_validation_rules')) { /** * Возвращает список правил валидации для указанных полей * * @param array<int|string,string|array> $fields поля, для которых необходимо вернуть правила валидации * @param array<string,array> $additionalRules дополнительные правила для полей указанных в $fields, * * @return array<string,array> сформированный ассоциативный массив: поле => правила */ function get_validation_rules(array $fields, array $additionalRules = []): array { $globalRules = config('validation'); foreach ($fields as $fieldKey => $field) { if (!is_int($fieldKey)) { $resultRules[$fieldKey] = $field; continue; } $rulePath = explode('.', $field); $rule = &$globalRules; foreach ($rulePath as $rulePathItem) { $rule = &$rule[$rulePathItem]; } if (!empty($rule)) { if (array_key_exists($field, $additionalRules)) { $uniqueAdditionalRules = array_diff($additionalRules[$field], $rule); $rule = array_merge($uniqueAdditionalRules, $rule); } $resultRules[$field] = $rule; } } return $resultRules ?? []; } }
Вот она, настоящая рама с педалями! Вон, даже foreach крутится! Более того, тут еще и багажник красуется на 15 строке:
$globalRules = config('validation');
Да, в процессе возник закономерный вопрос: где же хранить этот набор правил для валидации? И, пожалуй, самым очевидным ответом на этот вопрос был - в файлах конфигураций. Выдерну лишь отрывок, просто чтобы показать как это выглядит:
<?php return [ 'user' => [ 'phone_number' => ['required', 'string'], 'firstname' => ['required', 'string', 'max:255'], 'date_of_birth' => ['required', 'date', 'before:today'], 'citizenship' => ['required', 'max:512'], 'adress' => ['required', 'string', 'max:255'], 'affiliation' => ['required', 'string', 'max:255'], 'education' => ['required', 'string', 'max:500'], ], 'email' => ['required', 'string', 'max:255'] ];
Собственно, правила могут быть на любом уровне вложенности, но за наименованиями, конечно, придется следить самому. Уровни вложенности, как и было написано в самом начале, разделяются точкой.
Что же, наконец-то можно протестировать это все в действии, для примера возьмем, собственно, класс request'та, в котором необходимо реализовать правила валидации:
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class CoolRequest extends FormRequest { /** * Determine if the user is authorized to make this request. */ public function authorize(): bool { return true; } /** * Get the validation rules that apply to the request. * * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string> */ public function rules(): array { return get_validation_rules( [ 'user.firstname', 'user.phone_number' => ['string'], // Переписываем правила валидации полностью 'user.email' => ['required'] // Добавляем новое поле, которого нет в глобальных правилах ], [ 'user.firstname' => ['min:2'] // Добавляем дополнительное правило валидации к полю user.firstname ] ); } }
Вот и все, когда вопрос с транспортом решен, можно ехать на все четыре стороны, и несмотря ни на что, в этой истории все закончилось хорошо. Велосипед был собран, а чудак понял, что у него, в целом, и дома всяких DRY'ев хватает.
Вот такая вот идея, с нетерпением жду замечаний и предложений по улучшению! Всем спасибо, кто читал!
