Laravel 9

Laravel 9 Comes with the PHP 8 Minimum Version

Laravel 9 is here after it has been rescheduled from September 2021 to February 9, 2022. It’s the first long-term support (LTS) release to be introduced following the 12-month release cycle. You may be familiar with the fact that Laravel transitioned to yearly releases with the release of Laravel 8. Before that, major versions were released every six months. The goal of this transition is to ease the maintenance burden on the community and challenge the Laravel development team to ship more powerful new features without introducing breaking changes. That’s why they have shipped a variety of robust features to Laravel 8 without breaking backward compatibility.

The commitment to ship great new features during the current release is most likely to lead to future “major” releases being mainly used for “maintenance” tasks with the likes of upgrading upstream dependencies. All that, of course, can be seen in these release notes.

Focus on building your project and apps and leave the server management to FastComet. With FastComet Managed Laravel Hosting you have the option to install Laravel applications included in the package, and there are no extra fees applied.

Table of Contents:

Key Laravel 9 Changes and Additions

Laravel 9 carries on the torch with the improvements made in Laravel 8.x. The new version introduces improved route:list output, Flysystem 3.0, support for Symfony 6.0 components, Symfony Mailer, a Laravel Scout database driver, new Eloquent accessor/mutator syntax, implicit route bindings via Enums, and a variety of bug fixes and usability improvements. Let’s dive in!

PHP 8 Is the Minimum Version in Laravel 9

Since Laravel 9 requires Symfony 6.0 and it has a minimum requirement of PHP 8.0, that means Laravel 9 carries this same requirement.

New Design for routes:list

The routes:list command has been included in Laravel for a long time now, and one issue that arises sometimes is that if you have huge and complex routes defined, trying to view them in the console can get messy. However, this is getting a makeover, thanks to a pull request from Nuno Maduro.

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.

New Query Builder Interface

Thanks to Chris Morrell, Laravel 9 features a new Query Builder Interface. See this merged PR for all the details.

The lack of a shared interface or inheritance between Query\Builder, Eloquent\Builder and Eloquent\Relation can be tricky, especially for developers who rely on type hints for static analysis, refactoring, or code completion in their IDE:

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 an Illuminate\Database\Eloquent\Concerns\DecoratesQueryBuilder trait that implements the interface in place of the existing __call implementation.

PHP 8 String Functions

Since PHP 8 is now the minimum, Tom Schlick has 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

Previous releases of Laravel have utilized the Swift Mailer library to send outgoing emails. Nevertheless, that library is no longer maintained and has been succeeded by Symfony Mailer.

For further info, review the upgrade guide to learn more about ensuring that your application is compatible with Symfony Mailer.

Flysystem 3.x

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

Improved Eloquent Accessors/Mutators

Laravel 9 offers a new way to define Eloquent accessors and mutators. In previous Laravel releases, 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, 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,
    );
}

Additionally, this new approach to defining accessors caches 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

PHP 8.1 introduces support for Enums. Laravel 9 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 Error response will be returned automatically. For example:

enum Category: string
{
    case Dogs = 'dogs';
    case Cats = 'cats';
}

You may define a route that will only be invoked if the {category} route segment is dogs or cats. Otherwise, an HTTP 404 response will be returned:

Route::get('/categories/{category}', function (Category $category) {
    return $category->value;
});

Controller Route Groups

Now you have the option to use the controller method for defining the common controller for all routes within the group. 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+.

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,
];

After 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

In previous Laravel releases, 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 now automatically scopes the query to retrieve the nested model by its parent using conventions to guess the relationship name on the parent. This behavior was only previously supported by Laravel when a custom key was used for the child route binding.

However, in Laravel 9, you may 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();

Also, 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 starter kit received an “API” scaffolding mode and complimentary Next.js frontend implementation. You may use this starter kit scaffolding to jump-start those Laravel applications that are serving as a backend, Laravel Sanctum authenticated API for a JavaScript frontend.

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

When using MySQL or PostgreSQL, the fullText method may now be added to column definitions to generate full text indexes:

$table->text('bio')->fullText();

Additionally, 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 can accomplish that by 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' => 'Joseph C']);

Soketi Echo Server

Although not exclusive to Laravel 9, 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.

Note:
Feel free to visit our tutorials if you need to learn How to Run Node.js on Shared Hosting.

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 and improved Ignition ships with Laravel 9 and includes light/dark themes, customizable “open in editor” functionality, and more.

New Helpers

Laravel 9 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:

1$string = str('Taylor')->append(' Otwell');

3// 'Taylor Otwell'

If no argument is provided to the str function, the function returns an instance of Illuminate\Support\Str:

1$snake = str()->snake('LaravelFramework');

3// '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:

1return 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:

1return 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.

How to Install Laravel 9

If you wish to try Laravel 9 for development and testing purposes, you can opt to install and run it on your local machine. This last version is not yet available in Softaculous, but it still can be manually installed on FastComet Shared Hosting via Composer.

You may create a new Laravel project by directly using Composer. After creating the application, you can start Laravel’s local development server using the Artisan CLI’s serve command:

composer create-project laravel/laravel example-app
cd example-app
php artisan serve

The other option for you is to install the Laravel Installer as a global Composer dependency:

composer global require laravel/installer
laravel new example-app
cd example-app
php artisan serve

Make sure that you place Composer’s system-wide vendor bin directory in your $PATH so the laravel executable can be located by your system.

Upgrading to Laravel 9

As we already mentioned, Laravel 9 requires PHP 8.0.2 or higher so before you proceed with the upgrade, check if the needed version is set up.

You should also update the following dependencies in your application’s composer.json file:

  • laravel/framework to ^9.0
  • nunomaduro/collision to ^6.0

In addition, you need to replace facade/ignition with “spatie/laravel-ignition“: “^1.0” in your application’s composer.json file.

You can access and edit your application’s composer.json file by using the cPanel File Manager, an FTP client such as FileZilla, or through SSH (for advanced users only).

As the last step, examine any other third-party packages consumed by your application and verify you are using the proper version for Laravel 9 support.

Final Words

Laravel keeps pushing the boundaries when it comes to great new improvements in the world of programming. It keeps growing with every new release and making things much less complicated for web application devs all over the world. We can’t see what’s in store with Laravel 10, even though we know it’s a year away. 

Share your opinions or even excitement regarding Laravel 9 in the comment section. Happy coding! 

Joseph

Joseph is part of the FastComet Marketing team. With years of content writing experience behind him, it's one of his favorite activities. Joseph takes part in the SEO of the FastComet website and blog. His goal is to write comprehensive posts and guides, always aiming to help our clients with essential information. Joseph also has a thirst for knowledge and improvement, which makes the hosting environment a perfect place for him.