A look at what is coming to Laravel 9

Published on by

A look at what is coming to Laravel 9 image

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) {

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 and Eloquent\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(
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;

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;

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:


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')

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()

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.


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'


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 L. Barnes photo

Eric is the creator of Laravel News and has been covering Laravel since 2012.

Filed in:

Laravel Newsletter

Join 40k+ other developers and never miss out on new tips, tutorials, and more.


Version 4 of Tinkerwell is available now. Get the most popular PHP scratchpad with all its new features and simplify your development workflow today.

Visit Tinkerwell
Laravel Forge logo

Laravel Forge

Easily create and manage your servers and deploy your Laravel applications in seconds.

Laravel Forge
Tinkerwell logo


The must-have code runner for Laravel developers. Tinker with AI, autocompletion and instant feedback on local and production environments.

No Compromises logo

No Compromises

Joel and Aaron, the two seasoned devs from the No Compromises podcast, are now available to hire for your Laravel project. ⬧ Flat rate of $7500/mo. ⬧ No lengthy sales process. ⬧ No contracts. ⬧ 100% money back guarantee.

No Compromises
Kirschbaum logo


Providing innovation and stability to ensure your web application succeeds.

Shift logo


Running an old Laravel version? Instant, automated Laravel upgrades and code modernization to keep your applications fresh.

Bacancy logo


Supercharge your project with a seasoned Laravel developer with 4-6 years of experience for just $2500/month. Get 160 hours of dedicated expertise & a risk-free 15-day trial. Schedule a call now!

Lucky Media logo

Lucky Media

Bespoke software solutions built for your business. We ♥ Laravel

Lucky Media
Lunar: Laravel E-Commerce logo

Lunar: Laravel E-Commerce

E-Commerce for Laravel. An open-source package that brings the power of modern headless e-commerce functionality to Laravel.

Lunar: Laravel E-Commerce
LaraJobs logo


The official Laravel job board

Larafast: Laravel SaaS Starter Kit logo

Larafast: Laravel SaaS Starter Kit

Larafast is a Laravel SaaS Starter Kit with ready-to-go features for Payments, Auth, Admin, Blog, SEO, and beautiful themes. Available with Vue and Livewire stacks.

Larafast: Laravel SaaS Starter Kit
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit: Laravel SaaS Starter Kit

SaaSykit is a Laravel SaaS Starter Kit that comes with all features required to run a modern SaaS. Payments, Beautiful Checkout, Admin Panel, User dashboard, Auth, Ready Components, Stats, Blog, Docs and more.

SaaSykit: Laravel SaaS Starter Kit
Rector logo


Your partner for seamless Laravel upgrades, cutting costs, and accelerating innovation for successful companies


The latest

View all →
Running a Single Test, Skipping Tests, and Other Tips and Tricks image

Running a Single Test, Skipping Tests, and Other Tips and Tricks

Read article
View Third-party Relations in model:show - Now Available in Laravel 11.11 image

View Third-party Relations in model:show - Now Available in Laravel 11.11

Read article
Asserting a JSON Response Structure in Laravel image

Asserting a JSON Response Structure in Laravel

Read article
Backpack turns 8 years old, celebrates with 40% discount image

Backpack turns 8 years old, celebrates with 40% discount

Read article
Create a DateTime from a Timestamp With this New Method Coming to PHP 8.4 image

Create a DateTime from a Timestamp With this New Method Coming to PHP 8.4

Read article
Neovim Plugin to for Navigating Laravel and Livewire Components image

Neovim Plugin to for Navigating Laravel and Livewire Components

Read article