When you want to validate Laravel input, you can do it out of the box using for example Form Request class. Let’s assume you want to allow creating tags. Each created tag should have at least 3 characters. Simple? Well, it looks really easy, but when we look at details, we can find a few issues to solve here.
So first we need to add valid routes:
1 2 |
Route::get('/tags/create','TagController@create'); Route::post('/tags','TagController@store')->name('tags.store'); |
Now let’s create our controller:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<?php namespace App\Http\Controllers; use App\Http\Requests\TagRequest; class TagController extends Controller { public function store(TagRequest $request) { dd($request->all()); // here we could create tag } public function create() { return view('tags.create'); } } |
Now simple view (we don’t care here about proper HTML tags just for simplicity):
1 2 3 4 5 6 7 8 9 |
@foreach ($errors->all() as $error) {{ $error }}<br /> @endforeach <form action="{{ route('tags.store') }}" method="post"> {{ csrf_field() }} <input type="text" name="name" /> <input type="submit"> </form> |
and finally validation request class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<?php namespace App\Http\Requests; class TagRequest extends Request { public function rules() { return [ 'name' => ['required','min:3'] ]; } public function authorize() { return true; } } |
Nothing fancy at all. Let’s try our validation. When you send form with empty input, you will get redirected to form with
The name field is required.
error. Everything fine so far.
Now, let’s fill in only one letter in input. You will be again redirected to form with
The name must be at least 3 characters.
error. Everything fine again.
Now, let’s try to fill input only with a few spaces. You will be again redirected to form with
The name field is required.
error. Again, no problem here.
So what’s the catch? Let’s try to fill in in input only one letter and a few more spaces. You will now see that you won’t be redirected to form any more and you will go to store()
method. Because we don’t put into database and just display the input, we will get something like this:
1 2 3 4 |
array:2 [▼ "_token" => "yTJHnTGauhym9DDQ3epXsGCiDabYtI5d8OeQAgii" "name" => "a " ] |
As you see we have 2 problem here. First, validation didn’t work as we expected. Spaces haven’t been trimmed to only one letter in this case was considered as valid (and we use rule that tag’s name should have at least 3 letters). The second problem is, that in 99% (or even more) cases we don’t care about white spaces and we want to trim input before putting data into database.
The first solution we can try is overriding default all()
method in our TagRequest
class. We could add new method into this class like so:
1 2 3 4 5 6 7 |
public function all() { $data = parent::all(); $data['name'] = trim($data['name']); return $data; } |
You could test it a few times with different input and you see it will work. Also when using dd()
in our controller input is trimmed. Is this the best solution to achieve this? For sure – it’s not.
First of all, you need to manually specify field in all()
method you want to trim and in addition you need to override this method in multiple request classes. There’s also one more problem with this. It would work only with all()
method for your request. This is fine with validation, but if you try to use in your controller (or anywhere else) input()
method to get individual field (for instance $request->input('name')
you will see again that this field is not trimmed any more.
There are probably 2 ways you can go with this – you can create Middleware that would trim the input or you could update default App\Http\Request
class to do this by default. Assuming in some cases you would like not to trim input, let’s go into 2nd solutions because it would make it easier to modify in case for any reason you would like to get original input without trimming applied.
All we need to do is to override original input()
method, because all other methods are build on top of this.
Default Laravel implementation looks like this:
1 2 3 4 5 6 |
public function input($key = null, $default = null) { $input = $this->getInputSource()->all() + $this->query->all(); return data_get($input, $key, $default); } |
we could modify default App\Http\Request
like so:
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 |
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; abstract class Request extends FormRequest { protected $trim = true; public function input($key = null, $default = null) { $input = $this->getInputSource()->all() + $this->query->all(); // replace input with trimmed input $input = $this->getTrimmedInput($input); return data_get($input, $key, $default); } /** * Get trimmed input * * @param array $input * * @return array */ protected function getTrimmedInput(array $input) { if ($this->trim) { array_walk_recursive($input, function (&$item, $key) { if (is_string($item) && !str_contains($key, 'password')) { $item = trim($item); } }); } return $input; } } |
this way, we can apply easily trim to all form request classes and in case we don’t want to apply trimming, the only we need to do is to set trim
property to false
in form request class like so:
1 |
protected $trim = false; |
As you see we excluded trimming password fields because someone might want to use extra spaces for his password and if we modify it, they won’t be able to log in.
There’s one more thing we should consider. What in case we would like to inject somewhere Request object with trimmed input when we don’t need to use validation at all? The solution is pretty simple – we need one extra Form Request class with empty rules()
method like so:
1 2 3 4 5 6 7 8 9 10 11 |
<?php namespace App\Http\Requests; class TrimmedRequest extends Request { public function rules() { return []; } } |
now in cases we want to use trimmed input, we need to inject this class and in case we don’t care about trimming, we can inject \Illuminate\Http\Request
object.
Great article. I use a technique very similar to this in my own solution , but instead of a static property I used an array of ‘untrimmable’ fields to handle inputs that should be left alone https://gist.github.com/msbrime/336a788c7cced2137bdc7896c1241239
Be aware that Laravel 5.4 trim strings by default using this middleware: https://github.com/laravel/laravel/blob/master/app/Http/Middleware/TrimStrings.php
Good work and simple to implement. I m working on large project and wants to apply this to my project but my concern is the array_walk_recursive() function.
Does it affect on my application performance??
because i m using on large application and not wanting any performance issue
I believe it won’t affect performance. Be aware that there since Laravel 5.4 there’s built-in middleware that might do it automatically if you apply it. See my previous comment for this article