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:

public function storeBlog(BlogRequest $request)
{
    // request has already been validated
}

So what does BlogRequest look like?

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…

    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…

$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.

// 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.

<?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 [];
    }

}