Technology Blog

Look deep into latest news and innovations happening in the Tech industry with our highly informational blog.

Laravel 9: a deep dive into the latest major release


This transition is intended to ease the maintenance burden on the community and challenge our development team to ship amazing, powerful new features without introducing breaking changes. Therefore, we have shipped a variety of robust features to Laravel 8 without breaking backwards compatibility, such as parallel testing support, improved Breeze starter kits, HTTP client improvements, and even new Eloquent relationship types such as “has one of many”.

Therefore, this commitment to ship great new features during the current release will likely lead to future “major” releases being primarily used for “maintenance” tasks such as upgrading upstream dependencies, which can be seen in these release notes.

Laravel 9 continues the improvements made in Laravel 8.x by introducing support for Symfony 6.0 components, Symfony Mailer, Flysystem 3.0, improved route:list output, a Laravel Scout database driver, new Eloquent accessor / mutator syntax, implicit route bindings via Enums, and a variety of other bug fixes and usability improvements.

PHP 8.0

Laravel 9.x requires a minimum PHP version of 8.0.

Flysystem 3.x

Laravel 9.x upgrades our upstream Flysystem dependency to Flysystem 3.x. Flysystem powers all of filesystem interactions offered by the Storage facade.

Please review the upgrade guide to learn more about ensuring your application is compatible with Flysystem 3.x.

Improved Eloquent Accessors / Mutators

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,

Enum Eloquent Attribute Casting

Enum casting is only available for PHP 8.1+.

Eloquent now allows you to cast your attribute values to PHP “backed” 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;

Implicit Route Bindings With Enums

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;

Forced Scoping Of Route Bindings

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;

Controller Route Groups

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

Full Text Indexes / Where Clauses

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

Laravel Scout Database Engine

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 MeiliSearch. 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.

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

Similarly, the renderComponent method may be used to render a given class component by passing the component instance to the method:

 use App\View\Components\HelloComponent;
return Blade::renderComponent(new HelloComponent('Julian Bashir'));

Slot Name Shortcut

In previous releases of Laravel, slot names were provided using a name attribute on the x-slot tag:

    <x-slot name="title">
        Server Error
    <strong>Whoops!</strong> Something went wrong!

However, beginning in Laravel 9.x, you may specify the slot’s name using a convenient, shorter syntax:

    Server Error

Checked / Selected Blade Directives

For convenience, you may now use the @checked directive to easily indicate if a given HTML checkbox input is “checked”. This directive will echo checked if the provided condition evaluates to true:

 <input type="checkbox"
        @checked(old('active', $user->active)) />

Likewise, the @selected directive may be used to indicate if a given select option should be “selected”:

 <select name="version">
    @foreach ($product->versions as $version)
        <option value="{{ $version }}" @selected(old('version') == $version)>
            {{ $version }}

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

For more information and to develop web applications using Laravel, Hire Laravel Developer from us as we give you a high-quality product by utilizing all the latest tools and advanced technology. E-mail us any clock at – or Skype us: “hkinfosoft”.

To develop custom web apps using Laravel, please visit our technology page.

Content Source: