Another thing where I feel like: “I must have done this a hundred times before, but it feels new”.
CRUDdy conventions
Laravel apps generally use forms a lot. They don’t have to, but a lot of the time you need to make some kind of CRUD (Create, Read, Update and Delete) interface for objects, and you so end up needing simple forms.
And Laravel uses “convention over configuration” to determine a lot of things. For example, rather than getting the developer to explicitly say “The User
model uses the users
table”, the framework assumes a default by convention:
By convention, the “snake case”, plural name of the class will be used as the table name unless another name is explicitly specified
https://laravel.com/docs/5.8/eloquent#eloquent-model-conventions
This is great because once you’ve learned the conventions, you can get on with coding without having to be explicit about a load of things.
Form validation is easy
And Laravel also lets you take a lot of shortcuts with creating forms. Such as the built-in validation. This lets you set a bunch of rules about the data submitted in a request, and the framework:
- Applies the rules
- Sanitises the inputs
- Redirects on failure and…
- …stores errors in the session.
This is great. Under certain circumstances. But you can’t just set the rules and go. You still need to pay attention to what’s going on.
The wrong error
One place that this beautifully-automatic system falls down is with error messages. It does its best, but really it needs a little more work.
Say you have a simple profile-updating form for a company object. You could have an input
named name
, but your label for this input is Company Name
:
<h3>Your company details</h3>
<div class="input-group">
<label for="name">Company name</label>
<input type="text" name="name" id="name">
</div>
You can validate this for, say, required-ness in your controller like this:
$validatedRequest = $request->validate([
'name' => 'required'
]);
// Do stuff with the request
The framework does its best to be clever here if validation fails, and it sill stores an error in the session that you can print in your template. BUT…the error will say that “The name field is required” (or similar – emphasis mine) and it will use the input’s name rather than the label. This is confusing for the user because the label for the input was “Company name” so the error should be “The Company name field is required”.
Custom error messages
You can create custom error messages for validations, but you have to use a more verbose form of validation. This is documented here, and I won’t go into too much detail, but you basically create an array of new messages like this:
$messages = [
'name.required' => 'You must provide a company name',
];
There are various ways to pass this to the validator – check the docs.
And this all works nicely. So far so good. But…
A complication
But there’s a slight complication for my application: I’m using form requests! These allow you to group authorisation, validation, and so on into a single class for handling the input, and to have lots of that taken care of for you. Their usage in a controller looks like this:
public function store(CompanyUpdate $request)
{
$validated = $request->validated();
}
Once you’ve created the CompanyUpdate
request class here, authorisation is automatic, and validation is taken care of in that single line.
But where, then, do I put my custom messages?!
The solution
I confess that custom error messages for form requests ARE documented, and I just missed them, which is why I’ve ended up writing this up for my own sanity.
To customise the error messages in your form request you can override the messages
method in your form request class:
public function messages()
{
return [
'name.required' => 'You must provide a company name',
];
}
Simple. But a kinda-tucked away, done-by-convention feature of form requests that’s really useful to know.
Hoepfully, like my future self, you’re now happy that:
- you need to customise your form validation messages; and
- you can now do this however you are doing your validations…
- …even if they are within a form request!