Principales novedades en Laravel 9
Laravel 9 fue lanzado el 8 febrero de 2022 e incluye múltiples mejoras y nuevas funcionalidades. Agrupación de rutas por controlador, una nueva API para definir accesores y mutadores, un nuevo motor de base de datos para Laravel Scout, y más mejoras.
Agrupando rutas por controlador
Supongamos que tenemos algunas rutas configuradas en nuestro archivo routes/web.php
para una web de podcast.
Route::get('/podcasts', [PodcastController::class, 'index']);
Route::get('/podcasts/{podcast}', [PodcastController::class, 'show']);
Route::post('/podcasts', [PodcastController::class, 'store']);
En Laravel 9 podemos ahora agrupar las rutas por controlador con Route::controller
.
Route::controller(PodcastController::class)->group(function () {
Route::get('/podcasts', 'index');
Route::get('/podcasts/{podcast}', 'show');
Route::post('/podcasts', 'store');
});
Donde asumirá que el controlador será PodcastController
para cualquier ruta declarada dentro del grupo.
Podemos probar las rutas con el comando php artisan route:list
.
De hecho, el resultado del comando artisan route:list
ha sido rediseñado también en Laravel 9. En versiones anteriores lucía así.
Clases de migración anónimas
Desde la versión 9, las clases de migraciones han dejado de tener nombre.
// migrations/2014_10_12_000000_create_users_table.php
return new class extends Migration
{
// ...
}
Anteriormente, tendrían el nombre class CreateUsersTable
.
Incluso las migraciones generadas con el comando Artisan make:migration
serán con una clase anónima.
php artisan make:migration create_podcasts_table
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('podcasts', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('podcasts');
}
};
Nuevos helpers
Laravel 9 introduce dos nuevos helpers. str()
y to_route()
.
Helper str()
str()
es un nuevo helper para generar un objeto Stringable.
Escencialmente es un alias de Str::of()
.
Por ejemplo, Str::of('Oliver')->upper()
y str('Oliver')->upper()
devuelven lo mismo.
De esta manera, evitamos tener que importar la clase Str
cada vez que queramos manipular un string.
- use Illuminate\Support\Str;
- Str::of('Oliver')->upper(); // OLIVER
+ str('Oliver')->upper(); // OLIVER
Helper to_route()
to_route
es un nuevo helper para realizar redirecciones de una forma más limpia.
Por ejemplo, redirect()->route('welcome')
y to_route('welcome')
redirigen a la misma ruta con nombre welcome
.
// ...
- return redirect()->route('welcome');
+ return to_route('welcome');
Rediseño de la página de error
La página de error de Ignition ha sido rediseñada para Laravel 9.
Con un nuevo menú superior para acceder al stack
, el contexto, la documentación y configuración de ignition.
Quizá más intuitiva ahora.
En Laravel 8, lucía así.
Renderizar un string con Blade
Con el método Blade::render()
, ahora es posible convertir un string con sintaxis Blade a su equivalente en HTML.
Por ejemplo, Blade::render('Hola {{ $user->name }}', ['user' => auth()->user()])
devolverá "Hola Oliver".
// ...
return Blade::render('Hola {{ $user->name }}', ['user' => auth()->user()]);
// Hola Oliver
Scoped bindings forzados
Cuando vinculamos múltiples modelos Eloquent a una misma ruta podemos hacerlos de tal manera que el segundo modelo de Eloquent pertenezca al primer modelo con el método scopeBindings
.
Por ejemplo, en la siguiente ruta, buscará y devolverá el podcast únicamente si pertenece al usuario en la ruta.
De lo contrario devolverá un error 404 de que no fue encontrado.
use App\Models\Podcast;
use App\Models\User;
Route::get('/users/{user}/podcasts/{podcast}', function (User $user, Podcast $podcast) {
return $podcast;
})->scopeBindings();
Cobertura de tests
Laravel 9 introduce una nueva opción --coverage
al comando Artisan test
.
Habilitando esta opción nos genera un reporte de la cobertura que tienen nuestros tests en el proyecto.
Esta opción requiere que tengamos instalado XDebug.
En Mac y con Homebrew instalado, podemos hacerlo con el comando pecl install xdebug
. Modificamos nuestro archivo php.ini
para añadir la configuración XDEBUG_MODE=coverage
y reiniciamos PHP.
Ahora podemos ejecutar el comando Artisan php artisan test --coverage
para ejecutar los tests y generarnos un reporte.
En este caso la cobertura es del 54.2%.
Si queremos establecer una cobertura mínima en nuestro proyecto, podemos hacerlos con la opción --min
, php artisan test --coverage --min=70
De esta manera nos arrojará error si no cumplimos con la mínima cobertura.
Motor "base de datos" para Laravel Scout
Para bases de datos pequeñas, Laravel ahora incluye un motor "base de datos" para usarlo junto con Laravel Scout. De esta manera, no tenemos que utilizar o instalar un servicio como Algolia o MeiliSearch.
Para utilizar este motor, simplemente debemos añadir la variable SCOUT_DRIVER=database
en nuestro archivo .env
.
Hay que tomar en cuenta que este motor únicamente funciona para bases de datos MySQL y Postgres.
Índices Full Text
En bases de datos MySQL o PostgreSQL podemos definir columnas del tipo fullText
para generar índices Full Text:
$table->text('description')->fullText();
De esta manera podemos realizar consultas del tipo MATCH AGAINST
, en el caso de MySQL:
$podcasts = Podcast::whereFullText('description', 'Laravel')->get();
Bastante útil para realizar búsquedas de texto simples.
Aporta un mejor rendimiento que con consultas del tipo where like
.
Nueva API para accesores y mutadores
Ahora podemos definir nuestro accesores y mutadores de una forma más limpia.
Anteriormente, para definir un accesores que nos devuelva el valor en mayúsculas y un mutador que guarde el valor en minúsculas utilizábamos la convención getXAttribute y setXAttribute:
public function getUsernameAttribute($value)
{
return str($value)->upper();
}
public function setUsernameAttribute($value)
{
return str($value)->lower();
}
Ahora, podemos hacerlo con un solo método que declare que devolveremos un objeto Illuminate\Database\Eloquent\Casts\Attribute
.
use Illuminate\Database\Eloquent\Casts\Attribute;
// ...
public function username(): Attribute
{
return new Attribute(
get: fn($value) => str($value)->upper(),
set: fn($value) => str($value)->lower()
);
}