validating: making an Eloquent model refuse to save invalid data
Automatically validating Eloquent models for Laravel
At a glance
- What is it?
- Validating is a trait for Laravel Eloquent models that runs the model's validation rules before the save, and refuses to write the row if the rules fail. It exists because the framework's own validation lives at the form layer, and a model that can be saved from a console command, a queue job or an API endpoint has no form to be validated by.
- Who is it for?
- Validating suits a Laravel application where models are written from several places, since a single trait on the base model makes invalid rows impossible rather than merely unlikely. It is a poor fit if you want form level validation with custom messages, since the readme is explicit that this is the framework's job, and a poor fit if you want the model to be savable in a dirty state during a multi-step flow, because the forceSave escape hatch then becomes the normal path.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Activity is slowing. The repository last received commits 6 months ago.
- What is it written in?
- Mainly PHP, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The gap the trait fills
The description of the library is one sentence: it is a trait for Laravel Eloquent models which ensures that models meet their validation criteria before being saved. If they are not considered valid the model will not be saved and the validation errors will be made available.
The rest of the feature list names the specifics: multiple rulesets, injecting the model identifier into unique validation rules, and raising exceptions on failed validations.
The reason this exists is a design decision in the framework, and the readme explains it directly. Because Laravel 5.0 introduced form request validation, Validating is now designed to keep your core data valid and leave form validation to the framework. The 4.2 era version was better suited to doing form validation, and supported custom validation messages, confirmation rules and multiple rulesets.
That division of labour is the whole point, and it is a sensible one. Form validation is about the user's input in one request. Model validation is about the invariant that the row in the database is never nonsense, no matter which code path wrote it. A console command, an import script, a queue worker and a form request all end up at save, and only the last of those has a form.
So the trait is a way of putting the invariant in the model rather than in each caller, which is a small idea with a large blast radius: once it is on the base model, a data integrity bug stops being possible through the model layer.
Three branches for three eras of Laravel
The readme is organised by framework version, and that is the first thing to work out before reading anything else.
For Laravel 4.2 and later, the 0.10.x branch holds the documentation and installation instructions. For Laravel 5.0 through 5.2, the 2.x branch does. And for Laravel 5.3 and later, the master branch is the current one, with the readme telling you to carry on reading.
The differences between the branches are not cosmetic, and the 2.x one is the interesting case. That version used a validation exception contract from the Laravel framework that has since been deprecated. From 5.3 the package extends the core validation exception instead, and the readme explains the consequence: the framework will automatically redirect back with errors when a validation error occurs, much like a form request would.
That is a significant behavioural change for anyone upgrading, because the exception type in your catch blocks changes. If you are on 5.3 or later you get the modern exception and the automatic redirect, and if you are on 5.0 to 5.2 you get the older contract and probably have to handle the redirect yourself.
The version branches are also a signal about the project's shape. A package that still documents a branch for an old framework version is a package with a user base that is not refreshing quickly, which is worth knowing when you are deciding whether to adopt it.
Wiring the trait, and one trap worth reading twice
Installation is a single Composer line, and the model change is a trait plus a rules array.
composer require watson/validatinguse Watson\Validating\ValidatingTrait;
class Post extends Eloquent
{
use ValidatingTrait;
protected $rules = [
'title' => 'required',
'slug' => 'required|unique:posts,slug',
'content' => 'required'
];
}Note the tab indentation in the property, which is a small clue about the era the code was written in.
There are two ways to apply this beyond a single model. You can add the trait to a base model if your application has one, and it will work on every model that extends from it, or you can extend the validating model class the package provides instead of extending the framework's Eloquent class directly.
Then there is the trap, and the readme is unusually blunt about it. If you use a base model, you need to set the rules property on any model that extends from it, or set an empty array as the rules for the base model itself. If you do not, the readme says you will inevitably end up with a logic exception whose message is that a relationship method must return an object of the relation type.
The exception message is worth recognising, because it gives you no hint that the cause is a missing property on a parent class. That is the single most likely way to lose an afternoon with this package.
Three ways to react to a failed save
The library gives you three ways to deal with an invalid model, and the choice is per model.
The default is the return value. A save returns false when validation fails, and the errors are available for you to attach to a redirect:
if ( ! $post->save()) {
// Oops.
return redirect()->route('posts.create')
->withErrors($post->getErrors())
->withInput();
}The second is the explicit exception, with a method that raises one when you attempt to save an invalid model. The readme stresses that you do not need to catch it, because Laravel knows how to handle a validation exception and will automatically redirect back with the form input and the errors.
$post->saveOrFail();If you do want to handle it yourself, you can catch it and pass the exception straight to the withErrors method, which the readme notes will work because Laravel knows what to do with it.
try {
$post->saveOrFail();
} catch (Watson\Validating\ValidationException $e) {
$errors = $e->getErrors();
return redirect()->route('posts.create')
->withErrors($errors)
->withInput();
}The third is the model-wide default. If you would rather have exceptions thrown by the ordinary save method, you set a property on the model, and the readme gives the docblock for it, with the note that if it is not set it defaults to false.
protected $throwValidationExceptions = true;And there is a one-off variant, the saveOrReturn method, for the cases where you want a return value or an exception for a single call rather than for the whole model.
forceSave, and the unique rule that fixes itself
Two features deserve their own attention, because they are the ones that make the package usable in the awkward cases.
The first is the escape hatch. If you are using the model and you wish to perform a save that bypasses validation, you can, and the readme says the result is the same as if you had called save on a model without the trait.
$post->forceSave();That is a deliberately small, deliberately loud escape. It exists because there are legitimate cases where you need to write a row that does not satisfy the rules, and a method named for what it does is better than a boolean flag on the model that someone sets once and forgets.
The second is the unique rule, and it is the cleverest thing the package does. The readme points out the problem: the slug field uses the unique rule, which would not work when updating an already persisted model, because the model would be compared against itself. Validation takes care of it, appending the model's primary key to the rule so that the rule works as expected, ignoring the current model.
protected $injectUniqueIdentifier = true;The property is documented with a docblock and the note that if it is not set it defaults to true, so the behaviour is on unless you turn it off.
Out of the box the package supports the unique rule that Laravel provides. The readme also says it supports the popular uniquewith-validator rule from another package..
Messages, and the state of the project
Custom validation messages are a property on the model, and the readme is careful to say they are passed to the validator.
protected $validationMessages = [
'slug.unique' => "Another post is using that slug already."
];That is the Laravel validation message format, keyed by the rule and the field, so anything you already know about Laravel messages applies here.
Two operational points close the picture. The package is MIT licensed, published on Packagist under the name watson/validating rather than the repository name, and the readme badges show the download count and the stable and unstable versions. There is a Travis build badge, which dates the continuous integration setup to a particular era, and the repository has a phpunit configuration, a tests directory, a source directory and an editor configuration.
There are no GitHub releases, and the last push was on 2026-04-22 with the repository not archived, so the code is still being touched even though the version numbers you get from Packagist are the ones to trust.
For a package like this, the size is the feature. The whole thing is a trait and a set of properties, with no configuration file, no service provider to register and no migrations. It fits into an existing model layer, and the cost of adopting it is a trait and a rules array per model, while the cost of removing it is deleting the trait and the properties.
Editorial conclusion
Validating suits a Laravel application where models are written from several places, since a single trait on the base model makes invalid rows impossible rather than merely unlikely. It is a poor fit if you want form level validation with custom messages, since the readme is explicit that this is the framework's job, and a poor fit if you want the model to be savable in a dirty state during a multi-step flow, because the forceSave escape hatch then becomes the normal path. Before you adopt it, set the rules property on any base model you use, since the readme warns that a missing property leads to a confusing relationship exception, and decide per model whether throwing by default is right for your code.
Frequently asked questions
What does the watson/validating trait do?
It ensures an Eloquent model meets its validation rules before being saved. If the model is not valid it is not saved, and the validation errors are made available through the model's error methods. It supports multiple rulesets, injects the model's identifier into unique rules, and can raise exceptions on failed validations.
Which versions of Laravel does validating support?
The current branch supports Laravel 5.3 and later. The 2.x branch covers Laravel 5.0 to 5.2 and used an older validation exception contract that has since been deprecated, and the 0.10.x branch covers Laravel 4.2 and later, where the package was better suited to doing form validation rather than keeping model data valid.
How do I save a model even if it does not pass validation?
Use the forceSave method, which bypasses validation and returns the same result as calling save on a model without the trait. For a one-off call that raises an exception instead, the package offers saveOrFail and saveOrReturn.
Why does my model throw a relationship exception after adding the trait?
The readme explains that a base model using the trait needs the rules property set on it, or on each model that extends from it, or an empty array on the base model. Without it you will get a logic exception saying that a relationship method must return an object of the relation type, which gives no hint that a missing property is the cause.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/dwightwatson-validating)Was this useful?