Actualizar Laravel 9 a 13, de una versión en una
Laravel publica una versión mayor cada año, y cada una viene con una guía de actualización escrita para la versión inmediatamente anterior. Esa es la estructura de la que depende todo el proceso: la documentación existe para 9 a 10, 10 a 11, 11 a 12 y 12 a 13, y no existe nada para 9 a 13.
Así que la secuencia son cuatro trabajos separados, cada uno con una aplicación que funciona y está confirmada al final.
Por qué de un salto en uno
Composer no le impedirá saltar. Ponga laravel/framework en ^13.0, resuelva las peleas que estallen entre sus otras dependencias, y al final conseguirá una instalación.
Después estará depurando cuatro versiones de cambios incompatibles a la vez, sin forma de atribuir un fallo al cambio que lo causó. Algunos interactúan: una modificación de columna que pierde en silencio un atributo en 11 produce un esquema que se comporta de forma rara en 13 por razones sobre las que la guía de 13 no dice nada.
De uno en uno, cada salto es una guía que puede seguir línea a línea, una ejecución de tests y un commit. Si algo se rompe dos saltos después, tiene un historial que puede bisecar en lugar de un cambio enorme.
Salto 1: Laravel 9 a 10
Estimación de la propia Laravel: 10 minutos. Requiere PHP 8.1 y Composer 2.2.
Es el más suave de los cuatro. Los elementos de alto impacto son subidas de dependencias y volver a poner minimum-stability en stable.
Lo que pilla de verdad a las aplicaciones:
- La propiedad
$datesde los modelos desaparece. Llevaba años deprecada y aquí es donde se elimina. Mueva las columnas a$castscon'datetime'. - Las expresiones
DB::rawya no pueden convertirse a cadena. Si su código hace(string) $expressiono llama a__toString, usegetValue($grammar)en su lugar. La mayoría de las aplicaciones nunca tocan esto. - Monolog 3. Si interactúa con Monolog directamente, o usa un servicio de logging de terceros, esos paquetes necesitan versiones que lo soporten.
MocksApplicationServicesse elimina, y se llevaexpectsEvents,expectsJobsyexpectsNotificationscon él. Sustitúyalos porEvent::fake(),Bus::fake()yNotification::fake(). Este solo afecta a su suite de tests, que es el tipo bueno de rotura.
Salto 2: Laravel 10 a 11
Estimación de la propia Laravel: 15 minutos. Requiere PHP 8.2, curl 7.34 y SQLite 3.26 si usa SQLite.
Este es el salto que cuesta tiempo de verdad, y dos de sus elementos merecen una mirada detenida.
Las modificaciones de columnas ahora descartan todo lo que no vuelva a declarar. Este es el que cambia su base de datos en silencio. En Laravel 10, cambiar una columna conservaba sus otros atributos. En 11, cada modificador que quiera conservar tiene que nombrarse otra vez:
// Laravel 10: keeps unsigned, default and comment
$table->integer('votes')->nullable()->change();
// Laravel 11: they are dropped unless you restate them
$table->integer('votes')
->unsigned()
->default(1)
->comment('The vote count')
->nullable()
->change();
Cada ->change() de su historial de migraciones es candidato. La sugerencia de la propia Laravel, si auditarlos todos no es práctico, es aplanar el historial con php artisan schema:dump para que el archivo de esquema sea el punto de partida.
Los paquetes oficiales dejan de incluir sus propias migraciones. Cashier, Passport, Sanctum, Spark y Telescope ya no cargan migraciones desde sus propios directorios, así que las publica en su aplicación:
php artisan vendor:publish --tag=sanctum-migrations
Pase esto por alto y las tablas que esos paquetes necesitan dejan de crearse en una instalación limpia, lo que normalmente aparece en la CI o en la máquina de un desarrollador nuevo, algún tiempo después de que creyera que la actualización estaba terminada.
El resto del salto:
- Los tipos de columna de coma flotante se reescribieron.
doubleyfloatya no aceptan total y decimales;unsignedDecimal,unsignedDoubleyunsignedFloatdesaparecen. - Sanctum 4, y subidas de versión mayor en todos los paquetes oficiales. Sanctum también necesita actualizar las referencias a middleware en su configuración.
- Doctrine DBAL ya no es una dependencia y sus métodos asociados se eliminan. Los métodos de esquema nativos de Laravel los sustituyen.
- Carbon 3 se soporta aquí junto a Carbon 2. Si pasa a 3, los métodos
diffIn*devuelven floats y pueden ser negativos, que es un cambio de comportamiento que no se anunciará: sencillamente hará que una duración sea incorrecta en algún sitio. - El límite de tasa pasó de minutos a segundos internamente. El uso documentado a través de
Limit::perMinuteno se ve afectado; la construcción manual de objetosLimitsí.
Una cosa que puede saltarse: Laravel 11 introdujo un esqueleto de aplicación más ligero, y la indicación de Laravel es explícitamente no migrar su estructura a él. Laravel 11 soporta la estructura de Laravel 10 a propósito. Reestructurar es un proyecto opcional, y mezclarlo con una actualización es como pierde la capacidad de saber qué cambio rompió algo.
Salto 3: Laravel 11 a 12
Estimación de la propia Laravel: 5 minutos. El más tranquilo de los cuatro.
HasUuidsahora produce UUIDv7 en lugar de v4 ordenados. Si sus IDs tienen que seguir siendo v4, cambie el trait porHasVersion4Uuids. Las filas existentes no se tocan; las nuevas cambian de forma.- Se elimina el soporte de Carbon 2. Si aplazó la decisión sobre Carbon 3 durante el salto 2, cae aquí.
- La regla de validación
imageya no acepta SVG. Un valor por defecto sensato, porque los SVG pueden llevar script. Si los acepta a propósito, dígalo:image:allow_svg. - El disco de sistema de archivos
localpasa por defecto astorage/app/privateen lugar destorage/app, si nunca definió el disco explícitamente. Compruébelo antes de que cambie dónde aterrizan sus archivos.
Salto 4: Laravel 12 a 13
Estimación de la propia Laravel: 10 minutos. Requiere PHP 8.3.
Laravel dice que en este ciclo se centró en minimizar los cambios incompatibles, y la guía lo confirma: un solo elemento de alto impacto más allá de las subidas de dependencias.
El middleware de CSRF cambia de nombre. VerifyCsrfToken pasa a ser PreventRequestForgery, que además añade verificación del origen mediante la cabecera Sec-Fetch-Site. Los nombres antiguos se mantienen como alias deprecados, así que esto no rompe nada en tiempo de ejecución, pero las referencias directas deberían actualizarse, en particular withoutMiddleware([VerifyCsrfToken::class]) en los tests.
Dos más con las que es fácil tropezar:
serializable_classesen la configuración de caché pasa por defecto afalse, endureciendo contra ataques de deserialización si suAPP_KEYse filtra. Si cachea objetos PHP a propósito, liste las clases explícitamente o pase a arrays.- Serialización de sesión. El esqueleto de Laravel 13 pone
serializationde sesión enjson. Si sincroniza sus archivos de configuración con el esqueleto nuevo, cambiar ese valor dephpajsoninvalida todas las sesiones activas: todo el que tenga sesión iniciada queda fuera. Bien si lo planifica, alarmante un lunes por la mañana si no.
Sobre esas estimaciones de tiempo
De cinco a quince minutos por salto, y son honestas, para lo que miden. Describen los cambios incompatibles del framework sobre una aplicación de serie.
No cubren las tres cosas que llevan el tiempo de verdad:
Sus dependencias. Cada paquete que usa necesita una versión compatible con la versión objetivo, y un solo paquete abandonado sin versión compatible puede bloquear todo el salto. Compruébelo antes de empezar, no después.
Su propio código. La guía le dice qué cambió en el framework, no cuáles de sus archivos dependen del comportamiento antiguo.
Verificar que sigue funcionando. Esto domina todo lo demás, y cuánto tarda lo decide si tiene tests.
Ese último punto merece franqueza. Con una suite decente, una actualización es: cambiar la restricción, ejecutar los tests, arreglar lo que esté en rojo, publicar. Sin ella, es una prueba de regresión manual de toda su aplicación hecha por alguien que tiene que adivinar qué hace la aplicación. Si no tiene tests y va cuatro versiones por detrás, escribir primero tests para sus rutas críticas no es un desvío: es lo que hace posible el resto.
El orden de las operaciones
Para cada salto: compruebe la compatibilidad de los paquetes, actualice PHP si este salto lo requiere, suba el framework y las dependencias, recorra la guía, ejecute los tests, recorra a mano las partes que ningún test cubre, confirme.
Después haga el siguiente.
Ponerse al día es el objetivo, y mantenerse al día es el sentido. Una vez que esté en 13, el salto del año siguiente es un paso documentado desde donde ya está, que es algo muy distinto de los cuatro que acaba de hacer. Dónde está hoy tiene las fechas de soporte con las que planificar el siguiente.
Hacemos actualizaciones de Laravel en aplicaciones a medida y en sistemas web heredados, incluidos los que van varias versiones por detrás sin tests en los que apoyarse. Si quiere saber qué haría falta para ponerse al día, cuéntenos qué está ejecutando.
Preguntas frecuentes
¿Puedo saltar directamente de Laravel 9 a Laravel 13?
Composer le dejará intentarlo, y estará depurando cuatro versiones de cambios incompatibles a la vez sin forma de saber cuál causó qué. Ir de una versión en una significa que cada paso tiene una guía escrita exactamente para esa transición, y una aplicación que funciona al final que puede confirmar antes de seguir.
¿Cuánto tarda una actualización de Laravel en la práctica?
Para una aplicación que va una versión por detrás, bien mantenida y con tests, un día es realista. Para una que va cuatro versiones por detrás, con mucho uso de paquetes y sin tests, planifique en semanas, la mayoría de las cuales se van en dependencias y en verificar el comportamiento y no en el framework en sí.
¿Qué es Laravel Shift?
Un servicio comercial que automatiza buena parte del trabajo mecánico de una actualización, y al que las propias guías de actualización de Laravel hacen referencia. No tenemos relación con él ni ningún interés en si lo usa. No elimina la necesidad de probar el resultado.