How to Upgrade to Laravel 9

Upgrading your Laravel app has many benefits: preventing security vulnerabilities, access to new features, and more. Read this article to learn what's new in Laravel 9 and how to upgrade in record time.

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

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.

Flysystem 3.x

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.

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

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.

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:

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

Now:

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

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

Step 3

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

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

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

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

Use the following code for FTP:

composer require league/flysystem-ftp "^3.0"

Use the following code for SFTP:

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.

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

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.

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.

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

To use Mailgun, add the following packages:

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:

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:

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

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

 '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 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 in your browser.

Laravel Shift Homepage 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 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 Sign in with Github

Authorize Shift 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 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 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.

What to do next:
  1. Try Honeybadger for FREE
    Honeybadger helps you find and fix errors before your users can even report them. Get set up in minutes and check monitoring off your to-do list.
    Start free trial
    Easy 5-minute setup — No credit card required
  2. Get the Honeybadger newsletter
    Each month we share news, best practices, and stories from the DevOps & monitoring community—exclusively for developers like you.
    Stop wasting time manually checking logs for errors!

    Try the only application health monitoring tool that allows you to track application errors, uptime, and cron jobs in one simple platform.

    • Know when critical errors occur, and which customers are affected.
    • Respond instantly when your systems go down.
    • Improve the health of your systems over time.
    • Fix problems before your customers can report them!

    As developers ourselves, we hated wasting time tracking down errors—so we built the system we always wanted.

    Honeybadger tracks everything you need and nothing you don't, creating one simple solution to keep your application running and error free so you can do what you do best—release new code. Try it free and see for yourself.

    Start free trial
    Simple 5-minute setup — No credit card required

    Learn more

    "We've looked at a lot of error management systems. Honeybadger is head and shoulders above the rest and somehow gets better with every new release."
    — Michael Smith, Cofounder & CTO of YvesBlue

    Honeybadger is trusted by top companies like:

    “Everyone is in love with Honeybadger ... the UI is spot on.”
    Molly Struve, Sr. Site Reliability Engineer, Netflix
    Start free trial
    Are you using Sentry, Rollbar, Bugsnag, or Airbrake for your monitoring? Honeybadger includes error tracking with a whole suite of amazing monitoring tools — all for probably less than you're paying now. Discover why so many companies are switching to Honeybadger here.
    Start free trial
    Stop digging through chat logs to find the bug-fix someone mentioned last month. Honeybadger's built-in issue tracker keeps discussion central to each error, so that if it pops up again you'll be able to pick up right where you left off.
    Start free trial
    “Wow — Customers are blown away that I email them so quickly after an error.”
    Chris Patton, Founder of Punchpass.com
    Start free trial