A look at what is coming to Laravel 9
Published on by Eric L. Barnes
Laravel v9 is the next major version of Laravel, and it was released February 8th, 2022. In this post, we wanted to outline all the new features and changes in this version.
Laravel 9 Release Date Changes
Laravel v9 was scheduled to be released around September of this year, but the Laravel Team decided to push this release back to February of 2022:
Laravel uses a variety of community-driven packages as well as nine Symfony components for a number of features within the framework. Symfony 6.0 is due for release in November. For that reason, we are choosing to delay the Laravel 9.0 release until 2022. By delaying the release, we can upgrade our underlying Symfony components to Symfony 6.0 without being forced to wait until September 2022 to perform this upgrade. In addition, this better positions us for future releases as our yearly releases will always take place two months after Symfony's releases.
This will also push future major releases back as well, and here is the schedule going forward:
- Laravel 9: February 8th, 2022
- Laravel 10: February 7th, 2023
PHP 8 the minimum version in Laravel 9
Since Laravel 9 will require Symfony 6.0 and it has a minimum requirement of PHP 8 that means Laravel 9 will carry this same restriction.
New Design for routes:list
The routes:list
command has been included in Laravel for a long time now, and one issue that some times arise is if you have a huge and complex routes defined it can get messy trying to view them in the console. Thanks to a pull request from Nuno Maduro this is getting a makeover.
New Test Coverage Option
A new artisan test --coverage
option will display the test coverage directly on the terminal. It also includes a --min
option that you can use to indicate the minimum threshold enforcement for test coverage.
Anonymous Stub Migrations
Earlier this year, Laravel 8.37 came out with a new feature called Anonymous Migrations that prevents migration class name collisions.
use Illuminate\Database\Migrations\Migration;use Illuminate\Database\Schema\Blueprint;use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('people', function (Blueprint $table) { $table->string('first_name')->nullable(); }); }};
When Laravel 9 launches, this will be the default when you run php artisan make:migration
New Query Builder Interface
Thanks to Chris Morrell, Laravel 9 will feature a new Query Builder Interface, and you can see this merged PR for all the details.
For developers who rely on type hints for static analysis, refactoring, or code completion in their IDE, the lack of a shared interface or inheritance between
Query\Builder
,Eloquent\Builder
andEloquent\Relation
can be pretty tricky:
return Model::query() ->whereNotExists(function($query) { // $query is a Query\Builder }) ->whereHas('relation', function($query) { // $query is an Eloquent\Builder }) ->with('relation', function($query) { // $query is an Eloquent\Relation });
This feature adds a new Illuminate\Contracts\Database\QueryBuilder
interface and a Illuminate\Database\Eloquent\Concerns\DecoratesQueryBuilder
trait that implements the interface in place of the existing __call
implementation.
PHP 8 String Functions
Since PHP 8 will be the minimum, Tom Schlick submitted a PR to move to using str_contains()
, str_starts_with()
and str_ends_with()
functions internally in the \Illuminate\Support\Str
class.
From SwiftMailer to Symfony Mailer
Symfony Mailer support was contributed by Dries Vints, James Brooks, and Julius Kiekbusch.
Previous releases of Laravel utilized the Swift Mailer library to send outgoing email. However, that library is no longer maintained and has been succeeded by Symfony Mailer.
Please review the upgrade guide to learn more about ensuring your application is compatible with Symfony Mailer.
Flysystem 3.x
Flysystem 3.x support was contributed by Dries Vints.
Laravel 9.x upgrades our upstream Flysystem dependency to Flysystem 3.x. Flysystem powers all of filesystem interactions offered by the Storage
facade.
Improved Eloquent Accessors / Mutators
Improved Eloquent accessors / mutators was contributed by Taylor Otwell.
Laravel 9.x offers a new way to define Eloquent accessors and mutators. In previous releases of Laravel, the only way to define accessors and mutators was by defining prefixed methods on your model like so:
public function getNameAttribute($value){ return strtoupper($value);} public function setNameAttribute($value){ $this->attributes['name'] = $value;}
However, in Laravel 9.x you may define an accessor and mutator using a single, non-prefixed method by type-hinting a return type of Illuminate\Database\Eloquent\Casts\Attribute
:
use Illuminate\Database\Eloquent\Casts\Attribute; public function name(): Attribute{ return new Attribute( get: fn ($value) => strtoupper($value), set: fn ($value) => $value, );}
In addition, this new approach to defining accessors will cache object values that are returned by the attribute, just like custom cast classes:
use App\Support\Address;use Illuminate\Database\Eloquent\Casts\Attribute; public function address(): Attribute{ return new Attribute( get: fn ($value, $attributes) => new Address( $attributes['address_line_one'], $attributes['address_line_two'], ), set: fn (Address $value) => [ 'address_line_one' => $value->lineOne, 'address_line_two' => $value->lineTwo, ], );}
Implicit Route Bindings With Enums
Implicit Enum bindings was contributed by Nuno Maduro.
PHP 8.1 introduces support for Enums. Laravel 9.x introduces the ability to type-hint an Enum on your route definition and Laravel will only invoke the route if that route segment is a valid Enum value in the URI. Otherwise, an HTTP 404 response will be returned automatically. For example, given the following Enum:
enum Category: string{ case Fruits = 'fruits'; case People = 'people';}
You may define a route that will only be invoked if the {category}
route segment is fruits
or people
. Otherwise, an HTTP 404 response will be returned:
Route::get('/categories/{category}', function (Category $category) { return $category->value;});
Controller Route Groups
Route group improvements were contributed by Luke Downing.
You may now use the controller
method to define the common controller for all of the routes within the group. Then, when defining the routes, you only need to provide the controller method that they invoke:
use App\Http\Controllers\OrderController; Route::controller(OrderController::class)->group(function () { Route::get('/orders/{id}', 'show'); Route::post('/orders', 'store');});
Enum Eloquent Attribute Casting
{note} Enum casting is only available for PHP 8.1+.
Enum casting was contributed by Mohamed Said.
Eloquent now allows you to cast your attribute values to PHP enums. To accomplish this, you may specify the attribute and enum you wish to cast in your model's $casts
property array:
use App\Enums\ServerStatus; /** * The attributes that should be cast. * * @var array */protected $casts = [ 'status' => ServerStatus::class,];
Once you have defined the cast on your model, the specified attribute will be automatically cast to and from an enum when you interact with the attribute:
if ($server->status == ServerStatus::provisioned) { $server->status = ServerStatus::ready; $server->save();}
Forced Scoped Bindings
Forced scoped bindings was contributed by Claudio Dekker.
In previous releases of Laravel, you may wish to scope the second Eloquent model in a route definition such that it must be a child of the previous Eloquent model. For example, consider this route definition that retrieves a blog post by slug for a specific user:
use App\Models\Post;use App\Models\User; Route::get('/users/{user}/posts/{post:slug}', function (User $user, Post $post) { return $post;});
When using a custom keyed implicit binding as a nested route parameter, Laravel will automatically scope the query to retrieve the nested model by its parent using conventions to guess the relationship name on the parent. However, this behavior was only previously supported by Laravel when a custom key was used for the child route binding.
However, in Laravel 9.x, you may now instruct Laravel to scope "child" bindings even when a custom key is not provided. To do so, you may invoke the scopeBindings
method when defining your route:
use App\Models\Post;use App\Models\User; Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) { return $post;})->scopeBindings();
Or, you may instruct an entire group of route definitions to use scoped bindings:
Route::scopeBindings()->group(function () { Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) { return $post; });});
Laravel Breeze API & Next.js
The Laravel Breeze API scaffolding and Next.js starter kit was contributed by Taylor Otwell and Miguel Piedrafita.
The Laravel Breeze starter kit has received an "API" scaffolding mode and complimentary Next.js frontend implementation. This starter kit scaffolding may be used to jump start your Laravel applications that are serving as a backend, Laravel Sanctum authenticated API for a JavaScript frontend.
Laravel Scout Database Engine
The Laravel Scout database engine was contributed by Taylor Otwell and Dries Vints.
If your application interacts with small to medium sized databases or has a light workload, you may now use Scout's "database" engine instead of a dedicated search service such as Algolia or MeiliSerach. The database engine will use "where like" clauses and full text indexes when filtering results from your existing database to determine the applicable search results for your query.
Full Text Indexes / Where Clauses
Full text indexes and "where" clauses were contributed by Taylor Otwell and Dries Vints.
When using MySQL or PostgreSQL, the fullText
method may now be added to column definitions to generate full text indexes:
$table->text('bio')->fullText();
In addition, the whereFullText
and orWhereFullText
methods may be used to add full text "where" clauses to a query for columns that have full text indexes. These methods will be transformed into the appropriate SQL for the underlying database system by Laravel. For example, a MATCH AGAINST
clause will be generated for applications utilizing MySQL:
$users = DB::table('users') ->whereFullText('bio', 'web developer') ->get();
Rendering Inline Blade Templates
Sometimes you may need to transform a raw Blade template string into valid HTML. You may accomplish this using the render
method provided by the Blade
facade. The render
method accepts the Blade template string and an optional array of data to provide to the template:
use Illuminate\Support\Facades\Blade; return Blade::render('Hello, {{ $name }}', ['name' => 'Julian Bashir']);
Soketi Echo Server
The Soketi Echo server was developed by Alex Renoki.
Although not exclusive to Laravel 9.x, Laravel has recently assisted with the documentation of Soketi, a Laravel Echo compatible Web Socket server written for Node.js. Soketi provides a great, open source alternative to Pusher and Ably for those applications that prefer to manage their own Web Socket server.
Bootstrap 5 Pagination Views
Laravel now includes pagination views built using Bootstrap 5. To use these views instead of the default Tailwind views, you may call the paginator's useBootstrapFive
method within the boot
method of your App\Providers\AppServiceProvider
class:
use Illuminate\Pagination\Paginator; /** * Bootstrap any application services. * * @return void */public function boot(){ Paginator::useBootstrapFive();}
Improved Ignition Exception Page
Ignition, the open source exception debug page created by Spatie, has been redesigned from the ground up. The new, improved Ignition ships with Laravel 9.x and includes light / dark themes, customizable "open in editor" functionality, and more.
New Helpers
Laravel 9.x introduces two new, convenient helper functions that you may use in your own application.
str
The str
function returns a new Illuminate\Support\Stringable
instance for the given string. This function is equivalent to the Str::of
method:
$string = str('Taylor')->append(' Otwell'); // 'Taylor Otwell'
If no argument is provided to the str
function, the function returns an instance of Illuminate\Support\Str
:
$snake = str()->snake('LaravelFramework'); // 'laravel_framework'
to_route
The to_route
function generates a redirect HTTP response for a given named route, providing an expressive way to redirect to named routes from your routes and controllers:
return to_route('users.show', ['user' => 1]);
If necessary, you may pass the HTTP status code that should be assigned to the redirect and any additional response headers as the third and fourth arguments to the to_route method:
return to_route('users.show', ['user' => 1], 302, ['X-Framework' => 'Laravel']);
The server.php file can be removed
A minor feature but you can now remove the server.php
file from your project and it will be included inside the framework. This file is only used for php artisan serve
.
And More...
Laravel 9 is still a few months away, and more new features and announcements will be coming out. We will be updating this post as those get announced. You can also check out the official release page.
Eric is the creator of Laravel News and has been covering Laravel since 2012.