12/08/2018, 13:41

New interesting features in laravel 5.2 and how to use them

Laravel 5.2 continues the improvements made in Laravel 5.1 by adding multiple authentication driver support, implicit model binding, simplified Eloquent global scopes, opt-in authentication scaffolding, middleware groups, rate limiting middleware, array validation improvements, and more. ...

Laravel 5.2 continues the improvements made in Laravel 5.1 by adding multiple authentication driver support, implicit model binding, simplified Eloquent global scopes, opt-in authentication scaffolding, middleware groups, rate limiting middleware, array validation improvements, and more.

Authentication Drivers / "Multi-Auth

In previous versions of Laravel, only the default, session-based authentication driver was supported out of the box, and we could not have more than one authenticatable model instance per application.

However, in Laravel 5.2, we may define additional authentication drivers as well define multiple authenticatable models or user tables, and control their authentication process separately from each other. For example, if our application has one database table for "admin" users and one database table for "student" users, we may now use the Auth methods to authenticate against each of these tables separately.

Authentication Scaffolding

Laravel already makes it easy to handle authentication on the back-end; however, Laravel 5.2 provides a convenient, lightning-fast way to scaffold the authentication views for our front-end. Simply execute the make:auth command on our terminal:

php artisan make:auth

This command will generate plain, Bootstrap compatible views for user login, registration, and password reset. The command will also update our routes file with the appropriate routes.

We have a layout (resources/views/layouts/app.blade.php) that is the core of this scaffold, and then a series of views that extend it:

  • welcome.blade.php - the public welcome page
  • home.blade.php - the dashboard for logged-in users
  • auth/login.blade.php - the login page
  • auth/register.blade.php - the register/signup page
  • auth/passwords/email.blade.php - the password reset confirmation page
  • auth/passwords/reset.blade.php - the password reset prompt page
  • auth/emails/password.blade.php - the password reset email

Our public page is still routed via routes.php:

Route::get('/', function () {
    return view('welcome');
});

And we now have a HomeController, which routes our dashboard:

class HomeController extends Controller
{
    /**
     * Show the application dashboard.
     *
     * @return Response
     */
    public function index()
    {
        return view('home');
    }
}

This is of course routed in routes.php in the web group. And notice that there's something else new there: The Route::auth() method:

Route::group(['middleware' => 'web'], function () {
    Route::auth();

    Route::get('/home', 'HomeController@index');
});

Route::auth()

The auth() method is a shortcut to defining the following routes:

// Authentication Routes...
$this->get('login', 'AuthAuthController@showLoginForm');
$this->post('login', 'AuthAuthController@login');
$this->get('logout', 'AuthAuthController@logout');

// Registration Routes...
$this->get('register', 'AuthAuthController@showRegistrationForm');
$this->post('register', 'AuthAuthController@register');

// Password Reset Routes...
$this->get('password/reset/{token?}', 'AuthPasswordController@showResetForm');
$this->post('password/email', 'AuthPasswordController@sendResetLinkEmail');
$this->post('password/reset', 'AuthPasswordController@reset');

We get FontAwesome, the Lato font, Bootstrap CSS, a basic hamburger-on-mobile responsive layout, jQuery, Bootstrap JS, and placeholders that are commented out for the default output CSS and JS files if we choose to use Elixir.

We also have a top nav that links us home, and links guests to either login or register, and links authenticated users to log out.

Implicit Model Binding

Implicit model binding makes it painless to inject relevant models directly into our routes and controllers. For example, assume we have a route defined like the following:

use AppUser;

Route::get('/user/{user}', function (User $user) {
    return $user;
});

In Laravel 5.1, we would typically need to use the Route::model method to instruct Laravel to inject the AppUser instance that matches the {user} parameter in our route definition. However, in Laravel 5.2, the framework will automatically inject this model based on the URI segment, allowing us to quickly gain access to the model instances we need.

Laravel will automatically inject the model when the route parameter segment ({user}) matches the route Closure or controller method's corresponding variable name ($$ser) and the variable is type-hinting an Eloquent model class.

Middleware Groups

When we are creating a site of any significant size in Laravel, our routes file will often get pretty large. One of the first things I do in a new site is group my routes by logically distinct sections like "admin", "auth", "public". Usually each of these groups get their own set of middleware—admin, for example, gets auth. Maybe the API group gets a different auth middleware, and it might get an API-specific rate limiter or something else.

Laravel 5.2 has introduced something called middleware groups, which are essentially a shortcut to applying a larger group of middleware, using a single key.

Middleware groups allow us to group several route middleware under a single, convenient key, allowing us to assign several middleware to a route at once. For example, this can be useful when building a web UI and an API within the same application. We may group the session and CSRF routes into a web group, and perhaps the rate limiter in the api group.

In fact, the default Laravel 5.2 application structure takes exactly this approach. For example, in the default AppHttpKernel.php file we will find the following:

/**
 * The application's route middleware groups.
 *
 * @var array
 */
protected $middlewareGroups = [
    'web' => [
        AppHttpMiddlewareEncryptCookies::class,
        IlluminateCookieMiddlewareAddQueuedCookiesToResponse::class,
        IlluminateSessionMiddlewareStartSession::class,
        IlluminateViewMiddlewareShareErrorsFromSession::class,
        AppHttpMiddlewareVerifyCsrfToken::class,
    ],

    'api' => [
        'throttle:60,1',
    ],
];

Then, the web group may be assigned to routes like so:

Route::group(['middleware' => ['web']], function () {
    //
});

However,we need to keep in mind the web middleware group is already applied to our routes by default since the RouteServiceProvider includes it in the default middleware group.

Rate Limiting

Rate limiting is a tool—most often used in APIs—that limits the rate at which any individual requester can make requests.

That means, for example, if some bot is hitting a particularly expensive API route a thousand times a minute, our application won't crash, because after the nth try, they will instead get a 429: Too Many Attempts. response back from the server.

Usually a well-written application that implements rate limiting will also pass back three headers that might not be on another application: X-RateLimit-Limit, X-RateLimit-Remaining, and Retry-After (we'll only get Retry-After if we've hit the limit). X-RateLimit-Limit tells us the max number of requests we're allowed to make within this application's time period, X-RateLimit-Remaining tells us how many requests we have left within this current time period, and Retry-After tells us how many seconds to wait until we try again. (Retry-After could also be a date instead of a number of seconds).

A new rate limiter middleware is now included with the framework, allowing us to easily limit the number of requests that a given IP address can make to a route over a specified number of minutes. For example, to limit a route to 60 requests every minute from a single IP address, we may do the following:

Route::get('/api/users', ['middleware' => 'throttle:60,1', function () {
    //
}]);

Array Validation

Validating array form input fields is much easier in Laravel 5.2. For example, to validate that each e-mail in a given array input field is unique, we may do the following:

$validator = Validator::make($request->all(), [
    'person.*.email' => 'email|unique:users'
]);

Likewise, we may use the * character when specifying our validation messages in our language files, making it a breeze to use a single validation message for array based fields:

'custom' => [
    'person.*.email' => [
        'unique' => 'Each person must have a unique e-mail address',
    ]
],

Form array validation simplifies the process of validating the somewhat abnormal shape of data HTML forms pass in when the array syntax is used. A common use case is when we allow a user to add multiple instances of the same type on one form.

Let's imagine we have a form where a user is adding a company, and as a part of it they can add as many employees to the company as they want. Each employee has a name and a title.

Here's our HTML; imagine that we have some JavaScript that creates a new "employee" div every time we press the "Add another employee" button so they user can add as many employees they want.

<form>
    <label>Company Name</label>
    <input type="text" name="name">

    <h3>Employees</h3>
    <div class="add-employee">
        <label>Employee Name</label>
        <input type="text" name="employee[1][name]">
        <label>Employee Title</label>
        <input type="text" name="employee[1][title]">
    </div>
    <div class="add-employee">
        <label>Employee Name</label>
        <input type="text" name="employee[2][name]">
        <label>Employee Title</label>
        <input type="text" name="employee[2][title]">
    </div>
    <a href="#" class="js-create-new-add-employee-box">Add another employee</a>

    <input type="submit">
</form>

If we fill out that form and submit it, this is the shape of the $$POST:

array(2) {
  ["name"]=>
  string(10) "Acme, Inc."
  ["employee"]=>
  array(2) {
    [1]=>
    array(2) {
      ["name"]=>
      string(10) "Joe Schmoe"
      ["title"]=>
      string(11) "Head Person"
    }
    [2]=>
    array(2) {
      ["name"]=>
      string(18) "Conchita Albatross"
      ["title"]=>
      string(21) "Executive Head Person"
    }
  }
}

As we can see, we get an employee "object". And it contains an array of the IDs that we passed in, with the key/value pairs of "fieldname" => "user provided field value". But how do we validate this? Prior to 5.2, it's a bunch of manual work. Now, Laravel understands this nesting structure and can validate against it uniquely.

So, how do we do it? Let's take a look at a normal validator:

// CompaniesController.php
    public function store(Request $request)
    {
        $this->validate($request->all(), [
            'name' => 'required|string'
        ]);
        // Save, etc.
    }

And now let's add validation for our company employee fields:

    // CompaniesController.php
    public function store(Request $request)
    {
        $this->validate($request->all(), [
            'name' => 'required|string',
            'employee.*.name' => 'required|string',
            'employee.*.title' => 'string',
        ]);
        // Save, etc.
    }

Now we're validating every employee[][name] and employee[][title] uniquely, with pretty much no effort on our part. Beautiful.

Bail Validation Rule

A new bail validation rule has been added, which instructs the validator to stop validating after the first validation failure for a given rule. For example, we may now prevent the validator from running a unique check if an attribute fails an integer check:

$this->validate($request, [
    'user_id' => 'bail|integer|unique:users'
]);

Eloquent Global Scope Improvements

In previous versions of Laravel, global Eloquent scopes were complicated and error-prone to implement; however, in Laravel 5.2, global query scopes only require us to implement a single, simple method: apply.

So there you have some of the cool new features of laravel 5.2. Don't forget to check the official documentation for more comprehensive overview about the changes in the latest release!

0