What's New In Laravel 10

Harry 4 min read
Table of Contents

The next major version of Laravel, 10.0, is now available. Follow the upgrade guide or start a new project on Laravel 10 to jump right in.

For existing projects, it's estimated to take 10 minutes to upgrade. Though mileage may vary.

When is the Release Date?

Laravel 10 was released on 14th February, 2023. It is to receive bug fixes until August 6th 2024, and security fixes until February 4th, 2025.

It'll officially support PHP 8.1 and PHP 8.2. Laravel 9 supports PHP 8.0 to PHP 8.2, so it's not a bad idea to upgrade to PHP 8.1 or 8.2 early if you are on 8.0 currently.

Deprecations Removed

Dependency Updates

New Features

Laravel Pennant

Laravel Pennant is a new first-party package to help you manage features that are conditionally enabled based on your own parameters. The state of flags will be stored against the active user and can be used for gradually rolling out new features and A/B testing amongst others.

To get started, install Laravel Pennant via Composer and follow the install guide:

$ composer require laravel/pennant

An example of Laravel Pennant in action - defining a new-api feature which is enabled for all internal team members, excluding high traffic customers, and then the rest have a 1/100 chance of being rolled in.

namespace App\Providers;
use App\Models\User;
use Illuminate\Support\Lottery;
use Illuminate\Support\ServiceProvider;
use Laravel\Pennant\Feature;
class AppServiceProvider extends ServiceProvider
     * Bootstrap any application services.
    public function boot(): void
        Feature::define('new-api', fn (User $user) => match (true) {
            $user->isInternalTeamMember() => true,
            $user->isHighTrafficCustomer() => false,
            default => Lottery::odds(1 / 100),

Then you can check if the feature is enabled and conditionally fire off the respective logic.

namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Laravel\Pennant\Feature;
class PodcastController
     * Display a listing of the resource.
    public function index(Request $request): Response
        return Feature::active('new-api')
                ? $this->resolveNewApiResponse($request)
                : $this->resolveLegacyApiResponse($request);
    // ...

Processes Facade

New for Laravel 10 is the Processes Facade. Handy if your application interacts with a shell underneath.

An example provided by the Laravel documentation:

use Illuminate\Support\Facades\Process;
$result = Process::run('ls -la');
return $result->output();

This will output the results of running ls -la. For those that don't know, it will list all files (including hidden ones) within the current working directory alongside permissions and ownership.

Check out the documentation to see a more in-depth guide of what the Processes Facade is capable of.

Horizon/Telescope UI Facelift

Horizon and Telescope have received updates to their user interface. Bringing both up to modern trends.

Laravel Horizon (Before):

Laravel 9 Horizon Pre-Facelift

Laravel Horizon (After):

Laravel Horizon Facelift

Test Profiling

It is now possible to profile tests using the new --profile option when running tests.

$ php artisan test --profile
Laravel 10 Test Profiling

The top 10 slowest tests are listed, with the slowest at the top.

Faster View Hashing

PHP8.1 comes with native support for xxh128 hashing and other variants. The algorithm is not designed for security but for randomness and speed.

It is in fact the fastest hashing algorithm supported by PHP to date.

PHP Hash Algorithm Benchmark
Full benchmarks:

Laravel 10 has migrated away from sha1 to xxh128 for hashing blade view paths.

Custom Validation Rules will now use __invoke by default

Invokable validation classes were added in Laravel 9, removing a bunch of boilerplate from the validation class.

// Before
class Quantity implements Rule
    protected $messages = [];

    public function passes($attribute, $value)
        if (! is_array($value)) {
            $this->messages[] = trans('validation.quantity.must_be_an_object');

            return false;

    public function message()
        return $this->messages;
// After
class InvokableQuantity implements InvokableRule
    public function __invoke($attribute, $value, $fail)
        if (! is_array($value)) {
            return $fail('validation.quantity.must_be_an_object')->translate();

All you had to do was add the --invokable flag when calling php artisan make:rule InvokableQuantity --invokable. In Laravel 10 this is now the default behaviour and --invokable is no longer required.

Native Type Declarations Instead of DocBlocks

Early PHP depended on DocBlocks to annotate return and method argument types. As PHP has evolved and made this part of the language itself, the need for DocBlocks has become less necessary.

Starting from Laravel 10, DocBlocks will be removed in favour of native type declarations. To give an example of what this would look like.

 * @param int $id
 * @return User
public function getUser($id)
    return User::find($id);


public function getUser(int $id): User
    return User::find($id);

Much better, right?

Filesystem $path Is Now Optional

Laravel 9.x introduced scoped filesystem drivers enabling filesystems to reuse an existing driver. In Laravel 10, several of the Filesystem methods that required a path variable are now optional.

This means you can use scoped filesystem drivers to define common paths and leave out the $path when using putFile, putFileAs, store and others.


Optimised Eager Loading

More a bug than a feature. Gary Green raised an issue where queries were being run for impossible eager loaded relations.

Given his example:

class User {
   public function picture()
      return $this->belongsTo(Picture::class);

User::create(['picture_id' => null]),
User::create(['picture_id' => null])
$users = User::all()->load(['picture']);

This would try and load the pictures relationship even though the relation's id is null.

select * from `pictures` where 0 = 1

This is now fixed in Laravel 10, where a null relation will no longer be queried.

Collection->random Can Now Preserve Keys

NathanaelGT added a preserve keys option to the collection's random method.

// Before
Arr::random($collection->all(), 2, true);

// After
$collection->random(2, true);
More from Harrk Blog

Harrk Blog

Programming nonsense, tutorials, and random musings about games and technology.

Great! You’ve successfully signed up.

Welcome back! You've successfully signed in.

You've successfully subscribed to Harrk Blog.

Success! Check your email for magic link to sign-in.

Success! Your billing info has been updated.

Your billing was not updated.