You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This is where your description should go. Limit it to a paragraph or two. Consider adding a small example.
8
+
This package makes it easy to add validation helpers to your Eloquent models. Some similar packages already exist, but this package aims to achieve flawless functionality in a customizable, Laravel-esque way.
We invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can support us by [buying one of our paid products](https://spatie.be/open-source/support-us).
15
-
16
-
We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on [our contact page](https://spatie.be/about-us). We publish all received postcards on [our virtual postcard wall](https://spatie.be/open-source/postcards).
12
+
- Adds several validation methods to your models like `validate()`, `passesValidation()`, and `failsValidation()`.
13
+
- Validation can be configured to occur automatically when saving the model (opt-in via an interface and can be turned off globally when needed).
14
+
- Validation rules can be configured as a single definition or broken out into independent rules for creating vs. updating.
15
+
- Validation rules can be superseded or mixed with custom rules at runtime.
16
+
- The data used for validation can be customized and transformed prior to validating.
17
+
- Models can define custom validation messages and attribute names.
18
+
- The validation configuration (data, rules, messages, names) are accessible via public methods, so incorporating them into validation processes with requests, controllers, Nova, Filament, etc. is easy.
19
+
- Model event hooks for `validating` and `validated` are provided, easy to work with, and can be used in your existing model observers.
20
+
- Custom validation listeners can be defined for specific model events.
21
+
- Helpers are provided to work with `Unique` rules that need to ignore the current model record when updating.
22
+
- A custom ValidationException is thrown that includes the model that was validated as a property to assist with debugging, logging, and error messages.
17
23
18
24
## Installation
19
25
@@ -23,39 +29,330 @@ You can install the package via composer:
use StevenFox\LaravelModelValidation\ValidatesAttributes;
42
+
43
+
class ValidatingModel extends Model
44
+
{
45
+
use ValidatesAttributes;
46
+
47
+
protected function commonValidationRules() : array
48
+
{
49
+
return [
50
+
// rules go here as ['attribute' => ['rule1', 'rule2', ...]
51
+
// like a normal validation setup
52
+
];
53
+
}
54
+
}
55
+
56
+
$model = new ValidatingModel($request->json());
57
+
58
+
$model->validate(); // A ModelValidationException is thrown if validation fails.
59
+
$model->save();
60
+
$validator = $model->validator();
61
+
$validatedData = $validator->validated();
62
+
63
+
// Other helpful methods...
64
+
$passes = $model->passesValidation(); // An exception, will *not* be thrown if validation fails.
65
+
$fails = $model->failsValidation(); // No exception thrown here, either.
66
+
$validator = $model->validator();
31
67
```
32
68
33
-
You can publish the config file with:
69
+
### The `ValidatesWhenSaving` Interface
70
+
You can make a model automatically perform validation when saving by adding the `\StevenFox\LaravelModelValiation\Contracts\ValidatesWhenSaving` interface.
71
+
This is an **opt-in** feature. Without implementing this interface on your individual models, you can still perform validation on command; it simply won't be performed during the `save()` process automatically.
use StevenFox\LaravelModelValidation\Contracts\ValidatesWhenSaving;
76
+
use StevenFox\LaravelModelValidation\ValidatesAttributes;
77
+
78
+
class ValidatingModel extends Model implements ValidatesWhenSaving
79
+
{
80
+
use ValidatesAttributes;
81
+
82
+
// This model will now validate upon saving.
83
+
}
84
+
```
85
+
86
+
#### Validation Listeners
87
+
By default, this package will register an event listener for the `creating` and `updating` model events that performs the validation prior to saving the model. You can customize this behavior by overloading the static `validatingListeners()` method on your models. Here is the default implementation that you can adjust to your needs.
88
+
89
+
```php
90
+
protected static function validatingListeners(): array
91
+
{
92
+
return [
93
+
'creating' => ValidateModel::class,
94
+
'updating' => ValidateModel::class,
95
+
];
96
+
}
97
+
```
98
+
99
+
> Note: We specifically use the `creating` and `updating` events over the more general `saving` event so that we don't redundantly validate a model that is "saved" without any changed attributes (which does NOT fire an `updating` event, saving us from redundancy).
100
+
101
+
> Note: Keep in mind that the automatic validation process is implemented with Laravel's model event system. Thus, if you perform a `saveQuietly()` or do something else that disables/halts the model's event chain, you will disable the automatic validation as a consequence.
102
+
103
+
### More Control Over Validation Rules
104
+
You can use the following methods to gain finer control over the validation rules used in particular situations.
105
+
106
+
```php
107
+
use Illuminate\Database\Eloquent\Model;
108
+
use StevenFox\LaravelModelValidation\ValidatesAttributes;
109
+
110
+
class ValidatingModel extends Model
111
+
{
112
+
use ValidatesAttributes;
113
+
114
+
/**
115
+
* Define rules that are common to both creating and updating a model record.
116
+
*/
117
+
protected function commonValidationRules(): array
118
+
{
119
+
return [
120
+
'name' => ['required', 'string', 'max:255'],
121
+
// more rules...
122
+
];
123
+
}
124
+
125
+
/**
126
+
* Define the rules that are unique to creating a record.
127
+
* These rules will be *combined* with the common validation rules.
128
+
*
129
+
*/
130
+
protected function validationRulesUniqueToCreating(): array
* Define the rules that are used when creating a record.
147
+
* If you use this method on your model, the 'commonValidationRules'
148
+
* will not be used by default.
149
+
*/
150
+
protected function validationRulesForCreating(): array
151
+
{
152
+
// ...
153
+
}
154
+
155
+
/**
156
+
* Define the rules that are used when updating a record.
157
+
* If you use this method on your model, the 'commonValidationRules'
158
+
* will not be used by default.
159
+
*/
160
+
protected function validationRulesForUpdating(): array
161
+
{
162
+
// ...
163
+
}
164
+
}
37
165
```
38
166
39
-
This is the contents of the published config file:
167
+
#### Unique Columns
168
+
Specifying an attribute as `unique` is a common validation need. Therefore, this package provides a shortcut that you can use in the `commonValidationRules()` method for your unique columns. The helper function will simply define a `Unique` rule for the attribute, and when the model record already exists in the database, the rule will automatically invoke the `ignoreModel($this)` method.
40
169
41
170
```php
42
-
return [
171
+
/**
172
+
* Define rules that are common to both creating and updating a model record.
173
+
*/
174
+
protected function commonValidationRules(): array
175
+
{
176
+
return [
177
+
'email' => [
178
+
'required',
179
+
'email',
180
+
'max:255',
181
+
$this->uniqueRule(), // adds a unique rule that handles ignoring the current record on update
182
+
],
183
+
// more rules...
184
+
];
185
+
}
186
+
```
187
+
188
+
### Runtime Customization for Rules
189
+
190
+
#### Superseding Rules
191
+
You can also use the `setSupersedingValidationRules()` method to set temporary rules that will **replace** all other rules defined on the model.
$model->validate(); // The validator will **only** use the $customRules for validation.
202
+
203
+
$model->clearSupersedingValidationRules();
204
+
$model->validate(); // The validator will now go back to using the normal rules defined on the model.
44
205
```
45
206
46
-
Optionally, you can publish the views using
207
+
>Note: You can temporarily disable a specific model instance's validation by setting the `supersedingValidationRules` to an empty array. The validation process will still run, but with no rules to validate against, the model will automatically pass.
$model->validate(); // Validation will run, but with no rules defined, no actual validation will occur.
215
+
216
+
$model->clearSupersedingValidationRules();
217
+
$model->validate(); // Validation will occur normally.
50
218
```
219
+
#### Mixin Rules
220
+
You can use the `setMixinValidationRules()` method to set rules that will be **merged** with the other rules defined on the model. The rules you define for a particular attribute will replace any existing rules for that attribute.
51
221
52
-
## Usage
222
+
For example, suppose your model specifies that a dateTime column must simply be a `date` by default, but for a particular situation, you want to ensure that the attribute's value is a date _after a particular moment_. You can do this by mixing in this custom ruleset for this attribute at runtime.
53
223
54
224
```php
55
-
$laravelModelValidation = new StevenFox\LaravelModelValidation();
// Normally, the ValidatingModel specifies that the 'date_attribute'
228
+
// is simply a 'date'.
229
+
230
+
// However, here we will specify that it must be a date after tomorrow.
231
+
$mixinRules = [
232
+
'date_attribute' => ['date', 'after:tomorrow']
233
+
];
234
+
235
+
$model->setMixinValidationRules($mixinRules);
236
+
237
+
$model->validate(); // The validator will use a *combination* of the mixin rules and the standard rules defined within the model.
238
+
239
+
$model->clearMixinValidationRules();
240
+
$model->validate(); // The validator will now go back to using the normal rules defined on the model.
241
+
```
242
+
243
+
### Validation Data
244
+
By default, this package will use the model's `getAttributes()` method as the data to pass to the validator instance. Normally, the array returned from the `getAttributes()` method represents the raw values that will be stored within the database. This means attributes with casting will be mutated into the format used for storage, making the validation logic as seamless as possible. For example, most date attributes on models are cast to `Carbon` instances, but when validating dates, the validator needs to receive the string representation of the date, not a `Carbon` instance.
245
+
246
+
If you need to customize the attributes used as data for validation, you can do so in two ways:
247
+
1. Overload the `rawAttributesForValidation()` method and return what you need.
248
+
2. Overload the `prepareAttributesForValidation($attributes)` method to transform the default attribute values into a validation-ready state.
249
+
250
+
### Globally Disabling Validation When Saving
251
+
It is possible to disable the automatic validation during the save process for models that implement the `ValidatesOnSave` interface. This can be helpful when setting up a particular test, for example.
252
+
253
+
#### Option 1
254
+
Call the static `disableValidationWhenSaving()` on a validating model class. This will disable validation until you explicitly activate it again. This is similar to the `Model::unguard()` concept, and like unguarding, you would likely do the disabling of validation in the `boot` method of a `ServiceProvider`.
255
+
```php
256
+
// Perhaps in a ServiceProvider...
257
+
258
+
public function boot(): void
259
+
{
260
+
ValidatingModel::disableValidationWhenSaving();
261
+
262
+
// All models that validate their attributes and implement
263
+
// the ValidatesWhenSaving interface will no longer perform
264
+
// that automatic validation during the saving process.
265
+
}
266
+
```
267
+
268
+
#### Option 2
269
+
Call the static `whileValidationDisables()` method, passing in a callback that executes the logic you would like to perform while automatic validation is globally disabled. This is similar to the `Model::unguarded($callback)` concept.
// do something while automatic validation is globally disabled...
273
+
});
57
274
```
58
275
276
+
### Validation Model Events
277
+
This package adds `validating` and `validated` model events. It also registers these as "observable events", which means you can listen for them within your model observer classes, like you would for `saving`, `deleting`, etc.
278
+
279
+
When implementing a listener for this event, the model record emitting the event and the related validator instance will be supplied to the callback.
280
+
281
+
```php
282
+
\Illuminate\Support\Facades\Event::listen(
283
+
'eloquent.validating*',
284
+
function (Model $model, Validator $validator) {
285
+
// Do something when any model is "validating".
286
+
// $model will be an instance of Model and ValidatesAttributes.
287
+
}
288
+
);
289
+
```
290
+
291
+
Similar to the other observable model events, this package provides static `validating($callback)` and `validated($callback)` methods that you can use to register listeners for these events.
You can access the Validator instance that was last used to perform the `validate()` process with the `validator()` method.
304
+
```php
305
+
$model = new ValidatingModel($request->json());
306
+
307
+
$validator = $model->validate();
308
+
309
+
// Or...
310
+
311
+
$model->validate();
312
+
$validator = $model->validator();
313
+
```
314
+
315
+
> Note: A new validator instance is instantiated and stored on the model instance each time the `validate()` method is invoked.
316
+
317
+
```php
318
+
$model = new ValidatingModel(...);
319
+
320
+
$validator1 = $model->validate();
321
+
322
+
$validator2 = $model->validate();
323
+
324
+
// $validator1 is *not* a reference to the same object as $validator2.
325
+
```
326
+
327
+
#### Customizing the Validator
328
+
You can customize the validator instance with the `beforeMakingValidator()` and `afterMakingValidator($validator)` methods on a model.
329
+
330
+
> Note: The `afterMakingValidator()` method can be a great place to specify `after` hooks for your validation process.
331
+
332
+
You can pass custom validation messages and custom attribute names to the validator via the `customValidationMessages()` and `customValidationAttributeNames()` methods respectively.
333
+
334
+
```php
335
+
class ValidatingModel extends Model
336
+
{
337
+
use ValidatesAttributes;
338
+
339
+
public function customValidationMessages(): array
340
+
{
341
+
return ['email.required' => 'We need to know your email address.']
342
+
}
343
+
344
+
public function customValidationAttributeNames(): array
345
+
{
346
+
return ['ip_v4' => 'ip address'];
347
+
}
348
+
}
349
+
```
350
+
351
+
### Validation Exception
352
+
When the `validate()` method is invoked and validation fails, a `ModelValidationException` is thrown by default. This exception extends Laravel's `ValidationException`, but stores a reference to the model record that failed validation to make debugging or error messages easier to handle.
353
+
354
+
You can provide your own validation exception by overloading the `validationExceptionClass()` or `throwValidationException($validator)` methods.
0 commit comments