The New and Exciting Features of Laravel 9
Laravel is an open-source PHP web application framework that provides a structure and starting point for building both simple and complex web apps. Developers love it because of its intuitiveness, versatility, and scalability.
Laravel handles essential functions such as app security, permission control, routing, and dependency injection in the background, making it one of the most widely used PHP frameworks today. This article will focus on Laravel 9, released in February 2022, including its new features and changes. However, before diving into that, the article shares some background on what makes Laravel a useful framework. Note: Laravel 10 is already available, so it’s recommended to check the official release notes for the latest Laravel framework and ecosystem changes.
Table Of Contents
Three Reasons Why You Should Use Laravel
There are many reasons to choose Laravel for your next project, but these are my top three:
Significant Changes to Consider Before Updating Your Laravel 8 Project to Laravel 9
Before we review Laravel 9’s new features, it’s worth noting some important changes that you’ll need to consider if you’re currently using Laravel 8 for a project. If you’re starting a new project, most of the information in this section won’t affect you.
PHP Version Requirement
The new version of the framework requires a minimum PHP version of 8.0, while the previous version, Laravel 8, requires a minimum PHP version of 7.3. To update your project to run with Laravel 9, you’ll need to change the PHP version you’re running on the hosting server and check that all third-party packages used on your app satisfy the requirement.
There are also some PHP 8 features that may be affected by upgrading to Laravel 9, most notably of which is named arguments. When working with Laravel, you should know that its backward compatibility guidelines don’t cover named arguments. Essentially, this means you should avoid using named arguments on methods from the Laravel core because the names of the parameters can change without warning. You can read more in Laravel’s release notes.
Symfony Mailer
Since SwiftMailer library is no longer maintained, the Laravel team decided to drop it from the framework and implemented the new version called Symfony Mailer. This library is probably well known by all Symfony developers, and pretty easy for any PHP developer to learn. There are no significant changes from a technical perspective, but it’s worth checking the upgrade guide to make sure everything will work fine.
FlySystem 3.x
This dependency is responsible for all interactions with the filesystem. If you haven’t heard of FlySystem, you may recognize it when I tell you that it supercharges the `Storage` facade, which is the class responsible for handling all filesystem interactions in a Laravel app.
If you’re migrating to Laravel 9, there’s a good chance that this change will impact your app. While the Laravel team made this transition as seamless as possible, it’s still a good idea to review the upgrade guide.
The last version of Flysystem doesn’t change the concept of the package, but it improves the developer experience significantly. Because it’s a complete rewrite, it makes the code better and easier to maintain.
The Server.php File
This file is only used to run `PHP artisan serve` command. In the new version, it was moved to the core of the framework, meaning you can delete this file from the root of your project to make it cleaner.
What’s New in Laravel 9?
As I said, the changes described above are much more critical for people migrating from Laravel 8 to 9 than to newcomers or projects that started in the new version. Let’s take a look at some cool features that you can use on the latest version of the framework next.
It’s worth noting that major version updates are released every year around February, although the Laravel team may deploy minor changes and patch releases every week. Laravel 9 will receive bug fixes until August 2023 and security fixes until February 2024.
Eloquent
Enum Attribute Casting
Since PHP 8.1, the language has built-in support for Enums. Starting from Laravel 9, you can use Eloquent to cast attribute values to PHP enums. You only need to use the $casts property array and specify the attribute and Enum class.
For example:
1// App\Enums\BloodGroup2enum BloodGroup: string3{4 case A = 'a';5 case B = 'b';6 case O = 'o';7 case AB = 'ab';8}910// App\Models\BloodGroup11use App\Enums\BloodGroup;1213/**14 * The attributes that should be cast.15 *16 * @var array17 */18protected $casts = [19 'blood_group' => BloodGroup::class,20];21
And now Eloquent should understand how to cast from and to the Enum class.
1// from2if ($person->blood_group == BloodGroup::A) {3 $person->someMethod();4}56// to7 $person->blood_group = BloodGroup::B;8 $person->save();9
Implicit Route Bindings with Enums
You can also type-hint an Enum in your route definition and the framework will only invoke the route if that route segment is a valid Enum value.
1Route::get('/blood_groups/{blood_group}', function (BloodGroup $blood_group) {2 return $blood_group->value;3});4
Improved Eloquent Accessors/Mutators
The latest version of Laravel offers a new way to define Eloquent accessors and mutators. Prior to this version, there was only one way to do that, and it required the PHP developer to define prefixed methods on the model, like this example:
1public function getBloodGroupAttribute($value)2{3 return strtoupper($value);4}56public function setNameAttribute($value)7{8 $this->attributes['blood_group'] = $value;9}10
The new way to define an accessor and mutator requires only a single, non-prefixed method by type-hinting a return type of Illuminate\Database\Eloquent\Casts\Attribute.
1use Illuminate\Database\Eloquent\Casts\Attribute;23public function blood_group(): Attribute4{5 return new Attribute(6 get: fn ($value) => strtoupper($value),7 set: fn ($value) => $value,8 );9}10
Routes
Controller Route Groups
This new feature allows you to define a common controller for all routes inside a specific group. Using this is particularly helpful when you have an extensive routes file, and cleaning it up could help to make it easier to understand:
1use App\Http\Controllers\BloodGroupController;23Route::controller(BloodGroupController::class)->group(function () {4 // Calls BloodGroupController::show method5 Route::get('/blood_group/{id}', 'show');6 // Calls BloodGroupController::store method7 Route::post('/blood_group', 'store');8});9
Forced Scoping of Route Bindings
Consider the following scenario:
1use App\Models\User;2use App\Models\Donations;34Route::get('/users/{user}/donations/{donation}', function (User $user, Donation $donation) {5 return $donation;6});7
Usually, you’d expect the donation object to be automatically scoped, considering the user, meaning that the parameter {donation} is always related to the same user from the URL. But that’s not the case.
Let’s imagine we have two users and two donations on our database.
If you make a GET request for this route (mixing user and donation):
1HTTP GET /users/1/donations/22
Sure enough, the route will still return information from Donation 2.
It happens because Laravel doesn’t automatically scope the models from route binding. In the previous version of Laravel, it was possible to create this scope using a custom keyed implicit binding for the nested route parameter.
1// Laravel 8 example2use App\Models\User;3use App\Models\Donations;45Route::get('/users/{user}/donations/{donation:uuid}', function (User $user, Donation $donation) {6 return $donation;7});8
This behavior isn’t evident enough for anyone to understand what’s going on under the hood. To fix it, Laravel 9 includes an explicit `->scopeBindings()` method to instruct the framework to scope “child” bindings even without a custom key.
1// Laravel 9 example2use App\Models\User;3use App\Models\Donations;45Route::get('/users/{user}/donations/{donation}', function (User $user, Donation $donation) {6 return $donation;7})->scopeBindings();8
Tip: You can use the same approach for route groups.
Anonymous Migration Classes
Anonymous migrations are now a default behavior when you create a new migration file. It’s a simple change, but it helps avoid name collisions between migration classes.
It won’t affect how you code the migrations, and you can use this along with other non-anonymous migration classes.
Here’s an example of what it looks like now:
1<?php23use Illuminate\Database\Migrations\Migration;4use Illuminate\Database\Schema\Blueprint;5use Illuminate\Support\Facades\Schema;67return new class extends Migration8{9 /**10 * Run the migrations.11 *12 * @return void13 */14 public function up()15 {16 Schema::table('blood_groups', function (Blueprint $table) {17 $table->string('active')->default(1);18 });19 }2021 /**22 * Reverse the migrations.23 *24 * @return void25 */26 public function down()27 {28 Schema::table('blood_groups', function (Blueprint $table) {29 $table->dropColumn('active');30 });31 }32};33
Blade
Inline Blade Templates
While the use cases for this feature aren’t very common, it does come in handy in key instances. For instance, if you need to turn a raw Blade template string into valid HTML, you can through the render method on the Blade facade. This method accepts two parameters, the template string, and an optional array to provide data to the template.
1use Illuminate\Support\Facades\Blade;23return Blade::render('New blog article, {{ $title }}', ['title' => 'What is new in Laravel 9']);4
Slot Name Shortcut
Another simple but effective change is naming a slot on a blade component, which you can do using a shorter, more convenient syntax than before:
1<!-- Before -->2<x-slot name="version">3 Laravel 8.x4</x-slot>56<!-- After -->7<x-slot:version>8 Laravel 9.x9</x-slot>10
Checked / Selected Directives
There are two new directives to use on a blade file: @checked and @selected. These directives can be used to quickly indicate the state of a given HTML checkbox/select input.
Both will change the status of the HTML input by setting “checked”/”selected” on them if the provided condition evaluates to true.
1{{-- Checkbox --}}2<input type="checkbox"3 name="is_version_9"4 value="is_version_9"5 @checked(old('is_version_9', $laravel->is_version_9)) />67{{-- Select --}}8<select name="version">9 @foreach ($laravel->versions as $version)10 <option value="{{ $version }}" @selected(old('version') == $version)>11 {{ $version }}12 </option>13 @endforeach14</select>15
Pagination Views with Bootstrap Five Support
Laravel adopted Tailwind as the default CSS framework for some of the required views and packages, but it’s up to you if you want to use it or not.
To use Bootstrap 5 with the Laravel pagination directive, you should first change the default on the AppServiceProvider class by calling the Paginator’s useBootstrapFive method.
1use Illuminate\Pagination\Paginator;23public function boot()4{5 Paginator::useBootstrapFive();6}7
Doing this will change the HTML structure returned from `->links()` method to be compatible with Bootstrap Five, instead of Tailwind.
Improved Validation of Array with Nested Data
The new `Rule::forEach` method makes it easy to validate nested array values. It accepts a closure that gets invoked for each iteration of the array attribute under validation. That closure should return an array of rules to use for the array element.
1use Illuminate\Support\Facades\Validator;2use Illuminate\Validation\Rule;34//Each blood group element, will get the ID attributed validate against the rules specified5$validator = Validator::make($request->all(), [6 'blood_groups.*.id' => Rule::forEach(function ($value, $attribute) {7 return [8 Rule::exists(BloodGroup::class, 'id'),9 'is_numeric',10 'required'11 ];12 }),13]);14
Improved Exception Page
Debugging errors with Laravel is an easy task, and the new version has made it even easier. Laravel uses Ignition, an open-source package by Spatie.
Image Source: Laravel Release Notes
The latest version of the Ignition package, which is part of Laravel, includes:
These, among many other user-friendly features, make it a unique experience.
Improved Route Listing
While it’s not news that Laravel has a command to list all routes inside the application, this feature just got reworked. Made with the developer experience in mind, it’s now easier to visualize the routes.
The new output you’ll get after running `php artisan route:list` is:
Test Coverage Using Artisan
Now you can test coverage using the well-known Laravel command: `php artisan test`. This command now has a `–coverage` option available that can help you keep track of the amount of code coverage your tests provide.
Once you run it, this is the result you get:
And there’s more to it. You can use the `–min` option to define a minimum threshold for your tests.
For example:
1`php artisan test --coverage --min=90`2
Collections Are Now Easier For IDEs to Understand
I never get tired of saying that Laravel is always being iterated upon to create the best possible experience for PHP developers. From significant changes to small ones like this, it’s undoubtedly one of the main concerns of the Laravel team.
The latest Laravel version added an improved “generic” style type definition to the collections component. This makes it easier for IDEs such as PHPStorm or static analysis tools like PHPStan to understand Laravel collections and help developers with autocompletion.
Helper Functions
Laravel has an extensive list of helper functions available. Many of them are used internally, but they are also available to use anywhere in your app.
In the new release, there are two new helpers, and you’ll probably need them at some point:
1 – str()
This function is equivalent to the `Str::of()` method, meaning that everything available is also available in the str() helper. Take a look at this example:
1```php2$string = str('Scalable')->append(' Path'); // 'Scalable Path'3```4
If you don’t pass an argument to the str() function, it will return an instance of Illuminate\Support\Str, and you still can chain methods and pass the string to them.
1```php2$upper = str()->upper('scalable path'); // SCALABLE PATH3```4
2 – to_route()
Before this update, it was necessary to do something like this to redirect the user where you wanted:
1```php2return redirect()->route('blood_groups.show', ['blood_group' => 'A']);3```4
It’s not bad, but it could be shorter and more straightforward. This helper does precisely that.
Using the to_route function generates a redirect HTTP response for the route you’ve passed to it.
1return to_route('blood_groups.show', ['blood_group' => 'A']);2
You can also use the third and fourth arguments of the to_route() helper to pass the HTTP status code and any additional responder headers needed, respectively.
1return to_route('blood_groups.show', ['blood_group' => 'A'], 302, ['X-Custom-Header' => 'Laravel9'])2
Full-Text Indexes
Since Laravel 8, migrations can add a full-text index to a column through the `fullText()` method. And we have two new methods: `whereFullText` and `orWhereFullText`.
You can use them just like any other method on queries:
1$posts = DB::table('posts')2 ->whereFullText('text', 'GraphQL')3 ->get();4
The difference here is that both methods will generate an appropriate SQL query for the database engine to query a full-text column. For example, if you are using a MySQL database, instead of generating a `LIKE` clause to compare text, Laravel would automatically generate a `MATCH AGAINST` clause. This can significantly improve the performance of the app.
New Laravel Scout Database Engine
Laravel Scout is directly related to full-text search on Laravel Apps, specifically on Eloquent models. It provides an excellent way to add this feature to your app in a matter of minutes.
Before Laravel 9, the only way to use Scout was through services like Algolia or Meilisearch. Now we have a new database driver, which allows full-text searches using a common database.
Even though it’s not the best solution from a performance perspective, it’s still interesting if your application interacts with databases that aren’t too large or have a small workload.
Should I Upgrade My App to Laravel 9?
The short answer is yes. It’s best to always keep up with the latest version to benefit from the new features and a longer period of bug and security fixes for the core of your application.
Still, sometimes it’s complicated to update your application because you need to deal with third-party packages, hosting, and many other things related to your project.
My advice: don’t rush to update your application right away if it’s not necessary. Take some time to plan the update, check if the packages your app depends on also have a newer version supporting Laravel 9 and PHP 8, and don’t forget to change the hosting environment accordingly.
Remember to read carefully through the official upgrade guide. You’ll find all major and minor changes that you need to adapt to make your app 100% compatible with Laravel 9.
Additional Resources for Laravel Developers
There are some essential resources to sharpen your Laravel knowledge and keep your skills up to date. Of course, my first suggestion is the official documentation website, which I think is some of the best documentation available for web frameworks and a reason alone to choose Laravel for your project.
If you’re a visual learner, you can also go to laracasts.com, which is an excellent video resource to learn Laravel and PHP, Techniques, Testing, and even JavaScript frameworks. You can also check out this free video series about Laravel 9 which explains the new features through quick and compelling videos.
Conclusion
Laravel has been the most popular PHP framework for a long time for good reason. Regardless of whether you’re building a simple or enterprise-level app, Laravel has features with substantial capabilities, a vast ecosystem, and a highly active community.
The future of Laravel will undoubtedly follow the directives established by the internal team regarding new releases, avoiding breaking changes, and a regular release schedule. The future likely holds even more features and new tools to improve the developer experience and help create amazing apps.
Exciting News: Laravel 10 Has Been Released!
If you’re a fan of Laravel, you’ll be thrilled to know that Laravel 10 has been released as of February 14th, 2023! This latest release comes packed with a host of new features and improvements that promise to make the development experience even better.
One of the most notable changes is that Laravel 10 now requires PHP 8.1 as a minimum requirement. Additionally, there are new features like the ability to identify slow-running tests with the new —profile option, and a Process facade that provides a beautiful abstraction layer for starting and interacting with external processes.
Perhaps the most exciting new addition to Laravel 10 is the Laravel Pennant package. This powerful tool lets you incrementally roll out new application features with confidence, A/B test new interface designs, and much more. With Laravel Pennant, you can take your development process to the next level.
While this article covers Laravel 9 updates, we highly recommend developers check out the official Laravel 10 release notes for a full rundown of all the changes and updates. Trust us, you won’t want to miss out on this exciting new release!