Sep 10, 2014 | laravel, 5.0, laravel 5

Laravel 5.0 - ValidatesWhenResolved

Series

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

!
Warning: This post is over a year old. I don't always update old posts with new information, so some of this information may be out of date.

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


This is part of a series of posts on New Features in Laravel 5.0:

  1. Sep 10, 2014 | laravel, 5.0, laravel 5
  2. Sep 10, 2014 | laravel, 5.0, laravel 5
  3. Sep 12, 2014 | laravel, laravel 5, 5.0
  4. Sep 20, 2014 | laravel, 5.0, laravel 5
  5. Sep 28, 2014 | laravel, laravel 5, 5.0
  6. Sep 30, 2014 | laravel, 5.0, laravel 5
  7. Oct 9, 2014 | laravel, 5.0, laravel 5
  8. Oct 10, 2014 | laravel, 5.0, laravel 5
  9. Oct 10, 2014 | laravel, 5.0, laravel 5
  10. Nov 20, 2014 | laravel, 5.0, laravel 5
  11. Jan 2, 2015 | laravel, 5.0, commands, laravel 5
  12. Jan 16, 2015 | laravel, laravel 5
  13. Jan 19, 2015 | laravel 5, laravel
  14. Jan 21, 2015 | laravel, events, 5.0, laravel 5
  15. Jan 26, 2015 | laravel, laravel 5
  16. Feb 1, 2015 | laravel, laravel 5
  17. Feb 14, 2015 | laravel 5, laravel, eloquent

Subscribe

For quick links to fresh content, and for more thoughts that don't make it to the blog.