Saltar al contenido principal

Introducción

Laravel 12 se lanzó en febrero de 2025. Esta guía explica los pasos para actualizar de Laravel 11.x a 12.x.
El tiempo estimado necesario para la actualización es de unos 5 minutos. Sin embargo, el impacto de los cambios disruptivos en tu aplicación varía según el tamaño y las funcionalidades utilizadas.

Actualización automatizada con Laravel Shift

Puedes automatizar la actualización con Laravel Shift. Shift actualiza automáticamente las dependencias y los archivos de configuración de tu aplicación.

Cambios por nivel de impacto

Impacto: alto

  • Actualización de dependencias
  • Actualización del instalador de Laravel

Impacto: medio

  • Trait HasUuids de Eloquent y UUIDv7

Impacto: bajo

  • Carbon 3
  • Mapeo por índice de resultados en Concurrency
  • Resolución de dependencias de clase en el contenedor
  • Exclusión de SVG en la validación de imagen
  • Ruta raíz por defecto del disco de sistema de archivos local
  • Inspección de base de datos con múltiples schemas
  • Merge de peticiones con arrays anidados

Pasos de actualización

Actualización de dependencias

Impacto: alto Actualiza las siguientes dependencias en composer.json.
{
  "require": {
    "laravel/framework": "^12.0"
  },
  "require-dev": {
    "phpunit/phpunit": "^11.0",
    "pestphp/pest": "^3.0"
  }
}
Después, instala las dependencias con el siguiente comando.
composer update

Actualización del instalador de Laravel

Impacto: alto Si creas nuevas aplicaciones de Laravel con el CLI del instalador, actualízalo a la versión compatible con Laravel 12.x y con los nuevos starter kits. Si lo instalaste con composer global require:
composer global update laravel/installer
Si lo instalaste con php.new, ejecuta el comando de reinstalación según tu SO.
/bin/bash -c "$(curl -fsSL https://php.new/install/mac/8.4)"
Si usas la versión bundled de Laravel Herd, actualiza el propio Herd a la última release.

Nuevos starter kits

En Laravel 12 se han introducido nuevos starter kits que sustituyen a Breeze y Jetstream. Al ejecutar laravel new puedes elegir de forma interactiva el stack de frontend. Starter kits disponibles:
  • React: Inertia 2, React 19, TypeScript, Tailwind 4, shadcn/ui
  • Vue: Inertia 2, Vue Composition API, TypeScript, Tailwind 4, shadcn-vue
  • Svelte: Inertia 2, Svelte 5, TypeScript, Tailwind 4, shadcn-svelte
  • Livewire: Livewire, Tailwind 4, Flux UI
laravel new my-app
Al ejecutar el comando se te pide elegir un starter kit. Tras seleccionarlo, basta con instalar las dependencias y arrancar el servidor de desarrollo.
cd my-app
npm install && npm run build
composer run dev
Si estás actualizando una aplicación existente, no hace falta reintroducir el starter kit. Es un cambio para proyectos nuevos.

Cambios disruptivos (Breaking Changes)

Eloquent

Trait HasUuids y UUIDv7

Impacto: medio El trait HasUuids ahora devuelve UUID v7 (UUID ordenables). Si quieres mantener el comportamiento anterior de UUID v4 ordenables, cambia al trait HasVersion4Uuids.
use Illuminate\Database\Eloquent\Concerns\HasUuids; // [tl! remove]
use Illuminate\Database\Eloquent\Concerns\HasVersion4Uuids as HasUuids; // [tl! add]
Si venías usando el trait HasVersion7Uuids, cámbialo al trait HasUuids (ahora tiene el mismo comportamiento).

Validación

Exclusión de SVG en la validación de imagen

Impacto: bajo La regla de validación image ya no permite SVG por defecto. Si quieres permitir SVG, indícalo explícitamente.
use Illuminate\Validation\Rules\File;

'photo' => 'required|image:allow_svg'

// o bien
'photo' => ['required', File::image(allowSvg: true)],

Storage

Ruta raíz por defecto del disco de sistema de archivos local

Impacto: bajo Si no defines explícitamente el disco local en la configuración de filesystems, la ruta raíz por defecto ha cambiado de storage/app a storage/app/private. Para mantener el comportamiento anterior, define el disco local explícitamente en el archivo de configuración.
// config/filesystems.php
'local' => [
    'driver' => 'local',
    'root' => storage_path('app'),
],

Base de datos

Inspección de base de datos con múltiples schemas

Impacto: bajo Schema::getTables(), Schema::getViews() y Schema::getTypes() devuelven ahora por defecto los resultados de todos los schemas. Para acotar a un schema concreto, pasa el argumento schema.
// Tablas de todos los schemas
$tables = Schema::getTables();

// Solo tablas del schema 'main'
$tables = Schema::getTables(schema: 'main');
Schema::getTableListing() devuelve ahora por defecto los nombres de tabla calificados con el schema.
$tables = Schema::getTableListing();
// ['main.migrations', 'main.users', 'blog.posts']

$tables = Schema::getTableListing(schema: 'main', schemaQualified: false);
// ['migrations', 'users']

Cambios en la firma del constructor de Database

Impacto: muy bajo Los constructores de Illuminate\Database\Schema\Blueprint e Illuminate\Database\Grammar requieren ahora una instancia de Connection como primer argumento.
// Laravel <= 11.x
$grammar = new MySqlGrammar;
$grammar->setConnection($connection);

// Laravel >= 12.x
$grammar = new MySqlGrammar($connection);
Además, se han eliminado o marcado como obsoletas las siguientes APIs.
APIEstado
Blueprint::getPrefix()Obsoleta
Connection::withTablePrefix()Eliminada
Grammar::getTablePrefix() / setTablePrefix()Obsoletas
Grammar::setConnection()Eliminada
Obtén el prefijo de tabla directamente desde la conexión.
$prefix = $connection->getTablePrefix();

Concurrency

Mapeo por índice de resultados

Impacto: bajo Al pasar un array asociativo a Concurrency::run(), los resultados se devuelven ahora con la clave correspondiente.
$result = Concurrency::run([
    'task-1' => fn () => 1 + 1,
    'task-2' => fn () => 2 + 2,
]);

// ['task-1' => 2, 'task-2' => 4]

Contenedor

Valor por defecto en la resolución de dependencias de clase

Impacto: bajo El contenedor de DI ahora respeta los valores por defecto de las propiedades de clase al resolver una instancia de clase.
class Example
{
    public function __construct(public ?Carbon $date = null) {}
}

$example = resolve(Example::class);

// Laravel <= 11.x: devuelve una instancia de Carbon
// Laravel >= 12.x: devuelve null

Request

Merge de peticiones con arrays anidados

Impacto: bajo Ahora puedes hacer merge con $request->mergeIfMissing() de datos de array anidados usando notación por puntos. Antes, las claves con notación por puntos se creaban tal cual como claves de nivel superior.
$request->mergeIfMissing([
    'user.last_name' => 'Otwell',
]);

Enrutamiento

Prioridad de rutas

Impacto: bajo Se ha unificado el comportamiento con y sin caché cuando hay varias rutas con el mismo nombre. Incluso sin caché de rutas, ahora tiene prioridad la ruta registrada primero, no la última.

Autenticación

Cambios en el constructor de DatabaseTokenRepository

Impacto: muy bajo El constructor de Illuminate\Auth\Passwords\DatabaseTokenRepository recibe ahora el parámetro $expires en segundos en lugar de en minutos.

Carbon 3

Impacto: bajo Se ha eliminado el soporte de Carbon 2.x. Todas las aplicaciones de Laravel 12 requieren Carbon 3.x.

Conclusión

Laravel 12 es una versión que se puede actualizar de forma relativamente fluida. Resumen de los principales puntos a tener en cuenta.
CambioImpactoAcción
Actualización de dependencias en composer.jsonAltoCambiar a laravel/framework ^12.0
Actualización del instalador de LaravelAltocomposer global update
HasUuids → UUIDv7MedioSi es necesario, cambiar a HasVersion4Uuids
Exclusión de SVG en la validación imageBajoAñadir allow_svg si quieres permitir SVG
Cambio de la ruta raíz por defecto del disco localBajoIndicarla explícitamente en el archivo de configuración
Carbon 3 obligatorioBajoComprobar la compatibilidad de la API

Referencias

Última modificación el 13 de julio de 2026