Basic Form Request Validations With Laravel

Laravel allows us to validate requests before they even make it to our controllers. It does this using named form requests

Thus you might write a controller action like so:

1
2
3
4
public function storeBlog(BlogRequest $request)
{
// request has already been validated
}

So what does BlogRequest look like?

1
2
3
4
5
6
7
8
9
10
11
class BlogRequest extends BasicRequest
{
public function storeBlogRules()
{
return [
'title' => 'required',
'description' => 'required',
'owner_email' => 'required|unique:blogs,owner_email'
];
}
}

You can see what BasicRequest looks like below. If you’re doing a lot of crud applications you might benefit from having your own validations built into the request.

Problems with this approach

I’ve found 2 problems with this approach.

#1 Validation rules are stored in a Request object.

What happens if you want to re-use validation rules? A lot of people would store their validation rules on the Blog model to solve this problem. It just depends on how much you want to re-use your rules. Honestly, rules sometimes differ slightly enough to add another 100 lines of code in your Blog model, it may make sense to put them somewhere else. THe problem here is that you have to create a new BlogRequest just to get the validation rules. If you are running inside of a console Command, initiating a new BlogRequest object isn’t really that sensible.

#2 Sometimes rules need parameters

For example, sometimes we need to check for existance or uniqueness of columns on a table. Let’s go back to our Blog example example, and now we want to update a blog…

1
2
3
4
5
6
7
8
9
10
public function updateBlogRules( ... )
{
// HOW TO GET $blog->id IN HERE?

return [
'title' => 'required',
'content' => 'required'
'owner_email' => "unique:blogs,owner_email,id,{$blog->id}"
];
}

If we inspect $this inside of the BlogRequest object we notice that it has the route and request information all available to us. Thus we could make use of that and parse out route parameters to get the blog id. Something like…

1
$params = $this->route()->parameters()

But honestly, at this point, I feel the BasicRequest and BlogRequest has become too magical. It is almost easier just to keep validation inside the controller and skip all this extra request stuff.

1
2
3
4
5
6
7
8
9
10
// inside BlogsController.php
public function storeBlog(Request $request)
{
$this->validate($request, [
'title' => 'required',
...
]);

// is this really that much worse?
}

Sure, there is a place for Request validation, but I think having named request validation for every request in your application is going to cause more headaches and time than it will save.

Here is the Basic Request that BlogRequest extends (if you want to see it)

I saved this for last because it’s just a giant piece of code that you will either ignore or copy and paste.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

abstract class BasicRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
$method = 'authorize' . $this->findMethodName();

// we use policies for authorization so
// we can just ignore this for the most part
if (!method_exists($this, $method))
{
return true;
}

return $this->$method($this->route()->parameters());
}

/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
$method = lcfirst($this->findMethodName()) . 'Rules';

// we aren't strict on rules for get requests
if (strtolower($this->method()) == 'get' && !method_exists($this, $method))
{
return [];
}

return $this->$method($this->route()->parameters());
}

/**
* Find the method name for the current route
*
* @return string
*/
protected function findMethodName()
{
list($class, $method) = explode('@', $this->route()->getActionName());

return $method;
}

/**
* Default authorize
*
* @return bool
*/
public function authorizeIndex()
{
return true;
}

/**
* Default authorize
*
* @return bool
*/
public function authorizeCreate()
{
return true;
}

/**
* Default authorize
*
* @return bool
*/
public function authorizeShow()
{
return true;
}

/**
* Default authorize
*
* @return bool
*/
public function authorizeEdit()
{
return true;
}

/**
* Default authorize
*
* @return bool
*/
public function authorizeStore()
{
return true;
}

/**
* Default authorize
*
* @return bool
*/
public function authorizeUpdate()
{
return true;
}

/**
* Default authorize
*
* @return bool
*/
public function authorizeDestroy()
{
return true;
}

/**
* Default rules
*
* @return array
*/
public function indexRules($params)
{
return [];
}

/**
* Default rules
*
* @return array
*/
public function createRules($params)
{
return [];
}

/**
* Default rules
*
* @return array
*/
public function showRules($params)
{
return [];
}

/**
* Default rules
*
* @return array
*/
public function editRules($params)
{
return [];
}

/**
* Default rules
*
* @return array
*/
public function storeRules($params)
{
return [];
}

/**
* Default rules
*
* @return array
*/
public function updateRules($params)
{
return [];
}

/**
* Default rules
*
* @return array
*/
public function destroyRules($params)
{
return [];
}

}