Changes in upcoming Laravel 5.5 LTS release

Introduction

Laravel 5.5 will be next LTS release after Laravel 5.1 LTS. We should expect it until end of August 2017 probably around 28th (Laracon EU). But what should we expect exactly comparing to Laravel 5.4?

One of bigger changes is initial requirement. Laravel 5.5 will need PHP 7.0+. It won’t work on PHP 5.6, sorry. Looking at http://php.net/supported-versions.php it should be not a big problem as PHP 7 is with us already a while.

Obviously I won’t cover all the changes in this article but only focus on some of them, especially on those I think are the most interesting and helpful for Laravel developers

 

Frontend changes

Frontend presets

A new command preset was added that help you to start with project with default scaffolding. You should pass as parameter for this command one of:

  • bootstrap
  • vue
  • react
  • none

depending on your needs. This command in general remove other preset files and create new ones according to type you passed so you are ready to go and you don’t need to do it manually.

Blade custom if directive

Now you can easily add custom if directive to your Blade templates which help you make your Blade templates easier to read.

For example let assume you want to display some text on your dev site just to make sure users/clients will know this is test site only.

You can register your directive like this:

Blade::if('develop', function() {
   return app()->environment('develop');
});

And later in your view you can use:

@develop
   This site is only for testing purposes. All accounts you create will be removed
@else
    This is real site
@enddevelop

Obviously @else is optional here, so in above case probably you won’t use it at all.

Responses

Simple pages

Sometimes what you need is just to display very basic page. You could just render your view in your web.php file but in such case you couldn’t use route caching. So generally what you need is create controller and put method where you only return render your view. But now you can do it much simpler like this:

Route:view('/regulations','regulations', ['title' => 'Terms of service'])

As 1st argument you use url, as 2nd name of your view and as 3rd optional parameters array. As simple as that and it works without any problems with route caching

Redirections

Similar thing is with redirections. Sometimes it happens you created url and you moved it to new url. All you need is now is your routes to add:

Route::redirect('/old-url', '/new-url');

to make sure users will be redirected from old url to new one.

API responses

If you write simple APIs you might get interested in Responsable interface that you can use to return any object for example in Json format.

You can create route like this:

Route::get('/json', function () {
    return new App\Http\Responses\UserResponse(App\User::findOrFail(2));
});

and you can now define your custom response class like this:

<?php

namespace App\Http\Responses;

use App\User;
use Illuminate\Contracts\Support\Responsable;

class UserResponse implements Responsable
{
    /**
     * @var User
     */
    private $user;

    public function __construct(User $user)
    {
        $this->user = $user;
    }

    public function toResponse($request)
    {
        return response()->json([
            'email' => $this->user->email,
            'name' => $this->user->name,
        ]);
    }
}

For sure this is a step forward and will work with simple scenarios but I personally think for more complex ones probably you will still need use some custom transformers together with Fractal for example.

Mail/Notification changes

Preview mailables

Almost in every application you use e-mails and you need to verify if they are fine. Most of us probably were using mailtrap to send e-mail and view how it looks like. Now you don’t have to do it. If you use mailables all you need to do now is something like this:

Route::get('/preview', function() {
    return new \App\Mail\TestEmail(App\User::find(1));
});

and now you will see in your browser e-mail content. Pretty straightforward way to test your e-mails, don’t you think?

As you see above e-mail looks by default the same as e-mail you would get to your e-mail and it’s much easier now to test your changes in browser. Of course I would still recommend to test it later in some real e-mail clients to make sure everything looks as it should in real e-mail client.

Sending notifications

In case you use notifications and you wanted to send for example e-mail notification you had to send it to existing object (for example user in database). But now you don’t have to do it any more. You can just pass channel and channel delivery address. For example when you want to notify someone that new user was registered, you can now use:

Notification::route('mail, 'test@example.com')->notify(new App\Notification\UserWasRegistered);

So the employee or other 3rd party stuff don’t have account in your website just to get notification.

Requests changes

Custom validation rules

Another interesting feature is creating custom validation rules To validate your e-mail using some custom algorithm you can now use:

$request->validate([

    'email' => [

        'required',

        new \App\Rules\UserEmail(),

    ],

and put any custom loginc in your UserEmail like so:

<?php

namespace App\Rules;

use App\User;
use Illuminate\Contracts\Validation\Rule;

class UserEmail implements Rule
{
    /**
     * Create a new rule instance.
     *
     */
    public function __construct()
    {
        //
    }
    /**
     * Determine if the validation rule passes.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        return (bool) User::where('email', $value)->first();
    }
    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        return 'User e-mail is not valid.';
    }
}

This is far better solution than the one we know from Laravel 5.4 and earlier where you need to extend Validator. Obviously in above example we could use exists built-in rule but this is just a quick demo what we can do.

Validated fields and object persisting

Another cool feature is that when you run validation, in case validation passes you will get only fields that were really validated.

For example when you create validation like this:

$validated = $request->validate([
    'email' => [
        'required',
    ],
    'something' => [
        'required',
    ]
]);

and your url looked like this: search?email=testing&something=validated&another=method (notice another extra parameter)

In $validated you will have

array:2 [▼

  "email" => "testing"

  "something" => "validated"

]

although in request there was also another field.

This simplify creating/updating objects assuming you have validation rules for all fields that should be persisted (in fact you should).

Now, you can could create your  your sample method like this:

Route::get('/search', function(Illuminate\Http\Request $request) {
    $validated = $request->validate([
        'email' => [
            'required',
        ],
        'name' => [
            'required',
        ],
    ]);
    App\Search::create($validated);
});

This is much cleaner that specifing fields again. In past you should rather use

App\Search::create($request->only('email','name'));

instead of

App\Search::create($validated);

in such case.

It seems as a big improvement for me. As I’m using only Form requests for validation to get validated data from FormRequest you need to use:

$request->validated())

so your action could look like this:

Route::get('/search', function(App\Http\Requests\Search $request) {

    App\Search::create($request->validated());

});

Time-saving changes

Better error display using Whoops package

When you develop application when you break something you would probably get useful information where the errors is. In Laravel 4 Whoops package used for this but in Laravel 5 it was removed. Now it’s coming back again to Laravel 5.5

Below you can see how error page for same error looks like in Laravel 5.4 and in Laravel 5.5

 This is how it looks like in Laravel 5.4

This is how it looks like in Laravel 5.5

What version do you prefer? For me the new Laravel 5.5 version is much more developer friendly.

Re(freshing) migrations

In Laravel 5.5 new migrate:fresh command appear. It allows to delete all the tables from database and will run all existing migrations. Why this is so powerful? In Laravel 5.4 and earlier he have only migrate:refresh command which rollbacks all migrations and run them again. However if you don’t have your down method in migrations or you have some errors or foreign key constraints, migrate:refresh command might probably fail in some scenarios and you need to remove tables from your database manually.

For me it was very annoying and this was because I review daily at least a few Pull requests in different branches of same repository. So if in one branch there is some new migration and I run this and later I switch to other branch and want to refresh database, migrate:refresh command will fail because migration cannot be rolled back as it’s not existing at all in other branch. But in Laravel 5.5 I will only run migrate:fresh command and that’s it. It will work without any problem!

Commands auto-discovery

When we talk about commands,  another useful feature was added to Laravel 5.5. In past when you created command, you had to add this new command  manually in app/Console/Kernel.php to make it really work. For me it was quite annoying to do that over and over and now it has been improved. If you look at mentioned file you will see new line:

$this->load(__DIR__.'/Commands');

It runs automatically searching all commands in app/Console/Commands directory so you don’t need to do it manually any more. You just create new command and can run it without looking at Kernel any more.

Packages auto-discovery

Another useful improvement that will save each developer time is packages auto-discovery. In Laravel 5.4 and before whenever you install package you need to manually repeat the same steps – open config/app.php and add provider into providers section and if there are any facades you have to add them into aliases section. Now you don’t need to do it any more!

If package you use was updated to work with Laravel package auto-discovery all you need to do is require it via composer. Laravel will automatically register this package providers and facades. Finally it will create bootstrap/cache/packages.php file with all auto-discovered packages.

Testing

Migrations and transactions

New RefreshDatabase trait was added into Laravel 5.5. It will ensure that all existing migrations will be removed and it will migrate database again before running tests, so you will have everything migrated. In Laravel 5.4 and before when you were running your tests, you had to remember always to refresh your migrations in test database whenever you changed your database structure. Now you don’t need to do it any more. Obviously this RefreshDatabase is using migrate:fresh command under the hood.

Additionally this trait is automatically will make sure your test will run in transaction, so you don’t need to use DatabaseTransactions trait any more.

Displaying exceptions when writing tests

New withoutExceptionHandling method was added to be used when writing tests. Now if you get 500 exception you don’t need to look in your log any more to know what really happened. You only need to run this method before exception happens, rerun test and you will see the actual error in your console. I’m pretty sure it was inspired by Adam Wathan method we has already presenting a while ago ( https://gist.github.com/adamwathan/125847c7e3f16b88fa33a9f8b42333da )

Multiple Model factories

Laravel 5.5 allows you to create model factories in separate files. You have now  make:factory command to help you with this. This is quite similar to what we do in Devpark – we divide our code to modules using modular package (see http://devpark.pl/zastosowanie-modulow-w-laravel-5/?lang=en )  and create separate model factory for each module. But if you don’t use modules it will be much better to have smaller model factories instead of having one single file with dozens definitions for multiple models.

Carbon automatic clean-up after running test

Another small improvement is that Laravel will automatically restore current time for Carbon library after each test. In past whenever you used

Carbon::setTestNow($sampleTime);

you had to add into tearDown method

Carbon::setTestNow();

to make sure in the following tests time will be handled in valid way. In fact I was going to make Pull request with this improvement but it seems someone was quicker.

Disabling selected middlewares

Also quite handy improvement was added to disabling middlewares. In Laravel 5.4 and before you had to use all the middlewares or if you wanted disable any of them you had to disable all of them.

So as a simplest solution you might have code similar to this in your middleware:

if ($this->app->environment('testing')) {
    return $next($request);
}
// here the logic of middleware for your application

I was using such solution and didn’t feel well with this. Now you can do it much simpler for example like this:

$this->withoutMiddleware([TrimStrings::class]);
$response = $this->get('/?example= abc ');
$response->assertStatus(200);
$this->assertEquals(' abc ', $response->getContent());

In above test we just disabled TrimStrings middleware but all other middleware are enabled.

Eloquent

There were many changes made to Eloquent (see resources below for details) but what I like the most are extra methods wasChanged(), getChanges() or hasChanges() that help you track model changes that were really made.

For example if you want to allow your user change e-mail, usually you would like to send e-mail notification or maybe activation e-mail. In Laravel 5.5 you can do it very simple like this:

Route::get('/', function() {
    $data = [
        'email' => 'email@example.com',
    ];
    $user = App\User::findOrFail(1);
    $user->update($data);
    
    if ($user->wasChanged('email')) {
        Mail::to($user)->send(new EmailChanged($user));
    }
});

but in Laravel 5.4 and before it wasn’t so easy. You had to write code like this:

Route::get('/', function() {
    $data = [
        'email' => 'email@example.com',
    ];
    $user = App\User::findOrFail(1);
    $user->fill($data);
    
    $emailChanged = $user->isDirty('email');
    
    $user->save();

    if ($emailChanged) {
        Mail::to($user)->send(new EmailChanged($user));
    }
});

Summary

That’s all for now. Hope you enjoy the changes that were made in upcoming Laravel 5.5 release.  Below you will find additional resources if you want to dig further into changes upcoming to next Laravel LTS.


Other Laravel 5.5 resources

https://streamacon.com/video/laracon-us-2017/day-1-taylor-otwell

https://laracasts.com/series/whats-new-in-laravel-5-5

https://github.com/laravel/framework/blob/master/CHANGELOG-5.5.md

https://laravel.com/docs/5.5/upgrade