Hello everyone, today I will tell you how and why I structured validation in Laravel.
Let's remember how Form Request works
Form Request is a class where we describe the validation rules for incoming data. Usually the class contains a set of rules for the request from the client. We can declare it in the controller, and through the container in Laravel, it will automatically check the data in accordance with our rules and, through the internal mechanisms of the framework, will give a response to the client.
For example, we need to update the user profile. Form Request might look like this:
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UpdateUserProfile extends FormRequest
{
public function rules(): array
{
return [
'email' => ['required', 'email'],
'name' => ['required', 'alpha'],
'age' => ['integer', 'max:120'],
];
}
public function messages():array
{
return [
'email.required' => 'Email email'
];
}
}
. . Form Request ...
, , . .
, . - "mail ". , , .
Form Request ?
Form Request .
Form Request . ( ). , . ( ) .
. "Don't repeat yourself". ( ), .
public function store(Request $request)
{
$validated = $request->validate([
'email' => ['required', 'email']
]);
}
, (field) ValidatorValue.
Form Request.
class UpdateUserProfile extends FormRequestDecompose
{
public function rules(): array
{
return [
new UserEmail(auth()->user()->id),
new UserName(),
new UserAge(),
];
}
}
FormRequestDecompose, ValidatorValue.
, .
( )
?
ValidatorValue. . , , . getRules , getMessages ( ).
class UserEmail implements ValidatorValue
{
private $attribute;
private $exceptUserId;
public function __construct(int $userId, string $attribute = 'email')
{
$this->exceptUserId = $userId;
$this->attribute = $attribute;
}
public function getAttribute(): string
{
return $this->attribute;
}
public function getRules(): array
{
return [
'required',
'email',
"unique:users,email,{$this->exceptUserId}",
];
}
public function getMessages(): array
{
return [
"{$this->attribute}.email" => ', email',
"{$this->attribute}.required" => ', email',
"{$this->attribute}.unique" => 'Email '
];
}
}
interface ValidatorValue
{
/**
* Should return list rules
* @example ['required','email','unique:users,email'];
* @return array
*/
public function getRules(): array;
/**
* @return string
*/
public function getAttribute(): string;
/**
* @return array
*/
public function getMessages(): array;
}
?
Laravel BotMan, Laravel.
, Facade . .
$validatorUserEmail = UserEmail(auth()->user()->id);
$this->validator = Validator::make([
$validatorUserEmail->getAttribute() => $answerFromUser
],[
$validatorUserEmail->getAttribute() => $validatorUserEmail->getRules()
],
$validatorUserEmail->getMessages());
if ($this->validator->fails() === false) {
// ...
}
, .
:
if ($this->validate($answer->getText(), new UserEmail($this->user->id)) {
// ...
}
, , FormRequestDecompose, .
With this approach, I like that all the rules are in one place. We can use it both in Form Request and in normal validation. Secondly, the name of the class can be more expressive for the subject area, for example: ConsumerEmail, SellerPersonalPhone.
Especially for the lazy and curious, I created a repository . The code is pretty simple there, so you can just adapt it for yourself. If you have encountered a similar problem in your practice, write in the comments how you solved it.