Laravel

Laravel 14: qué se rompe al actualizar y qué cambia solo

Autorangel cruz
Publicado
Lectura8 min de lectura
Laravel 14: qué se rompe al actualizar y qué cambia solo

Laravel 14 es la próxima versión mayor del framework y todavía no está lanzada: se desarrolla en la rama master de laravel/framework, requiere PHP 8.4 como mínimo y se espera para el primer trimestre de 2027. Lo que ya está mergeado se puede leer hoy: firmas que cambian de orden y comportamientos por defecto distintos sin que toques una línea.

Todo lo que sigue está comprobado en la rama master del repositorio de laravel/framework, contrastado contra la rama 13.x, el 21 de septiembre de 2026. 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.

Fecha de lanzamiento y soporte de Laravel 14

Laravel lanza una versión mayor por año, siempre alrededor del primer trimestre. La política de soporte del framework da 18 meses de bug fixes y 2 años de parches de seguridad a cada versión mayor.

Versión PHP Lanzamiento Bug fixes hasta Seguridad hasta
12 8.2–8.5 24 feb 2025 13 ago 2026 24 feb 2027
13 8.3–8.5 Q1 2026 Q3 2027 Q1 2028
14 8.4+ Q1 2027 (previsto) Q3 2028 Q1 2029

Las tres primeras columnas de las versiones 12 y 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 dos de soporte se derivan de aplicar los 18 meses y los 2 años a ese trimestre.

De la misma tabla sale un dato práctico: Laravel 12 dejó de recibir bug fixes el 13 de agosto de 2026 y pierde los parches de seguridad el 24 de febrero de 2027, más o menos cuando aparezca Laravel 14. Si tu aplicación sigue en 12, el salto que toca ahora es a 13.

PHP 8.4 como mínimo

Este es el único breaking change que afecta a todo el mundo por igual. El composer.json de la rama master declara "php": "^8.4", frente al "php": "^8.3" de la rama 13.x.

# Comprueba el runtime antes de cualquier otra cosa
php -v

Si tu servidor corre PHP 8.3, Laravel 14 no es una opción hasta que actualices el runtime, y te quedas en Laravel 13 (con bug fixes hasta Q3 2027, tiempo de sobra para planificarlo).

El salto arrastra el 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 las dependencias de 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 antes que nada.

Qué se rompe: firmas que cambian

Queue::pause() cambia el orden de los argumentos

Este es el cambio más fácil de pasar por alto, porque no falla en tiempo de compilació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 medio

Lo mismo aplica a pauseFor(), resume() e isPaused(). Si dejas el orden viejo, Queue::pause('redis', 'emails') pausará 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 nada en los logs te avisa. Busca cada llamada a mano:

grep -rn "Queue::pause\|Queue::pauseFor\|Queue::resume\|Queue::isPaused" app/ routes/ tests/

pauseAll(), resumeAll() y los paused de nivel global 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 IDs repetidos en la entrada 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ú mismo.

Defaults que cambian sin que toques nada

Aquí está el 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 sucio con el último limit y offset del recorrido, así que 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
$query->count();
 
// Laravel 14: el builder sigue intacto, el count es el real
$query->count();

El cambio arregla un comportamiento raro, pero lo hace en silencio. Si alguna parte de tu código compensaba la mutación reseteando límites a mano, o dependía de ella, ese apaño ahora sobra o miente.

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 del recorrido y sobrevivían al model:prune.

En Laravel 14, el mismo modelo y el mismo prunable() borran filas que antes se quedaban en la base de datos. 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 --pretend

Cache::has() y Cache::forget() aceptan arrays

En Laravel 13, Cache::has(['a', 'b']) pasaba el array a get() y la comprobación no significaba lo que parecía. En master, has() con un array resuelve por many() y devuelve true solo si ninguna de las claves falta; 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 dos

Si tenías un helper propio que iteraba las claves para simular esto, ya puedes tirarlo. Y si le pasabas un array a has() por accidente, el resultado que obtenías antes no significaba lo que parecía.

Novedades ya mergeadas en Laravel 14

Lo aditivo va en lista corta: es la parte que la documentación oficial cubrirá mejor cuando salga la versión.

Rutas con el método HTTP QUERY. QUERY entra en el array de verbos del router y aparece Route::query():

Route::query('/search', SearchController::class);

$user->authorize(). El contrato Illuminate\Contracts\Auth\Access\Authorizable ahora declara authorize($ability, $arguments = []) junto a can(), así que la autorización con excepción se puede pedir al propio usuario y no solo al Gate:

$user->authorize('update', $post);

Contexto en report(). Los tres helpers pasan a aceptar 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, ahora 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();

orWhereKey() y orWhereKeyNot() ya existían en Laravel 13; lo nuevo es el soporte de subqueries y los parámetros de booleano y negación.

Storage::fake('ondemand') para discos on-demand. FilesystemManager::build() pasa a devolver $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, eso hace testeable el código que crea discos al vuelo:

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 desde ahora

Todavía no hay nada que actualizar, pero hay trabajo que no depende del lanzamiento:

  1. 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.
  2. Haz inventario de las llamadas afectadas: Queue::pause y familia, findOr con arrays, Cache::has con arrays y los chunk/lazy cuyo builder se reutiliza después.
  3. Corre model:prune --pretend en los modelos con soft deletes para saber cuánto crece el borrado.
  4. Revisa la matriz de CI de tus paquetes: Symfony 8.1, testbench 12 y PHPUnit 11.5/12.5/13.

Cuando salga la versión, el upgrade guide oficial listará cada cambio con su etiqueta de impacto, y Laravel Shift automatiza buena parte del trabajo mecánico abriendo un pull request con commits atómicos.

Si vienes de más atrás, primero toca el salto a 13, y para eso están las novedades de Laravel 13 con su propia lista de breaking changes.


Preguntas frecuentes

¿Cuándo sale Laravel 14?

La previsión es el primer trimestre de 2027, siguiendo el ciclo anual del framework. No hay fecha exacta anunciada; Laravel 13 salió en Q1 2026 y Laravel 12 el 24 de febrero de 2025.

¿Qué versión de PHP necesita Laravel 14?

PHP 8.4 como mínimo, según el composer.json de la rama de desarrollo. Laravel 13 pedía 8.3. El salto va atado a Symfony 8, que también requiere 8.4.

¿Cuáles son los breaking changes de Laravel 14?

Los que ya están mergeados: 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.

¿Puedo probar Laravel 14 hoy?

Puedes leer y ejecutar la rama de desarrollo, aunque la API puede cambiar hasta el lanzamiento y no sirve para producción. Laravel 13 recibe bug fixes hasta Q3 2027.

¿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 primer trimestre de 2028, según la tabla de la política de soporte oficial.

¿Laravel 12 sigue soportado?

Solo en seguridad. Los bug fixes terminaron el 13 de agosto de 2026 y los parches de seguridad llegan hasta el 24 de febrero de 2027.


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.

Hablemos