Многие правила валидации в Laravel записываются в виде строк, где параметры идут после двоеточия и разделяются запятыми. Правило вроде  required_without:email  работает, но ваш редактор не может дополнять его автоматически, а опечатка в названии правила обнаружится только во время работы валидатора.

В классе  Rule  уже были заложены построители для таких правил, как  in ,  unique ,  exists ,  enum ,  date ,  email  и  file . В Laravel 13.36 добавлен построитель для каждого правила, которое раньше существовало только в виде строки.

#До и после

Вот форма профиля, валидация которой написана с помощью строковых правил:

$request->validate([
    'email' => 'required_without:phone|email',
    'phone' => 'required_without:email',
    'website' => 'nullable|url:https',
    'device_id' => 'uuid:4',
    'tags.*' => 'distinct:ignore_case',
    'password' => 'required|confirmed',
    'timezone' => 'timezone:per_country,US',
]);
 

Те же правила с новыми построителями:

use Illuminate\Validation\Rule;
 
$request->validate([
    'email' => [Rule::requiredWithout('phone'), 'email'],
    'phone' => [Rule::requiredWithout('email')],
    'website' => [Rule::nullable(), Rule::url()->protocols(['https'])],
    'device_id' => [Rule::uuid()->version(4)],
    'tags.*' => [Rule::distinct()->ignoreCase()],
    'password' => [Rule::required(), Rule::confirmed()],
    'timezone' => [Rule::timezone(['per_country', 'US'])],
]);
 

Строковые правила по-прежнему работают, и вы можете совмещать оба стиля в одном массиве.

#Правила без параметров

Они возвращают правило как обычную строку, поэтому  Rule::required()  эквивалентно строке  required . Полный список таких правил включает в себя:

  •  accepted() 
  •  activeUrl() 
  •  bail() 
  •  base64() 
  •  boolean() 
  •  declined() 
  •  exclude() 
  •  filled() 
  •  hexColor() 
  •  json() 
  •  list() 
  •  macAddress() 
  •  missing() 
  •  nullable() 
  •  present() 
  •  prohibited() 
  •  required() 
  •  sometimes() 
  •  ulid() 

Передача  true  в  Rule::boolean()  возвращает  boolean:strict , которое принимает только  true  и  false .

#Правила, принимающие поля или значения

Они принимают имена других полей, ключи или значения в качестве аргументов — либо в виде массива, либо в виде отдельных аргументов:

Rule::requiredWith('first_name', 'last_name');
Rule::requiredWith(['first_name', 'last_name']);
 
Rule::acceptedIf('plan', 'pro');
Rule::prohibits('coupon_code');
Rule::requiredArrayKeys('street', 'city', 'postcode');
 

Вот полный набор таких правил:

  •  acceptedIf() 
  •  declinedIf() 
  •  excludeWith() 
  •  excludeWithout() 
  •  inArray() 
  •  inArrayKeys() 
  •  missingIf() 
  •  missingUnless() 
  •  missingWith() 
  •  missingWithAll() 
  •  notRegex() 
  •  presentIf() 
  •  presentUnless() 
  •  presentWith() 
  •  presentWithAll() 
  •  prohibitedIfAccepted() 
  •  prohibitedIfDeclined() 
  •  prohibits() 
  •  regex() 
  •  requiredArrayKeys() 
  •  requiredIfAccepted() 
  •  requiredIfDeclined() 
  •  requiredWith() 
  •  requiredWithAll() 
  •  requiredWithout() 
  •  requiredWithoutAll() 
  •  timezone() 

#Правила с опциями

Они возвращают объект с методами для настройки параметров правила:

Rule::url()->protocols(['https']);            // url:https
Rule::uuid()->version(7);                     // uuid:7
Rule::ip()->version(6);                       // ipv6
Rule::distinct()->strict()->ignoreCase();     // distinct:strict,ignore_case
Rule::confirmed()->customField('repeat_pass'); // confirmed:repeat_pass
Rule::currentPassword()->guard('admin');      // current_password:admin
 

Метод  version()  у  Rule::ip()  принимает  4  или  6  и генерирует исключение  InvalidArgumentException  для любого другого числа. У  Rule::uuid()  метод  version()  также принимает строки  'nil'  и  'max' .

В том же релизе у цепочечного (fluent) построителя дат появился метод  equals() , который добавляет правило  date_equals :

Rule::date()->equals('2026-12-31');
Rule::date()->equals(today());
 

#Значения с запятыми

Строковый формат разделяет параметры по запятым. Допустим, правило  accepted_if  должно применяться, когда поле  plan  равно  Team, Annual :

'terms' => 'accepted_if:plan,Team, Annual',
 

Валидатор считывает это как два значения —  Team  и   Annual , и правило никогда не совпадает с  Team, Annual .

Построители, принимающие поля или значения, реализуют новый контракт  ParameterizedRule . Его метод  toArray()  возвращает название правила и его параметры в виде массива, и валидатор использует этот массив напрямую, без разбора строки. Значение сохраняет свою запятую:

'terms' => [Rule::acceptedIf('plan', 'Team, Annual')],
 

Это также охватывает кавычки, вертикальные черты и обратные слэши в значениях, а также имена полей или ключей, содержащие запятые (например, ключ массива для  Rule::requiredArrayKeys() ).

Нестроковые значения преобразуются в те строки, которые использовались бы в строковом формате, поэтому  true ,  false  и  null  превращаются в  'true' ,  'false'  и  'null' :

Rule::presentIf('is_company', true); // present_if:is_company,true
 

Если вы приведете построитель к строке (например, для логирования), вы получите строковый формат с кавычками вокруг любого значения, содержащего запятую или двойную кавычку.

#Пример Form Request

В Form Request для оформления заказа можно совместить существующие построители с новыми:

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
 
class CheckoutRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'plan' => [Rule::required(), Rule::in(['starter', 'Team, Annual'])],
            'terms' => [Rule::acceptedIf('plan', 'Team, Annual')],
            'company_name' => [Rule::requiredIfAccepted('is_business')],
            'vat_number' => [Rule::nullable(), Rule::requiredWith('company_name')],
            'billing' => [Rule::required(), Rule::requiredArrayKeys('street', 'city', 'postcode')],
            'coupon_code' => [Rule::nullable(), Rule::prohibits('gift_card')],
            'website' => [Rule::nullable(), Rule::url()->protocols(['https'])],
            'emails.*' => [Rule::distinct()->ignoreCase(), 'email'],
        ];
    }
}
 

#Дополнительное чтение

Эрик (@ericmp33) внес свой вклад в создание построителей правил в пулл-реквесте #61833.