It is possible to add extra rules as attributes to properties of a data object:
class SongData extends Data
{
public function __construct(
#[Uuid()]
public string $uuid,
#[Max(15), IP, StartsWith('192.')]
public string $ip,
) {
}
}
These rules will be merged together with the rules that are inferred from the data object.
So it is not required to add the required
and string
rule, these will be added automatically. The rules for the
above data object will look like this:
[
'uuid' => ['required', 'string', 'uuid'],
'ip' => ['required', 'string', 'max:15', 'ip', 'starts_with:192.'],
]
For each Laravel validation rule we've got a matching validation attribute, you can find a list of
them here.
##Referencing route parameters
Sometimes you need a value within your validation attribute which is a route parameter.
Like the example below where the id should be unique ignoring the current id:
class SongData extends Data
{
public function __construct(
public string $title,
#[Unique('songs', ignore: new RouteParameterReference('song'))]
public int $id,
) {
}
}
If the parameter is a model and another property should be used, then you can do the following:
class SongData extends Data
{
public function __construct(
public string $title,
#[Unique('songs', ignore: new RouteParameterReference('song', 'uuid'))]
public string $uuid,
) {
}
}
##Referencing the current authenticated user
If you need to reference the current authenticated user in your validation attributes, you can use the
AuthenticatedUserReference
:
use Spatie\LaravelData\Support\Validation\References\AuthenticatedUserReference;
class UserData extends Data
{
public function __construct(
public string $name,
#[Unique('users', 'email', ignore: new AuthenticatedUserReference())]
public string $email,
) {
}
}
When you need to reference a specific property of the authenticated user, you can do so like this:
class SongData extends Data
{
public function __construct(
#[Max(new AuthenticatedUserReference('max_song_title_length'))]
public string $title,
) {
}
}
Using a different guard than the default one can be done by passing the guard name to the constructor:
class UserData extends Data
{
public function __construct(
public string $name,
#[Unique('users', 'email', ignore: new AuthenticatedUserReference(guard: 'api'))]
public string $email,
) {
}
}
##Referencing container dependencies
If you need to reference a container dependency in your validation attributes, you can use the ContainerReference
:
use Spatie\LaravelData\Support\Validation\References\ContainerReference;
class SongData extends Data
{
public function __construct(
public string $title,
#[Max(new ContainerReference('max_song_title_length'))]
public string $artist,
) {
}
}
It might be more useful to use a property of the container dependency, which can be done like this:
class SongData extends Data
{
public function __construct(
public string $title,
#[Max(new ContainerReference(SongSettings::class, 'max_song_title_length'))]
public string $artist,
) {
}
}
When your dependency requires specific parameters, you can pass them along:
class SongData extends Data
{
public function __construct(
public string $title,
#[Max(new ContainerReference(SongSettings::class, 'max_song_title_length', parameters: ['repository' => 'redis']))]
public string $artist,
) {
}
}
##Referencing other fields
It is possible to reference other fields in validation attributes:
class SongData extends Data
{
public function __construct(
public string $title,
#[RequiredIf('title', 'Never Gonna Give You Up')]
public string $artist,
) {
}
}
These references are always relative to the current data object. So when being nested like this:
class AlbumData extends Data
{
public function __construct(
public string $album_name,
public SongData $song,
) {
}
}
The generated rules will look like this:
[
'album_name' => ['required', 'string'],
'songs' => ['required', 'array'],
'song.title' => ['required', 'string'],
'song.artist' => ['string', 'required_if:song.title,"Never Gonna Give You Up"'],
]
If you want to reference fields starting from the root data object you can do the following:
class SongData extends Data
{
public function __construct(
public string $title,
#[RequiredIf(new FieldReference('album_name', fromRoot: true), 'Whenever You Need Somebody')]
public string $artist,
) {
}
}
The rules will now look like this:
[
'album_name' => ['required', 'string'],
'songs' => ['required', 'array'],
'song.title' => ['required', 'string'],
'song.artist' => ['string', 'required_if:album_name,"Whenever You Need Somebody"'],
]
##Rule attribute
One special attribute is the Rule
attribute. With it, you can write rules just like you would when creating a custom
Laravel request:
#[Rule(['required', 'string'])]
public string $property
#[Rule('required|string')]
public string $property
#[Rule('required', 'string')]
public string $property
##Creating your validation attribute
It is possible to create your own validation attribute by extending the CustomValidationAttribute
class, this class
has a getRules
method that returns the rules that should be applied to the property.
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_PARAMETER)]
class CustomRule extends CustomValidationAttribute
{
public function getRules(ValidationPath $path): array|object|string
{
return [new CustomRule()];
}
}
Quick note: you can only use these rules as an attribute, not as a class rule within the static rules
method of the
data class.