Novedades de Laravel: qué cambia en Laravel 14 y Laravel 13

La versión actual de Laravel es la 13, lanzada el 17 de marzo de 2026 con PHP 8.3 como mínimo. La siguiente, Laravel 14, se desarrolla en la rama master de laravel/framework, pide PHP 8.4 y se espera para el primer trimestre de 2027. Este artículo junta las dos: qué se rompe al actualizar a cada una, qué comportamientos cambian sin que toques nada y qué trae de nuevo cada mayor. Cuando sale una versión nueva, se suma aquí.
Versiones de Laravel y soporte
Laravel lanza una versión mayor por año, alrededor del primer trimestre. Cada mayor recibe 18 meses de bug fixes y 2 años de parches de seguridad.
| Versión | PHP | Lanzamiento | Bug fixes hasta | Seguridad hasta |
|---|---|---|---|---|
| 11 | 8.2–8.4 | 12 mar 2024 | 3 sep 2025 | 12 mar 2026 |
| 12 | 8.2–8.5 | 24 feb 2025 | 13 ago 2026 | 24 feb 2027 |
| 13 | 8.3–8.5 | 17 mar 2026 | Q3 2027 | 17 mar 2028 |
| 14 | 8.4+ | Q1 2027 (previsto) | Q3 2028 | Q1 2029 |
Las filas de 11 a 13 salen de la tabla de la política de soporte de las notas de lanzamiento oficiales. Las fechas de Laravel 14 son previsiones: la de lanzamiento es la que publica Laravel News, y las de soporte resultan de aplicar los 18 meses y los 2 años a ese trimestre.
Laravel 11 ya no recibe ningún parche. Laravel 12 solo recibe parches de seguridad, hasta el 24 de febrero de 2027. Si tu aplicación está en cualquiera de las dos, el salto que toca es a 13, y Laravel 14 viene después.
Laravel 14: qué se rompe al actualizar
Laravel 14 todavía no está lanzado. Lo que sigue está en la rama master del repositorio, contrastado contra la rama 13.x. Es una rama de desarrollo, así que pueden entrar más cambios antes del lanzamiento; los que ya están mergeados rara vez se revierten.
PHP 8.4 como mínimo
El composer.json de master declara "php": "^8.4", frente al "php": "^8.3" de la rama 13.x. Es el único cambio que afecta a todo el mundo por igual.
# Comprueba el runtime antes de cualquier otra cosa
php -vSi tu servidor corre PHP 8.3, te quedas en Laravel 13 hasta que actualices el runtime, y Laravel 13 tiene bug fixes hasta Q3 2027.
El salto arrastra al resto de las dependencias. En master, los componentes de Symfony pasan a ^8.1.0 (console, http-foundation, http-kernel, routing, mailer, mime, process, finder, uid, var-dumper y error-handler), y Symfony 8 también pide PHP 8.4. En desarrollo, orchestra/testbench-core sube a ^12.0.0 y PHPUnit queda como ^11.5.50 || ^12.5.8 || ^13.0.3. Si mantienes un paquete con matriz de CI, esos tres son los que te van a romper el pipeline primero.
Queue::pause() cambia el orden de los argumentos
Este cambio no falla al cargar la aplicación: falla pausando la cola equivocada. En Laravel 13 la firma era pause($connection, $queue), con los dos argumentos obligatorios. En master es pause($queue, $connection = null), con la conexión opcional.
// Laravel 13
Queue::pause('redis', 'emails');
Queue::pauseFor('redis', 'emails', 60);
Queue::resume('redis', 'emails');
Queue::isPaused('redis', 'emails');
// Laravel 14
Queue::pause('emails'); // usa la conexión por defecto
Queue::pause('emails', 'redis'); // o la que le pases
Queue::pauseFor('emails', 60, 'redis'); // el TTL ahora va en el medioCon el orden viejo, Queue::pause('redis', 'emails') pausa una cola llamada redis en la conexión emails, que probablemente no exista. El worker sigue trabajando, la cola que querías detener sigue consumiendo y los logs no dicen nada. Busca cada llamada a mano:
grep -rnE "Queue::(pause|pauseFor|resume|isPaused)\(" app/ routes/ tests/pauseAll() y resumeAll() se quedan igual: no reciben argumentos.
findOr() con un array de IDs
En Laravel 13, findOr() con un array invocaba el callback solo si el resultado era null, y una colección vacía o incompleta no lo es. En master el callback se dispara si no se encontraron todos los IDs que pediste.
// IDs 1 y 2 existen, el 999 no
User::findOr([1, 2, 999], fn () => 'faltan usuarios');
// Laravel 13: devuelve la colección con los dos usuarios encontrados
// Laravel 14: ejecuta el callback y devuelve 'faltan usuarios'La comparación es contra array_unique($id), así que los IDs repetidos no cuentan de más. Si tu código dependía de recibir el subconjunto encontrado, cámbialo a find() y comprueba el conteo tú.
Laravel 14: defaults que cambian sin que toques nada
Esto es código que compila, pasa los tests y se comporta distinto en producción.
chunk() y lazy() ya no mutan el query builder
En Laravel 13, chunk() y lazy() aplicaban offset() y limit() sobre la propia instancia del builder. Al terminar, el builder quedaba con el último limit y offset del recorrido, y reutilizarlo devolvía resultados recortados. En master ambos métodos clonan el builder antes de iterar.
$query = User::where('active', true);
$query->chunk(100, function ($users) {
// procesa
});
// Laravel 13: el builder arrastra offset/limit del último chunk
// Laravel 14: el builder sigue intacto y el count es el real
$query->count();Si alguna parte de tu código compensaba la mutación reseteando límites a mano, ese apaño ahora sobra.
MassPrunable borra también los modelos soft deleted
El trait MassPrunable de master añade withTrashed() a la query cuando el modelo usa soft deletes, antes del forceDelete(). En Laravel 13 ya hacía forceDelete(), pero sin withTrashed(), así que los registros con deleted_at quedaban fuera y sobrevivían al model:prune.
En Laravel 14, el mismo modelo y el mismo prunable() borran filas que antes se quedaban. Si tu prunable() filtra por created_at y tienes años de registros soft deleted, el primer model:prune tras la actualización va a borrar mucho más de lo habitual.
# Mira el alcance antes de dejarlo suelto
php artisan model:prune --pretendCache::has() y Cache::forget() aceptan arrays
En Laravel 13, Cache::has(['a', 'b']) pasaba el array a get() y el resultado no significaba lo que parecía. En master, has() con un array resuelve por many() y devuelve true solo si están todas las claves; forget() con un array delega en deleteMultiple().
Cache::has(['user:1', 'user:2']); // true solo si están las dos
Cache::forget(['user:1', 'user:2']); // borra las dosSi tenías un helper propio que iteraba las claves para simular esto, ya puedes tirarlo.
Laravel 14: novedades ya mergeadas
Rutas con el método HTTP QUERY. QUERY entra en los verbos del router y aparece Route::query():
Route::query('/search', SearchController::class);$user->authorize(). El contrato Illuminate\Contracts\Auth\Access\Authorizable declara authorize($ability, $arguments = []) junto a can(), así que la autorización con excepción se le pide al propio usuario:
$user->authorize('update', $post);Contexto en report(). Los tres helpers aceptan un array de contexto que llega al handler de excepciones:
report($e, ['order_id' => $order->id]);
report_if($failed, $e, ['attempt' => $attempt]);
report_unless($ok, $e, ['payload' => $payload]);whereKey() acepta subqueries. Además de un ID, un array o un modelo, recibe un Closure, un query builder o una relación, y los resuelve con whereIn. La firma pasa a whereKey(mixed $id, string $boolean = 'and', bool $not = false):
$posts = Post::whereKey(fn ($query) => $query
->select('post_id')
->from('featured_posts')
)->get();Storage::fake('ondemand') para discos on-demand. FilesystemManager::build() devuelve $this->disks['ondemand'] si ya existe, en lugar de resolver siempre un disco nuevo. Como Storage::fake() registra el disco con el nombre que le pasas, el código que crea discos al vuelo se vuelve testeable:
Storage::fake('ondemand');
// Dentro del código bajo prueba, esto ahora devuelve el disco falso
Storage::build(['driver' => 's3', 'bucket' => 'facturas']);Cómo preparar la actualización a Laravel 14
- Sube el runtime a PHP 8.4. Laravel 13 soporta 8.3, 8.4 y 8.5, así que puedes mover PHP sin tocar el framework.
- Haz inventario de las llamadas afectadas:
Queue::pausey familia,findOrcon arrays,Cache::hascon arrays y loschunk/lazycuyo builder se reutiliza después. - Corre
model:prune --pretenden los modelos con soft deletes. - Revisa la matriz de CI de tus paquetes: Symfony 8.1, testbench 12 y PHPUnit 11.5/12.5/13.
Laravel 13: qué se rompe al actualizar desde Laravel 12
La guía oficial estima unos 10 minutos para la mayoría de las aplicaciones. Marca como impacto alto tres cosas: las dependencias, el instalador y la protección contra request forgery.
Dependencias y PHP 8.3
Laravel 13 requiere PHP 8.3. En composer.json:
laravel/framework → ^13.0
laravel/boost → ^2.0
laravel/tinker → ^3.0
phpunit/phpunit → ^12.0
pestphp/pest → ^4.0Si creas proyectos con el instalador, actualízalo también: composer global update laravel/installer, o la versión de Herd si usas la que trae Herd.
VerifyCsrfToken pasa a llamarse PreventRequestForgery
El middleware de CSRF cambia de nombre y además verifica el origen de la petición con el header Sec-Fetch-Site. VerifyCsrfToken y ValidateCsrfToken quedan como alias deprecados, así que nada se rompe el primer día, pero las referencias directas conviene cambiarlas, sobre todo en tests:
use Illuminate\Foundation\Http\Middleware\PreventRequestForgery;
// Laravel 12
->withoutMiddleware([VerifyCsrfToken::class]);
// Laravel 13
->withoutMiddleware([PreventRequestForgery::class]);La caché deja de deserializar objetos arbitrarios
La configuración de cache trae una opción nueva, serializable_classes, en false. Si guardas objetos PHP en caché, tienes que listar las clases permitidas o pasar a guardar arrays:
'serializable_classes' => [
App\Data\CachedDashboardStats::class,
],upsert() exige uniqueBy en MySQL y MariaDB
upsert() lanza InvalidArgumentException si uniqueBy va vacío, también en MySQL y MariaDB, donde ese valor se ignora porque el motor usa los índices de la tabla. Antes generaba SQL inválido.
Cambios de bajo impacto
- Prefijos de caché, Redis y cookie de sesión: los valores por defecto pasan de guion bajo a guion (
laravel-cache-en vez delaravel_cache_). Solo te afecta si tu configuración no los define; para conservar los viejos, fijaCACHE_PREFIX,REDIS_PREFIXySESSION_COOKIEen.env. - Sesión en JSON: el esqueleto nuevo pone
serializationenjsonenconfig/session.php. Si sincronizas ese archivo con el esqueleto, todas las sesiones activas se invalidan; para conservarlas, dejaphp. Container::call()respeta el defaultnullde un parámetro nullable sin binding, igual que la inyección por constructor.- MySQL
DELETEconJOINincluyeORDER BYyLIMIT. En MySQL, y en MariaDB anterior a 11.8.1, eso puede lanzarQueryExceptiondonde antes había un borrado sin límite. - Rutas con dominio se evalúan antes que las rutas sin dominio.
- Eventos de cola:
JobAttempted::$exceptionOccurredpasa a$exception(el objeto onull), yQueueBusy::$connectionpasa a$connectionName. - Pivots polimórficos con clase propia: el nombre de tabla inferido pasa a plural. Define
$tablesi dependías del singular. - Colecciones serializadas (en jobs, por ejemplo) restauran las relaciones eager-loaded.
- Vistas de paginación:
pagination::defaultpasa apagination::bootstrap-3, ypagination::simple-defaultapagination::simple-bootstrap-3. Strfactories se resetean entre tests.array_first()yarray_last(): Laravel 13 depende desymfony/polyfill-php85, que define esas funciones globales en PHP < 8.5 y choca con helpers viejos del mismo nombre. UsaArr::first().
Cómo actualizar a Laravel 13
- Comprueba que el servidor corre PHP 8.3 o superior.
- Actualiza las dependencias de
composer.jsony correcomposer update. - Busca referencias a
VerifyCsrfTokeny objetos PHP guardados en caché. - Repasa la lista de bajo impacto contra tu código.
Si tienes Laravel Boost ^2.0 instalado, el comando /upgrade-laravel-v13 guía la actualización desde Claude Code, Cursor, OpenCode, Gemini o VS Code. Laravel Shift la automatiza con un pull request.
Laravel 13: qué trae de nuevo
Laravel AI SDK
El paquete de primera parte laravel/ai da una API única, agnóstica del proveedor, para generación de texto, agentes con tool-calling, embeddings, audio, imágenes y vector stores. Se instala aparte con composer require laravel/ai.
use App\Ai\Agents\SalesCoach;
$response = SalesCoach::make()->prompt('Analiza esta transcripción de ventas...');
return (string) $response;Búsqueda vectorial con pgvector
El query builder trae cláusulas de similitud vectorial sobre PostgreSQL con pgvector, y los embeddings se generan desde un string:
$documents = DB::table('documents')
->whereVectorSimilarTo('embedding', 'Las mejores bodegas de la Rioja')
->limit(10)
->get();Si usas MariaDB, la búsqueda vectorial en Laravel con MariaDB tiene los requisitos y el SQL que se genera en cada motor.
JSON:API, queue routing, attributes y Cache::touch()
-
Recursos JSON:API de primera parte: serialización, inclusión de relaciones, sparse fieldsets, links y los headers correctos.
-
Queue routing por clase: la conexión y la cola de un job se definen en un solo sitio.
Queue::route(ProcessPodcast::class, connection: 'redis', queue: 'podcasts'); -
Más PHP attributes:
#[Middleware]y#[Authorize]en controladores,#[Tries],#[Backoff],#[Timeout]y#[FailOnTimeout]en jobs, y otros en Eloquent, eventos, notificaciones, validación y testing. Las propiedades de siempre siguen funcionando. -
Cache::touch()extiende el TTL de un item sin leerlo ni volver a guardarlo.
Para lo que viene después de actualizar, del deploy al monitoreo, está Laravel en producción.
Preguntas frecuentes
¿Cuál es la última versión de Laravel?
Laravel 13, lanzado el 17 de marzo de 2026. Laravel 14 está en desarrollo y se espera para el primer trimestre de 2027.
¿Qué versión de PHP necesita cada versión de Laravel?
Laravel 13 necesita PHP 8.3 y soporta hasta 8.5. Laravel 14 pide PHP 8.4 como mínimo, atado a Symfony 8. Laravel 12 soporta de 8.2 a 8.5.
¿Cuándo sale Laravel 14?
La previsión es el primer trimestre de 2027, siguiendo el ciclo anual. Laravel 13 salió el 17 de marzo de 2026 y Laravel 12 el 24 de febrero de 2025.
¿Cuáles son los breaking changes de Laravel 14?
Los mergeados hasta ahora: PHP 8.4 mínimo, el nuevo orden de argumentos en Queue::pause() y sus hermanos, findOr() con arrays invocando el callback cuando faltan IDs, chunk() y lazy() sin mutar el builder, MassPrunable incluyendo los modelos soft deleted, y Cache::has()/Cache::forget() tratando los arrays como tales.
¿Qué cambia entre Laravel 12 y Laravel 13?
PHP 8.3 como mínimo, el middleware de CSRF renombrado a PreventRequestForgery, la caché que ya no deserializa objetos arbitrarios y upsert() exigiendo uniqueBy. Lo nuevo: el AI SDK, búsqueda vectorial, recursos JSON:API, queue routing y más attributes.
¿Laravel 12 sigue soportado?
Solo con parches de seguridad, hasta el 24 de febrero de 2027. Los bug fixes terminaron el 13 de agosto de 2026.
¿Hasta cuándo tengo soporte si me quedo en Laravel 13?
Bug fixes hasta el tercer trimestre de 2027 y parches de seguridad hasta el 17 de marzo de 2028.
Referencias
¿Tienes un proyecto en mente?
Trabajo con Laravel, WordPress, SEO técnico y servidores MCP. El primer paso es una llamada de descubrimiento, sin costo ni compromiso, donde me cuentas qué necesitas y te digo con honestidad si puedo ayudarte.