Laravel 5.0 - ValidatesWhenResolved

(This is part of a series of posts on New Features in Laravel 5.0.)

  1. Laravel 5.0 - Form Requests
  2. Laravel 5.0 - ValidatesWhenResolved
  3. Laravel 5.0 - Directory structure and namespace
  4. Laravel 5.0 - Route Caching
  5. Laravel 5.0 - Cloud File Drivers
  6. Laravel 5.0 - Method Injection
  7. Laravel 5.0 - Route Annotations (removed)
  8. Laravel 5.0 - Event Annotations (removed)
  9. Laravel 5.0 - Middleware (Filter-style)
  10. Laravel 5.0 - Environment Detection & Environment Variables
  11. Laravel 5.0 - Event Scheduling
  12. Laravel 5.0 - Commands & Handlers
  13. Upgrading from Laravel 4 to Laravel 5
  14. Bringing Whoops Back to Laravel 5
  15. Laravel 5.0 - Events & Handlers
  16. Laravel 5.0 - Generating Missing Events
  17. Laravel 5.0 - Custom Error Pages
  18. Laravel 5.0 - Eloquent Attribute Casting

In creating FormRequest, Taylor separated out both an interface (ValidatesWhenResolved) and a trait (ValidatesWhenResolvedTrait) relating to validating a method when its dependencies are resolved (via the IOC container).

To be entirely honest with you, I haven't come up with a really clever use case for this code outside of FormRequests. But I hope documenting it here will allow people smarter than me to see if it brings any particularly useful possibilities.

So, if you read my last blog post, you know that FormRequest objects, when injected (via the IOC with dependency injection), can cancel execution of the method they're running on. If my form doesn't validate, the POST route for that form gets cancelled by my FormRequest class.

So, it turns out that that the aspect of the FormRequest that triggers the IOC container calling its validation on resolution is now available as a separate interface called ValidatesWhenResolved. Because of this, you can now build your own class that similarly intercepts the request prior to your controller (or non-controller, theoretically) method loading and can choose to pass or fail the validation.

NOTE: The route/method isn't actually cancelled on a failed validation. The FormRequest object simply throws an HTTP Exception, which then either gives an error JSON response or a redirect. Theoretically, you could do the exact same thing without the interface simply by throwing an exception in the constructor after validating. But the interface cleans it up a lot by performing the validation in an named method.

The interface #

At the time of this post, this is what the interface looks like:

<?php namespace Illuminate\Contracts\Validation;

use Illuminate\Contracts\Container\Container;

interface ValidatesWhenResolved {

    /**
     * Validate the given class instance.
     *
     * @return void
     */
    public function validate();

}

As you can see, we're only obligated to provid a validate() method. And really, the benefit that this class provides--other than the additional knowledge we gain about a class purely by observing that it's fulfilling a particular contract--is that the validate() method is called when it's resolved from the IOC container. So let's try creating our own non-FormRequest class that implements this interface.

Non-FormRequest validation in controllers #

<?php namespace App\Http\Controllers;

use App\Random\RandomAccess;
use Illuminate\Routing\Controller;
use Response;

class ValidatedController extends Controller
{
    public function random(RandomAccess $ram)
    {
        return Response::make('You made it!');
    }
}

OK, so now we have a route. Let's try a non-FormRequest class:

<?php namespace App\Random;

use Exception;
use Illuminate\Contracts\Validation\ValidatesWhenResolved;
use Illuminate\Http\Request;

class RandomAccess implements ValidatesWhenResolved
{
    public function __construct(Request $request)
    {
        $this->request = $request;
    }

    public function validate()
    {
        // Test for an even vs. odd remote port
        if (($this->request->server->get('REMOTE_PORT') / 2) % 2 > 0)
        {
            throw new Exception("WE DON'T LIKE ODD REMOTE PORTS");
        }
    }
}

Now that controller method is being intercepted randomly with an exception (depending on whether your request port is even or odd, which is perhaps the most useless example of all time).

As you can see, there's no magic happening here. Whether validate() returns true or false doesn't matter. You could use the ValidatesWhenResolvedTrait to share some of the failedValidation() workflow you have with FormRequest, but with the class I wrote above you're simply throwing an exception.

FormRequest-style validation not in controllers #

We can also use this elsewhere, and we can use a FormRequest-style validator using the ValidatesWhenResolvedTrait. I have yet to find a use case for this, though, so I'll leave this section short and simple. You could do it... but I don't yet know why you would. :)

Real world examples #

I get it. You're not going to turn on a random exception toggler like my example. And in some ways, this may end up looking just like route filters. But I still suspect there's something really creative we could do here. Is there anything you're planning to inject into your controller anyway? Maybe make it implement this contract so it can auto-validate upon injection, rather than calling a validation method later.

Zaključak #

As you can tell, I'm just fishing around here to see if I can find any clever or creative uses for this. Got any great ideas? Pass them along: @stauffermatt.


Comments? I'm @stauffermatt on Twitter


Tags: laravel | 5.0 | laravel 5

Matt Stauffer headshot

Hi, I'm Matt Stauffer.

I'm partner & technical director at Tighten Co.

You can find me on Twitter at @stauffermatt


Like what you're reading?

I wrote an entire 450+ page book for O'Reilly: Laravel: Up and Running.

You can order the eBook or print book today.