What’s new in Laravel 9?
Tags: laravel
Published: 2022-03-17 15:19:19

**Written by [Stephan Miller](https://blog.logrocket.com/author/stephan-miller/)✏️**

Laravel has dominated the PHP web framework landscape for many years now. If you build custom PHP websites, chances are you have used the framework once or twice and know that a Laravel update comes out every six months.

Laravel v9 released on 8 February 2022, and it offers plenty of new features.

## Laravel's release history

While past releases of Laravel occurred every six months, the new release cycle going forward will be every 12 months, which ensures the release syncs with Symfony — which Laravel uses behind the scenes — and allows the development team more time to fix any bugs that occur when interacting with the Symfony framework.

Here is a breakdown of the most recent and upcoming Laravel releases:


![Upcoming Laravel Releases](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pqkjeaas40hphp87ovvt.png)

You can see that version 9 is the new long-term support version, replacing version 6, which will no longer be supported by the end of this year. As of this month, v7 is no longer supported at all, so if you are using Laravel 6 or 7, it's time to upgrade to v9.

## What’s new in Laravel 9?

In this article, we’ll cover the current major changes in Laravel 9, but expect to see more features and changes soon.

### PHP 8 is the minimum requirement

Laravel uses Symfony 6, which requires at least [PHP 8](https://www.php.net/releases/8.0/en.php). PHP 8 comes with the new just-in-time (JIT) compiler, the [OPcache extension](https://www.php.net/manual/en/book.opcache.php), named arguments, and more.

### Symfony Mailer replaced Swift Mailer

Swift Mailer, which has been used in Laravel for years, is being removed and will no longer be maintained. In Laravel v9 and future releases, you'll have to use [Symfony Mailer](https://laravel.com/docs/9.x/mail). If you are upgrading an existing Laravel instance, check out the [upgrade guide](https://laravel.com/docs/9.x/upgrade#symfony-mailer).

### Controller route groups

You can now use the `controller` method of the Laravel 9 `Route` class to define the controller that will be used for every route in a route group.

```php
use App\Http\Controllers\PostController;

Route::controller(PostController::class)->group(function () {
    Route::get('/post/{id}', 'show');
    Route::post('/post', 'store');
});
```

### Better accessors and mutators in Eloquent

In Laravel 9, you can now use the `Illuminate\Database\Eloquent\Casts\Attribute` to declare a model prefix with a single non-prefixed term. Using one method call, you can now both get and set attributes.

```php
use Illuminate\Database\Eloquent\Casts\Attribute;

public function username(): Attribute
{
  return new Attribute(
    get: fn ($value) => strtoupper($value),
    set: fn ($value) => $value,
  );
}
```

### `Fulltext` indexes and where clauses

If you are using MySQL or [PostgreSQL](https://blog.logrocket.com/tag/postgresql/) in your Laravel application, you can now use the `fulltext` method on the column definitions in your migration files to generate full-text indexes.

```php
$table->text('content')->fullText();
```

Then, you can use the `whereFullText` and `orWhereFullText` methods to add full-text where clauses to your queries.

```php
$laravelPosts= DB::table('post')
           ->whereFullText('content', 'laravel')
           ->get();
```

### The new Scout database engine

Laravel v9 ships with the new Laravel Scout database engine. It provides full-text search capabilities to Eloquent models. It uses model observers to keep search indexes in sync with Eloquent records and is a good choice for applications that use a small- or medium-sized database or have a light workload. This engine will use "where-like" clauses when filtering results from your database.

To use it, just add the `Laravel\Scout\Searchable` trait to a model:

```php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;

class Article extends Model
{
    use Searchable;
}
```

### Breeze API with Next.js

Laravel v9 includes a complimentary [Next.js](https://blog.logrocket.com/tag/nextjs/) frontend implementation in its Breeze starter kit. By using this starter kit scaffolding, you can build Laravel applications that serve as both a backend and a JavaScript frontend using Laravel Sanctum authentication.

### Inline Blade rendering

If you need to transform a raw Blade template into valid HTML, you can now do that with inline Blade rendering.

```php
use Illuminate\Support\Facades\Blade;

return Blade::render('Hello, {{ $name }}', ['name' => 'Stephan Miller']);
```

### New query builder interface

The new query builder interface in Eloquent makes it possible to type hint Eloquent queries. In the past, it was difficult to tell whether you were dealing with `Query\Builder`, `Eloquent\Builder`, or `Eloquent\Relation`, leaving devs guessing on what to fix whenever a TypeError showed up.

```php
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
  });
```

### Implicit route bindings with enums

You can now type hints with a PHP enum in your Laravel route definitions. Laravel will then only invoke the route if the route contains a valid enum in the URI and will return a 404 if one of the enums is not found.

```php
enum Fruit: string
{
    case Apple = 'apple';
    case Cherry = 'cherry';
}
```

This route will only be invoked if the `{fruit}` route matches one of the enums.

```php
Route::get('/fruits/{fruit}', function (Fruit $fruit) {
    return $fruit->value;
});
```

### Forced scope route bindings

Laravel 9 can now automatically scope the query to retrieve a nested model by its parent in a route definition by using conventions to guess the relationship name of the parent. Here is an example of using scope bindings:

```php
use App\Models\Article;
use App\Models\User;

Route::get('/users/{user}/articles/{article}', function (User $user, Article $article) {
    return $article;
})->scopeBindings();
```

You can also use scope bindings on a group of route definitions.

```php
use App\Models\Article;
use App\Models\User;

Route::get('/users/{user}/articles/{article}', function (User $user, Article $article) {
    return $article;
})->scopeBindings();
```

### Bootstrap 5 pagination views

If you have ever tried to write your own pagination code, you know it's not fun. Laravel 9 makes it simple to add pagination to the pages in your app with Bootstrap 5 pagination views.

All you have to do is include the `Illuminate\Pagination\Paginator` and call its `useBootstrapFive` method in the boot method of your app's `App\Providers\AppServiceProvider` class.

```php
use Illuminate\Pagination\Paginator;

/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
    Paginator::useBootstrapFive();
}
```

### New helpers

Now that Laravel is using PHP 8, its `\Illuminate\Support\Str` facade will use PHP 8 string functions, which come with some new methods, including `str_contains`, `str_starts_with`, and `str_ends_with`. New helpers include `append` and `snake`.

```php
$string = str('Bob')->append(' Smith'); // 'Bob Smith'
$snake = str()->snake('LaravelIsGreat'); // 'laravel_is_great'
```

Another helper that was added is the `to_route` function. This function creates a redirect HTTP response for a named route. You can use it to redirect to named routes from routes and controllers.

```php
return to_route('posts.show', ['post' => 25]);
```

### Enum attribute casting

You can now cast attribute values to PHP enums in Laravel 9\. Here is an example of using casts in a model:

```php
use App\Enums\UserStatus;

/**
 * The attributes that should be cast.
 *
 * @var array
 */
protected $casts = [
    'status' => UserStatus::class,
];
```

After you have defined the cast in your model, this attribute will be automatically cast to and from the enum.

```php
if ($user->status == UserStatus::optin) {
    $user->status = UserStatus::verified;

    $user->save();
}
```

### Checked and selected Blade directives

I don't know how many times I've Googled, "How to set a checkbox checked in Laravel.” Laravel v9 has made this easier. Now you can use the `@checked` directive to set a checkbox as `checked`. If it evaluates to true, it will echo `checked`.

```php
<input type="checkbox"
        name="optin"
        value="optin"
        @checked(old('optin', $user->optin)) />
```

There is also a similar `@selected` directive for setting the selected option in a select.

```php
<select name="notification">
    @foreach ($notifications as $notification)
        <option value="{{ $notification }}" @selected(old('notification') == $notification)>
            {{ $notification }}
        </option>
    @endforeach
</select>
```

### Better validation of nested array data

The `Illuminate\Validation\Rule` validation class now has a new `forEach` method that accepts a closure that will run on each iteration of the array attribute being validated. The closure will return an array of rules to assign to the array element.

```php
use App\Rules\HasPermission;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;

$validator = Validator::make($request->all(), [
    'companies.*.id' => Rule::forEach(function ($value, $attribute) {
        return [
            Rule::exists(Company::class, 'id'),
            new HasPermission('manage-company', $value),
        ];
    }),
]);
```

### Soketi echo server

Laravel now comes with the [Soketi echo server](https://soketi.app/), a Laravel Echo compatible WebSocket server written for Node.js. It is an open-source alternative to Ably and Pusher for developers who prefer to manage their own WebSocket servers.

### Improved exception pages

Laravel 9 also has a new and improved exception page that was redesigned from the ground up. You can choose between light and dark themes, and it even has an "open in editor" functionality.

![Laravel 9 Exception Page](https://blog.logrocket.com/wp-content/uploads/2022/03/laravel-9-exception-page.png)

### Anonymous stub migration

Anonymous stub migration is now the default behavior when you perform a Laravel migration. This feature was available in Laravel 8.3, but is the default in Laravel v9\. This feature prevents name collisions with migration classes. Before this change, it was difficult to re-create a database from scratch if you reused a class name. Now, you won't have to worry about this.

### Better listing of routes in Artisan

Laravel already had the `route:list` Artisan command, but it now offers a better, color-coded breakdown of the routes in your app.

![Listing Routes in Laravel 9](https://blog.logrocket.com/wp-content/uploads/2022/03/listing-routes-laravel-9.png)

### Flysystem 3.x

In Laravel v9, Flysystem was migrated from v1.x to v3.x. Flysystem handles all the file manipulation functions that the `Storage` facade provides. Some changes you will see are:

*   The `put`, `write`, and `writeStream` methods now overwrite existing files by default
*   The `put`, `write`, and `writeStream` methods no longer throw an exception on a write error
*   If you try to read a file that doesn't exist, `null` will be returned
*   Deleting a file that doesn't exist now returns `true`

### Test coverage

The Artisan `test` command now has a `--coverage` option, which will output the test coverage percentages in the CLI output.

![Test Coverage Laravel 9](https://blog.logrocket.com/wp-content/uploads/2022/03/test-coverage-laravel-9.png)

### No more server.php file

Not the biggest change in the list, but you no longer need the `server.php` file in your project. It will now be included with the rest of the framework.

## Get started with Laravel v9

If you are ready to try the new version of Laravel, there are a few ways you can do it. If you already have composer installed, you can create a new Laravel project with it.

```shell
composer create-project laravel/laravel my-laravel-9-app
cd my-laravel-9-app
php artisan serve
```

You can also install Laravel globally with composer and use it to create a new project.

```shell
composer global require laravel/installer
laravel new my-laravel-9-app
cd my-laravel-9-app
php artisan serve
```

If you have [Docker Desktop](https://www.docker.com/products/docker-desktop) on Mac, you can run these commands to launch a Docker image running Laravel 9:

```shell
curl -s "https://laravel.build/my-laravel-9-app" | bash
cd my-laravel-9-app
./vendor/bin/sail up
```

You can change `my-laravel-9-app` to whatever you want. The last command uses [Laravel Sail](https://blog.logrocket.com/laravel-and-docker-a-guide-to-using-laravel-sail/), which is a lightweight command-line interface for interacting with Laravel's Docker configuration. The first time you run the command may take a few minutes. After that, the app will start quicker.

In Windows, you can run the same commands above in a new terminal session in your WSL2 Linux operating system. The same commands also work in Linux — you just have to have [Docker Compose](https://docs.docker.com/compose/) installed.

Is Laravel 9 worth the extra wait? I think so. There are many improvements and more to come now that the development team will push new features during the current release. It is also the new LTS version, so it will be around for a while. Good luck with your new Laravel 9 apps!

---

## [LogRocket](https://logrocket.com/signup/): Full visibility into your web apps

![LogRocket signup](https://blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png)

[LogRocket](https://logrocket.com/signup/) is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page and mobile apps.

[Try it for free](https://logrocket.com/signup/).