Aunque han pasado unos días desde que en febrero se lanzase Laravel 12 (tal como anunciamos en nuestro anterior post), hemos querido dar un repaso un tanto especial a las novedades que ésta nueva versión nos presenta. Para ello, además de mostraros las novedades que trae consigo la nueva versión, os presentamos un caso de uso realista en el que hemos aplicado las nuevas funcionalidades y hemos tratado de explicarlas un poco.
Novedades principales de Laravel 12
Laravel 12 presenta principalmente mejoras incrementales:
- Uso de UUID v7 como identificadores únicos ordenados cronológicamente.
- Nuevo método del Query Builder
nestedWhere()
para simplificar consultas complejas. - Mejoras en la validación de archivos, especialmente imágenes SVG (restringidas por defecto).
- Ejecución paralela mejorada con
Concurrency::run()
manteniendo claves asociativas. - Método mejorado para colecciones:
range()
con pasos variables. - Starter Kits renovados con autenticación avanzada incluida.
Caso de uso: Aplicación «Gestor de Tareas»
Instalación del proyecto
composer create-project laravel/laravel:^12.0 gestor-tareas npm install && npm run dev php artisan migrate php artisan storage:link
Migración para tareas
// database/migrations/create_tasks_table.php public function up() { Schema::create('tasks', function (Blueprint $table) { $table->uuid('id')->primary(); // UUID v7 por defecto en Laravel 12 $table->foreignId('user_id')->constrained()->cascadeOnDelete(); $table->string('titulo'); $table->text('descripcion')->nullable(); $table->string('imagen_path')->nullable(); $table->timestamps(); }); }
Explicación:
El campo id
utiliza UUID v7 automáticamente gracias al trait HasUuids
.
Modelo Task
namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Concerns\HasUuids; class Task extends Model { use HasUuids; protected $fillable = ['titulo', 'descripcion', 'imagen_path', 'user_id']; public function user() { return $this->belongsTo(User::class); } }
Explicación:
El trait HasUuids
asegura que cada tarea creada tenga automáticamente un UUID v7.
Controlador de tareas
class TaskController extends Controller { public function __construct() { $this->middleware('auth'); } public function index() { $tareas = Task::where('user_id', auth()->id())->latest()->get(); return view('tasks.index', compact('tareas')); } public function store(Request $request) { $request->validate([ 'titulo' => 'required|string|max:255', 'descripcion'=> 'nullable|string', 'imagen'=> ['nullable', File::image()] // No permite SVG por defecto ]); $request->mergeIfMissing(['descripcion' => '']); $datos = $request->only(['titulo', 'descripcion']); if ($request->hasFile('imagen')) { $datos['imagen_path'] = $request->file('imagen')->store('imagenes', 'public'); } $datos['user_id'] = Auth::id(); Task::create($datos); return redirect()->route('tasks.index')->with('success', 'Tarea creada.'); } }
Explicación:
Demuestra validación avanzada (sin SVG), asignación automática del usuario autenticado y el uso del método mergeIfMissing
.
Uso del método nestedWhere()
Ejemplo para filtrar tareas que cumplen múltiples condiciones fácilmente:
$tareasImportantes = Task::nestedWhere( 'prioridad', '=', 'alta', 'or', 'fecha_limite', '<', now()->addDays(7) )->get();
Explicación:
Este método facilita hacer consultas complejas más legibles.
Comando para ejecutar tareas concurrentes
class ComputeStats extends Command { protected $signature = 'compute:stats'; public function handle() { $resultados = Concurrency::run([ 'conteo_tareas' => fn() => DB::table('tasks')->count(), 'calculo_matematico' => fn() => array_sum(range(1, 1000000)), ]); foreach ($resultados as $clave => $valor) { $this->info("{$clave}: {$valor}"); } } }
Explicación:
Ejecuta dos procesos simultáneos y muestra cómo Laravel 12 preserva las claves definidas originalmente.
Ejemplo del nuevo método range()
en colecciones
$numeros = Collection::range(1, 10, 2)->all(); // [1,3,5,7,9]
Explicación:
El método range()
ahora permite generar rangos con saltos especificados.
Ejemplo de prueba unitaria usando Pest PHP v3
use App\Models\Task; use App\Models\User; test('crear tarea genera UUID v7', function () { $user = User::factory()->create(); $this->actingAs($user); $this->post('/tasks', ['titulo' => 'Ejemplo']); $tarea = Task::first(); expect($tarea->id)->toBeUuid(); });
Explicación:
Esta prueba verifica que el modelo genera correctamente un UUID v7 al crear nuevas tareas.
Cómo ejecutar el proyecto localmente:
- Instala dependencias PHP y JavaScript:
composer install npm install && npm run dev
- Ejecuta migraciones y vincula almacenamiento:
php artisan migrate php artisan storage:link
- Inicia servidor:
php artisan serve
- Ejecuta el comando concurrente desde consola:
php artisan compute:stats
Hasta aquí llega nuestro ejemplo. Esperamos que al probarlo en vuestras máquinas o dockers hayáis podido ver de cerca las novedades y que lo hayáis pasado bien con la guía de hoy.
Os espero en próximos artículos!