Laravel 6.0 is here!

Taylor Otwell, the creator of Laravel, has been teasing the 6.0 release for a few months now and today it has finally officially dropped. Let's take a look at what's changing and how you can start using it right away.

What's new?

Semantic versioning

Although the jump from 5.8 to 6.0 is a major release, Otwell has mentioned that in terms of new features and changes, this update will feel more like moving from 5.8 to 5.9.

Laravel has had huge updates with every major version release, which is typical in a young framework. All the updates throughout 5.x laid the groundwork for a stable and robust framework. Going forward with 6.0, you won't have to worry as much about huge breaking changes and rewrites with every new major release. Because of this new stability, the Laravel framework package is officially adopting the more traditional semantic versioning.

Job middleware

Instead of manually adding extra boilerplate code around your jobs, you can now extract it into middleware and then just attach it to the job!

This example shows how you could add rate limiting to a job prior to 6.0. You would add it to the job's handle() method for each job that requires it.

// app/Jobs/JobToBeThrottled.php
// Execute the job
public function handle() {
  // allow one job to process every 5 seconds
  Redis::throttle('key')->block(0)->allow(1)->every(5)->then(function () {
    // Handle the job
  }, function () {
    // Could not obtain lock
    return $this->release(5);
  });
}

With this new release, we can extract that code out of handle() and move it into a job-specific middleware. You can create a directory in app/Jobs/Middleware (or wherever you want, Laravel isn't opinionated about this) and add a file for the middleware in that folder.

// app/Jobs/Middleware/RateLimited.php
...
namespace App\Jobs\Middleware;
use Illuminate\Support\Facades\Redis;

class RateLimited {
  // Process the queued job.
  public function handle($job, $next) {
    Redis::throttle('key')
      ->block(0)->allow(1)->every(5)
      ->then(function () use ($job, $next) {
        // Lock obtained
        $next($job);
      }, function () use ($job) {
        // Could not obtain lock
        $job->release(5);
      });
  }
}

Now you can attach this middleware to the job and it will run through the middleware before processing. If you're familiar with route middleware, this process is very similar.

// app/Jobs/JobToBeThrottled.php
use App\Jobs\Middleware\RateLimited;

// get the middleware that the job will use
public function middleware() {
  return [new RateLimited];
}

public function handle() {
  // handle the job
}

This will keep your job files small, easier to read, and focused on their specific task. It will also allow you to easily reuse middleware throughout jobs.

Lazy collections

Laravel 6.0 introduces lazy collections, which will allow you to keep memory usage low when dealing with heavy data.

If you're working with eloquent models, you can choose to load one model into memory at a time instead of everything at once by using the cursor() method instead of all().

// This loads all eloquent models into memory at the same time
// This may be a very large number if you have thousands of posts

$posts = App\Post::all()->filter(function ($post) {
    return $post->id > 500;
});

By switching from all() to cursor(), only one eloquent model is loaded into memory at a time because cursor() uses the new LazyCollection instance.

$posts = App\Post::cursor()->filter(function ($post) {
  return $post->id > 500;
});

foreach ($posts as $post) {
  echo $post->id;
}

Eloquent subquery enhancements

Eloquent has always made it easier to run complex queries. This new release gives you even more help when it comes to running subqueries, or a query within a query, in a single call. This is useful when you need to select information from two tables with a relationship. In Laravel 5.x you were sometimes limited with what you could do in a subquery and would often end up being forced to chain a query using DB::raw().

The addSelect method has been added to subqueries, which should eliminate a lot of that pain! Eloquent subqueries will also now have access to orderBy.

For this example, imagine you have 2 tables: hotels and reservations. You want to know the most recent room type that was booked for a specific hotel. Instead of doing two separate eloquent queries, you can now do this:

use App\Reservation;
use App\Hotel;

return Hotel::addSelect(['last_booked_room' => Reservation::select('room_type')
  ->whereColumn('hotel_id', 'hotels.id')
  ->orderBy('created_at', 'desc')
  ->latest()
  ->limit(1)  
])->get();

Improved custom authorization responses

If you're using Laravel's out-of-the-box authentication with gates for authorization, Laravel 6.0 introduces a new method Gate::inspect. This makes it easier to provide custom messages to users during authorization requests, such as a specific error message if their request was denied.

For example, say you have the method below that determines if a user is authorized to edit a post. Before 6.0, it was difficult to retrieve this message and display it back to the user.

With the introduction of the Gate::inspect() method, you can now easily retrieve the full custom response to send back to the user.

If you want to limit the editing of posts to only users who have the role admin, you may have created a gate definition for editing posts.

// App/Providers/AuthServiceProvider.php
...

public function boot()
{
    $this->registerPolicies();

    // Define the gate that determines who can edit posts
    Gate::define('edit', function ($user) {
        return $user->isAdmin
          ? Response::allow()
          : Response::deny('You must be an administrator to edit posts.');
    });
}

Normally you would just use the allows or denies method to see if that user should be authorized to perform an action. The response depends on the boolean response from the Gate::define method defined above. The following example will not give you the custom response, just the boolean.

if (Gate::allows('edit')) {
  // let the user edit
}

if (Gate::denies('edit')) {
  // let the user edit
}

If you want to access the more detailed custom denial response, you can now use Gate::inspect.

// get the full authorization response returned by the gate
$response = Gate::inspect('edit');

if ($response->allowed()) {
  // Let the user edit the post
} else {
  // Display the denial message
  // 'You must be an administrator to edit posts.'
  echo $response->message();
}

Auth0 also provides a simple and quick way to add authorization to your Laravel API.

"Laravel 6.0 is here! Check out what's new and what's changing!"

Laravel UI composer package

Another update to be aware of is the extraction of the laravel/ui package. Front-end scaffolding will still exist, it's just not included by default. This means in a new Laravel 6.0 application, you won't see any Vue or Bootstrap scaffolding.

The package now includes presets for React, Vue, and Bootstrap. In a tweet back in May, Otwell mentions that he's considering adding a Tailwind option as well!

If you'd still like it included, you can bring it back in with composer require laravel/ui.

New branding

With every major release, there has also been new branding that comes along with it, and 6.0 is no exception!

Laravel 6.0 new logo

In addition to the new logo above, you can also find a fresh new redesign of the Laravel.com website.

Laravel 6.0 new website

Laravel Vapor

And last but certainly not least, the release of Laravel 6.0 also includes the launch of a much-anticipated product in the Laravel ecosystem - Laravel Vapor.

Laravel Vapor dashboard

Before this release, most people used Laravel Forge to provision and deploy their apps. With Forge, you could connect your chosen server (Digital Ocean, AWS, etc.) and Forge would provision it specifically for your Laravel application. This is awesome of course, but you still had to manage updates for yourself.

Laravel Vapor does all of this and more. Instead of managing and updating servers for your Laravel application on your own, Vapor is completely serverless!

This doesn't mean there are no servers involved, it just means you don't have to deal with them. Another benefit is that you only pay for what you use. Instead of paying a flat monthly rate, you're only charged when a request is made to your application. This also means you don't have to worry about scaling, as it will automatically be done for you.

Here are some more awesome features that Vapor provides:

  • On-demand scaling - Can execute jobs immediately as they come in
  • Powered by AWS
  • Ready for bursts of traffic
  • Zero downtime during deployment
  • Extremely fast
  • Multiple environments with free vanity URL for testing (https://snowy-hurricane-12349834324432.vapor.build)
  • Maintenance mode
  • Searchable logs
  • Create and scale your databases from Vapor
  • Backup and restore databases
  • Metrics and monitoring with notifications
  • Purchase domains and manage DNS from Vapor dashboard
  • Automatically added SSL certificates
  • Jobs, workers, PHP updates, and more are handled behind the scenes
  • Deployment easily configured with a simple vapor.yaml file

The cost for all of this is $39 per month or $399 per year with unlimited team members and projects. Keep in mind, you still have to pay for your own AWS costs beyond that.

Upgrading to 6.0

The Laravel documentation estimates it should take about one hour to upgrade from 5.8 to 6.0. Your project may have other dependencies and external packages that can increase that time, so keep that in mind when you decide to upgrade.

How to upgrade to Laravel 6.0

In your composer.json file, change the Laravel framework dependency from 5.8.* to ^6.0. If you're on a version earlier than 5.8, it's recommended to upgrade up to 5.8 before making the switch to 6.0.

// In composer.json
"laravel/framework": "^6.0",

Then in your terminal run:

composer update

Laravel Shift

Another awesome way to manage your upgrades, especially for bigger applications, is to use Laravel Shift. Shift is an automated tool that will do your upgrade for you.

It works like this:

  • Grant Shift access to your Laravel repository
  • Choose what version of Laravel you're on and what you want to upgrade to
  • Shift creates a new branch and modifies what needs to be changed in your code
  • Shift creates a pull request so that you can test and decide if it should be merged

The process is very simple and a great option if simply changing the package version in your composer.json didn't work for you. The service isn't free, but the one-time fee is very low.

You can find the upgrade instructions for 6.0 here.

Do I need to Upgrade?

Laravel 6.0 is the latest Long Term Support release, which means bug fixes will be guaranteed for two years and security fixes for three years. The previous LTS release with these guarantees was Laravel 5.5.

This table from the Laravel website outlines the current support schedule:

Laravel support schedule

With bug fixes for the previous LTS release ending very soon, it may be worth it to upgrade to 6.0 so that you know your project will be supported for a couple more years.

Aside: Securing Laravel APIs with Auth0

Securing Laravel APIs with Auth0 is very easy and brings a lot of great features to the table. With Auth0, we only have to write a few lines of code to get:

Sign Up for Auth0

You'll need an Auth0 account to manage authentication. You can sign up for a free account here. Next, set up an Auth0 API.

Set Up an API

Go to APIs in your Auth0 dashboard and click on the "Create API" button. Enter a name for the API. Set the Identifier to a URL(existent or non-existent URL). The Signing Algorithm should be RS256.

Create API on Auth0 dashboard

Create API on Auth0 dashboard

We're now ready to implement Auth0 authentication on our Laravel backend API.

Dependencies and Setup

Install the laravel-auth0 package via composer like so:

composer require auth0/login:"~5.0"

Generate the laravel-auth0 package config file like so:

php artisan vendor:publish

After the file is generated, it will be located at config/laravel-auth0.php. Ensure you replace the placeholder values with the authentic values from the Auth0 Admin Dashboard. Double check your values with laravel-auth0.

Your .env file should have the AUTH0_DOMAIN, AUTH0_CLIENT_ID, AUTH0_CLIENT_SECRET and AUTH0_CALLBACK_URL values like so:

AUTH0_DOMAIN=kabiyesi.auth0.com
AUTH0_CLIENT_ID=xxxxxxxxxxxxxxxxxx
AUTH0_CLIENT_SECRET=xxxxxxxxxxxxxxxxx
AUTH0_AUDIENCE=http://mylaravelapi.com
AUTH0_CALLBACK_URL=null

Activate Provider and Facade

The laravel-auth0 package comes with a provder called LoginServiceProvider. Add this to the list of application providers.

// config/app.php
'providers' => array(
    // ...
    \Auth0\Login\LoginServiceProvider::class,
);

If you would like to use the Auth0 Facade, add it to the list of aliases.

// config/app.php
'aliases' => array(
    // ...
    'Auth0' => \Auth0\Login\Facade\Auth0::class,
);

The user information can now be accessed with Auth0::getUser(). Finally, you need to bind a class that provides a user (your app model user) each time the user is logged in or an access_token is decoded. You can use the Auth0UserRepository provided by this package or you can build your own class.

To use Auth0UserRepository, add the following lines to your app's AppServiceProvider:

// app/Providers/AppServiceProvider.php
public function register()
{
    $this->app->bind(
      \Auth0\Login\Contract\Auth0UserRepository::class, 
      \Auth0\Login\Repository\Auth0UserRepository::class
    );
}

Configure Authentication Driver

The laravel-auth0 package comes with an authentication driver called auth0. This driver defines a user structure that wraps the normalized user profile defined by Auth0. It doesn't actually persist the object but rather simply stores it in the session for future calls.

This is adequate for basic testing or if you don't have a requirement to persist the user. At any point you can call Auth::check() to determine if there is a user logged in and Auth::user() to retreive the wrapper with the user information.

Configure the driver in config/auth.php to use auth0.

// app/config/auth.php
// ...
'providers' => [
    'users' => [
        'driver' => 'auth0'
    ],
],

Secure API Routes

Your API routes are defined in routes/api.php for Laravel 5.3+ apps.

// routes/api.php

<?php

use Illuminate\Http\Request;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

Route::get('/public', function (Request $request) {
  return response()->json(["message" => "Hello from a public endpoint! You don't need any token to access this URL..Yaaaay!"]);
});

Route::get('/wakanda', function (Request $request) {
  return response()->json(["message" => "Access token is valid. Welcome to this private endpoint. You need elevated scopes to access Vibranium."]);
})->middleware('auth:api');

Now, you can send a request to your protected endpoint which includes an access_token.

curl --request GET \
  --url http://localhost:8000/api/wakanda \
  --header 'authorization: Bearer <ACCESS TOKEN>'

Once a user hits the api/wakanda endpoint, a valid JWT access_token will be required before the resource can be released. With this in place, private routes can be secured.

More Resources

That's it! We have an authenticated Laravel API with protected routes. To learn more, check out the following resources:

Recap

Laravel has brought a lot of enthusiasm and excitement to the PHP world and it's been great to see the framework and accompanying products continue to improve.

With the introduction of semantic versioning, you can upgrade between major releases without worrying about huge breaking changes. This switch signifies more than just versioning, it shows that after all the improvements that came in 5.x, the framework is now at a stable point going forward!

In addition to semantic versioning, 6.0 is bringing speed improvements to accessing collections, additions to eloquent subquery selections, the introduction of job middleware, and more.

The full documentation is out now, so take a look if you'd like more information about the new release!

And if you're interested in adding authentication to your Laravel 6.0 application, Auth0 provides an incredibly simple SDK that can be integrated in minutes.