How to Upgrade to Laravel 9
Tags: php, laravel
Published: 2023-05-16 16:01:20

*This article was originally written by [Samson Omojola](https://www.honeybadger.io/blog/authors/omojolasamsonade/) on the [Honeybadger Developer Blog](https://www.honeybadger.io/blog/upgrade-to-laravel-9/).*

Keeping your apps updated and constantly upgrading to the most recent version of the stacks they utilize has many benefits: preventing vulnerabilities, enhancing usability, and accessing bug fixes and new features. Therefore, even if your app still functions properly with old code, whenever you can, it’s usually best to perform an upgrade.

In this article, I’ll share tips on how to upgrade earlier Laravel versions to more recent releases in your existing apps.

## What's New in Laravel 9

For a long time, Laravel has been a popular, if not the most popular, open-source PHP framework. It is flexible, scalable, and adaptive, and it has become the top-shelf choice for engineers and businesses working with PHP. [The latest version of Laravel (Laravel 9)](https://laravel.com/docs/9.x/) was released in February 2022, and the next release (Laravel 10) will be in February 2023. Laravel 9 comes with a number of new and interesting features. Some of the notable ones are described in the following paragraphs.

### [Symfony Mailer](https://symfony.com/doc/6.0/mailer.html)

Swift Mailer was used by earlier versions of Laravel to send outgoing emails. However, Symfony Mailer has replaced it since Swift Mailer is no longer maintained. Symfony Mailer is now used to enable email sending on Laravel apps via SMTP, Postmark, Mailgun, Amazon SES, and sendmail.

If the application that you are trying to upgrade uses Swift Mailer, you may need to [install some mailgun driver prerequisites and rename some Swift methods to Symphony methods](https://laravel.com/docs/9.x/upgrade#symfony-mailer).

### [Flysystem 3.x](https://flysystem.thephpleague.com/docs/)

In Laravel apps, files are manipulated with the help of the Storage facade. And the Storage facade's whole filesystem interface is powered by Flysystem. In version 9, Laravel’s upstream Flysystem dependency was upgraded from Flysystem 1.x to Flysystem 3.x.

### Improved Eloquent Accessors/Mutators

In Laravel 9, by type-hinting a return type of `Illuminate\Database\Eloquent\Casts\Attribute`, you can define an accessor and a mutator using a single non-prefixed method, unlike earlier versions where you had to define multiple prefixed methods. This simplifies getting and setting attributes.

### Implicit Route Bindings with Enum

Enums were introduced in PHP 8.1. Laravel 9, which uses PHP 8.1, supports using Enums in route definitions. When Enums are used on a route, the route will be invoked only if its URL has a valid Enum value. If not, the route will return a 404 response.

### Controller Route Group

Laravel 9’s Route::controller() method can be used to define a common controller for every route within a route group.

### Full-Text Indexes/Where Clauses

To generate full-text indexes for MySQL or PostgreSQL database colums, the `fullText` method can be added to the definition.

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

You can use the `whereFullText` and `orWhereFullText` methods to add “where” clauses to queries for fullText columns. When running your app, Laravel will transform them into the proper SQL for your chosen database system.

### Laravel Scout Database Engine

If your application doesn’t deal with a huge load of data, Laravel’s Scout database engine should suffice for search features and serve as an alternative to dedicated search services, such as Algollia and MeiliSearch.

Rendering inline Blade Templates -: the `Blade` facade’s render method can be used to generate valid HTML from a raw Blade template string.

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

return Blade::render('My name is {{ $name }}', ['name' => 'Samson Omojola']);
```

Above, we have Blade’s `render` method taking a Blade template string with which it will generate a corresponding HTML.

Blade’s `renderComponent` method can be used to render a class component. The method will render any component instance that’s passed into it.

```php
use App\View\Components\NameComponent;

return Blade::renderComponent(new NameComponent('Samson Omojola'));
```

### Slot-Name Shortcut

In Laravel 9, you can now specify a slot’s name without using the `name` attribute.

Before:

```php
<x-slot name="options">
      <a>display</a>
      <a>hide</a>
</x-slot>
```

Now:

```php
<x-slot:options>
   <a>display</a>
   <a>hide</a>
</x-slot>
```

### Laravel Breeze API and Next.js

The Laravel Breeze starter kit now has an API scaffolding mode and a complimentary Next.js frontend. This starter kit will enable you build Laravel applications that serve as a backend for a JavaScript frontend, using Laravel Sanctum authentication.

### New Helpers

Two new helper functions have been introduced with Laravel 9:  `str` and `to_route`. The former returns a `Illuminate\Support\Stringable` instance for any string it’s given, while the latter helps you redirect to a named route from your controllers and routes.

## Manually Upgrading to Laravel 9

### Step 1

Before upgrading an application to Laravel 9, it is advisable to back up your existing code and the composer.json file. This can be done by simply creating a repository on GitHub and uploading your code to it.

### Step 2

Change the PHP Version - Laravel 9 requires a minimum PHP version of 8.0 due to it’s dependence on Symfony’s most recent version (v6.0), which depends on PHP 8.

Navigate to your composer.json file and change the PHP version to 8.

```php
"require": {
    "php": "^8.0",
}
```

### Step 3

Update Dependencies - In your composer.json file, replace the following packages with their updated versions:

```php
“laravel/framework” : “^9.0”, 
“nunomaduro/collision”: “^6.1”, 
“spatie/laravel-ignition”: “1.0” 
}
```

Then, run `composer update` to update the dependencies.

After composer performs an update, some of your third-party packages might throw errors since they do not support Laravel 9. They’ll need to be updated to versions that do.

For example, you see an error like this:

```php
spatie/laravel-backup requires illuminate/support ~6.0|~7.0|~8.0 -> satisfiable by illuminate/support[v6.0.0, ..., v6.20.16, v7.0.0, ..., v7.30.4, v8.0.0, ..., v8.28.1].
- Only one of these can be installed: illuminate/support[v4.0.0, ..., v4.2.17, v5.0.0, ..., v5.8.36, v6.0.0, ..., v6.20.16, v7.0.0, ..., v7.30.4, v8.0.0, ..., v8.28.1], laravel/framework[v9.0.-beta.1]. laravel/framework replaces illuminate/support and thus cannot coexist with it.
- Root composer.json requires laracasts/generators dev-master as 1.1.4 -> satisfiable by laracasts/generators[dev-master].
}
```

You’ll need to navigate to the GitHub page or official website of spatie/laravel-backup and check whether its latest version supports Laravel 9. If it does, then go to your composer.json and change the package’s version to the latest version.

After updating all the outdated third-party packages, run `composer update` again.

If you get any syntax errors, go to [Laravel’s official upgrade guide page](https://laravel.com/docs/9.x/upgrade) and search for the necessary update related to that error.

### Updating Flysystem Code

Flysystem helps you interact with drivers, such as Amazon S3, FTP, and SFTP, that help you manage files. Previous versions of Laravel use Flysystem v1 to interact with these drivers. However, Flysystem 1 has been replaced with Flysystem 3 in Laravel 9, so utilizing any of these drivers in your Laravel 9 app will require using Flysystem 3 to install them.

Use the following code for Amazon S3:

```bash
composer require -W league/flysystem-aws-s3-v3 "^3.0"
```

Use the following code for FTP:

```bash
composer require league/flysystem-ftp "^3.0"
```

Use the following code for SFTP:

```bash
composer require league/flysystem-sftp-v3 "^3.0"
```

In version 1 of Flysystem, write methods, such as `write` and `writeStream`, only perform write operations if there's no file in the specified location. To write files to locations already occupied by other files, the `update` and `updateStream` methods must be used. In Flysystem 3, however, `write` and `writeStream` will automatically perform an update if used on locations that have existing files in them, eliminating the need for `update` and `updateStream` methods. Additionally, while these write methods throw exceptions when they fail in previous Laravel versions, they simply return a value of `false` in the latest version. However, if you'd prefer to have exceptions thrown, set `throw` to `true` in your filesystem disk's configuration code.

```php
'public' => [
    'driver' => 'local',
    // ...
    'throw' => true,
],
```

Flysystem no longer supports cached adapters, so you can't use them in your Laravel 9 application.

If your old application has a custom filesystem driver, you should know that registering a custom filesystem driver now requires slightly different steps. In previous Laravel versions, the callback given to `Storage::extend` returns an instance of `Filesystem`.

```php
use Illuminate\Support\Facades\Storage;
use League\Flysystem\Filesystem;
use Spatie\Dropbox\Client as DropboxClient;
use Spatie\FlysystemDropbox\DropboxAdapter;
 
Storage::extend('dropbox', function ($app, $config) {
    $client = new DropboxClient(
        $config['authorization_token']
    );
 
    return new Filesystem(new DropboxAdapter($client));
});
```

In Laravel 9, however, an instance of `FilesystemAdapter` is returned instead.

```php
use Illuminate\Filesystem\FilesystemAdapter;
use Illuminate\Support\Facades\Storage;
use League\Flysystem\Filesystem;
use Spatie\Dropbox\Client as DropboxClient;
use Spatie\FlysystemDropbox\DropboxAdapter;
 
Storage::extend('dropbox', function ($app, $config) {
    $adapter = new DropboxAdapter(
        new DropboxClient($config['authorization_token'])
    );
 
    return new FilesystemAdapter(
        new Filesystem($adapter, $config),
        $adapter,
        $config
    );
});
```

### Updating SWift Mailer Code

Since Swift Mailer has been replaced with Symfony Mailer, you should remove the `wildbit/swiftmailer-postmark` package and add `symfony/postmark-mailer` and `symfony/http-client`.

```bash
composer require symfony/postmark-mailer symfony/http-client
```

To use Mailgun, add the following packages:

```bash
composer require symfony/mailgun-mailer symfony/http-client
```

In previous Laravel versions, the methods `send`, `raw`, `html`, and `plain` from `Illuminate\Mail\Mailer` return `void`. In Laravel 9, they return an instance of `Illuminate\Mail\SentMessage`, which contains an instance of `Symfony\Component\Mailer\SentMessage`. Use the `getSymfonySentMessage` method to access this instance.

If you’ve used any of the following Swift Mailer methods in your application, they'll need to be replaced by their Symfony Mailer counterparts:

```php
Message::getSwiftMessage(); ---------------> Message::getSymfonyMessage();
 
Mailable::withSwiftMessage($callback); ----> Mailable::withSymfonyMessage($callback);
 
MailMessage::withSwiftMessage($callback); -> MailMessage::withSymfonyMessage($callback);
 
Mailer::getSwiftMailer(); -----------------> Mailer::getSymfonyTransport();
 
Mailer::setSwiftMailer($swift); -----------> Mailer::setSymfonyTransport(TransportInterface $transport);
 
MailManager::createTransport($config); ----> MailManager::createSymfonyTransport($config);
```

If your old code uses the `Illuminate\Mail\Events\MessageSent` event, you should know that the `message` property, which used to return an instance of `Swift_Message`, now returns an instance of `Symfony\Component\Mime\Email`. Additionally, a new property `sent` has been added to the event. You can use it to retrieve information about a sent email.

If you defined stream options for SMTP in your old code, you should remove the code as it is no longer supported. Here is an example:

```php
'smtp' => [
    // Laravel 8.x...
    'stream' => [
        'ssl' => [
            'verify_peer' => false,
        ],
    ],
 ],
```

All configuration options must now be defined directly under `smtp`, like this:

```php
 'smtp' => [
    // Laravel 9.x...
    'verify_peer' => false,
 ],
```

Finally, if you use `Mail::failures()` to retrieve the list of failed recipients after an email has been sent, you should remove the code, as it is no longer supported. Unfortunately, there is no way to retrieve the list of failed recipients in Laravel 9, and you are advised to validate email addresses before sending messages to them.

To find out what other changes you might need to make, review the list of [medium impact changes](https://laravel.com/docs/9.x/upgrade#medium-impact-changes) on the official upgrade page.

## Automatic Upgrade with Laravel Shift

Shift is an automated (paid) tool that can be used to automatically upgrade applications with older versions of Laravel to later versions.

To use Shift, navigate to [https://laravelshift.com](https://laravelshift.com) in your browser.

![Laravel Shift Homepage](https://www.honeybadger.io/images/blog/posts/upgrade-to-laravel-9/Screenshot_49.png)
_Laravel Shift Homepage_

On the homepage, click on the ‘Run Shift’ button on the top-right side of the page.

You should see a list of the different kinds of upgrades that can be done and their corresponding prices. Decide which one you want and click the purchase option on it.

![Shift Packages](https://www.honeybadger.io/images/blog/posts/upgrade-to-laravel-9/Screenshot_50.png)
_Shift Packages_

Next, select the option to sign in with your GitHub account and give Shift access to your repositories by clicking on ‘Authorize laravel-shift’.

![Sign in with Github](https://www.honeybadger.io/images/blog/posts/upgrade-to-laravel-9/Screenshot_51.png)
_Sign in with Github_

![Authorize Shift](https://www.honeybadger.io/images/blog/posts/upgrade-to-laravel-9/Screenshot_52.png)
_Authorize Shift_

On the next page, under Connection, ensure that the GitHub repo attached is your GitHub repo. Under REPOSITORY OR CLONE URL, enter the link to the specific repository you want to upgrade. Under Branch, enter the branch of the repository you want upgraded (e.g., main).

![Add Repository](https://www.honeybadger.io/images/blog/posts/upgrade-to-laravel-9/Screenshot_54.png)
_Add Repository_

With the above information, Laravel Shift will create a pull request to that branch with the required changes for an upgrade. You can review the changes manually and decide what to accept. Next, click on "Checkout and run". On the next page, your credit card details will be requested. Enter your details and click on Purchase Shift.

![Make Payment](https://www.honeybadger.io/images/blog/posts/upgrade-to-laravel-9/Screenshot_55.png)
_Make Payment_

After making the purchase, you’ll be directed to a page that will show you the status of your request.

You should see the "Running" status for some seconds, indicating that Shift is upgrading your application. When it's complete, the Running status will change to a link to the pull request Shift created for you. Click on it. Under the ‘Files changed’ tab, you’ll see the updates Shift made.

A downside of Shift is that it might be considered too expensive by some. It may also be considered slow because, if you are upgrading from a very old version to the latest version, Shift doesn’t do it in a single upgrade. It proceeds step-by-step; for example, if you’re upgrading from v7 to v9, Shift upgrades to v8 and then to v9. However, if you consider the amount of effort it saves you, you may find that it’s worthwhile.

## Conclusion

You’ve made it to the end of the post! We covered the new features that the latest version of Laravel ships with, tips for manually upgrading the Laravel version of your existing application, and a tool (Laravel Shift) that automates the process for you.
For more info on upgrading Laravel versions, you can check out [the official documentation](https://laravel.com/docs/9.x/upgrade).