« back published by @mmartin_joo on February 28, 2024
What's new in Laravel 11?
Laravel 11 will come out soon, and it will have some interesting changes. One of the biggest changes, of course, is the new simplified folder structure.
You can already install it by running the following command:
laravel new myapp --dev
Or using composer
:
composer create-project --prefer-dist laravel/laravel myapp dev-master
New folder structure
Http/Middleware
folder
No In older Laravel versions there was a Http/Middleware
folder with some default middlewares such as:
EncryptCookies
TrimStrings
RedirectIfAuthenticated
- etc
Now they are part of the framework instead of being in the Http/Middleware
folder in your project. Of course, when you create a new middleware with the php artisan make:middleware
command, it will still be created in the Http/Middleware
folder.
Http/Kernel.php
file
No So you created a new Middlaware
and you want to add it to your API routes. You would go to the Http/Kernel.php
file just to realize it's not there anymore.
Now all Middlware
configuration is done in the bootstrap/app.php
file:
<?php
use App\Http\Middleware\ExampleMiddleware;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
// Apply middleware to all routes
$middleware->use([ExampleMiddleware::class]);
// Use it only in the web routes
$middleware->web([ExampleMiddleware::class]);
// API only
$middleware->api([ExampleMiddleware::class]);
})
->withExceptions(function (Exceptions $exceptions) {
//
})->create();
As you can see, there's a new withMiddleware
method where you can configure your middlewares.
RouteServiceProvider.php
file
No By default, Laravel 11 will not have a RouteServiceProvider.php
file. So how can we configure our routes?
Let's say we want to add API routes with a prefix of /api
. In previous Laravel versions this was done in the RouteServiceProvider.php
file:
public function boot()
{
$this->routes(function () {
Route::middleware('api')
->prefix('api')
->group(base_path('routes/api.php'));
});
}
Now in Laravel 11, we need to use the bootstrap/app.php
file again:
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
apiPrefix: '/api',
)
We can check if it works by running the php artisan route:list
command:
Console/Kernel.php
file
No In previous Laravel versions commands were loaded in the Console/Kernel.php
file. This was also the place to write our task scheduling logic.
Now, in Laravel 11 task scheduling can be done in the routes/console.php
file:
use App\Console\Commands\ExampleCommand;
use App\Jobs\ExampleJob;
use Illuminate\Foundation\Inspiring;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Schedule;
Artisan::command('inspire', function () {
$this->comment(Inspiring::quote());
})->purpose('Display an inspiring quote')->hourly();
Schedule::call(function () {
dd('hey');
})->daily();
Schedule::command(ExampleCommand::class)->daily();
Schedule::command('app:example-command')->daily();
Schedule::job(ExampleJob::class)->daily();
We can use the same methods for scheduling:
call
to schedule a callbackcommand
to schedule a Commandjob
to schedule a Job
Less config files by default
In a new Laravel 11 project we have less config files by default in the config
directory. Some of them lives inside the framework, and they get merged, so we only need to add the ones we want to override.
These are the config files included by default:
If you need some of the older configs, such as broadcasting.php
you can publish it by running:
php artisan config:publish broadcasting
And now you have the broadcasting.php
file in your config
directory.
Eloquent-related changes
Eager load limit
I think this is the best new feature in Laravel 11. We can limit the number of records loaded when using eager load:
Post::query()
->with([
'comments' => fn ($query) => $query
->orderBy('created_at', 'desc')
->limit(10)
])->get();
This will load the 10 most recent comments for each post. It's a pretty handy feature as it helps you avoid performance issues.
You can do the same in earlier Laravel versions as well, but you need to install the staudenmeir/eloquent-eager-limit package. In fact, this exact package was merged into Laravel 11.
casts
method
New In Laravel 11 there's a new method called casts
that you can use to cast your model attributes. The old $casts
property is still there, but the method will take precedence if both are present.
Since now it's a method we can do more stuff:
protected function casts(): array
{
return [
'publish_at' => 'datetime',
'statuses' => AsEnumCollection::of(PostStatus::class),
'foo' => function () {
// Your own logic
},
];
}
Although they are not new, but here are some interesting Model casts you might not know about:
AsEnumCollection
to store multiple enum values in a single columnAsArrayObject
to store and access an associative array easily since the standardarray
cast work only with primitive arraysAsCollection
to cast values to a Laravel collection instead of a PHP array
SQLite
The default database driver is now SQLite. I'm not sure if I love this change but of course it's pretty easy to change it to MySQL in the .env
file.
once
There's a new function called once
which can be used to memoize functions.
Memoization is an optimization technique to speed up the execution of functions by caching the results of expensive calls and returning the cached result when the same inputs occur again. Basically, it's like a Redis a DB cache but for your functions.
Here's how memoization works:
- Function call: when a function is called with certain inputs, the function checks if the result for those inputs is already cached.
- Caching results: if the result is not cached, the function calculates the result and stores it in a data structure indexed by the input parameters.
- Returning cached result: on subsequent calls with the same inputs, instead of recalculating the result, the function retrieves the cached result from the data structure and returns it.
Memoization is particularly useful for optimizing recursive functions or functions with repeated computations. By storing previously computed results, it reduces redundant calculations and improves the performance of the program.
I don't think it's something that we use on a daily basis in high level web apps, but it's a nice addition to the framework.
The usage is pretty simple as always:
private function hashPassword(string $password)
{
$pass = once(fn () => bcrypt($password));
var_dump('Hashed password: ' . $pass);
return $pass;
}
For this example, I added a var_dump
statement to the bcrypt
function to see how many times it's called.
Here's some code to test if it works:
public function handle()
{
$this->hashPassword('password');
$this->hashPassword('password');
$this->hashPassword('password');
$this->hashPassword('password');
$this->hashPassword('my-strong-pass');
}
And here's the output:
As you can see, the bcrypt
function is called only twice:
- Once with the input of
password
- And one more time with the input of
my-strong-pass
So all the subsequent calls with the same input will return the cached result.