← IT Knowledge Base Home

Laravel — Fundamentos, Ciclo de Petición, Routing, Eloquent, Blade, Frontend, Autenticación, APIs, Colas, Testing, Rendimiento y Despliegue

Pregunta de origen (entrevista / cuestionario): ¿Cuál es su experiencia con Laravel (framework PHP) en desarrollo de aplicaciones, modelado de datos, APIs, seguridad, pruebas y despliegue en producción?

Esta página cubre 10 bloques del temario: fundamentos y ecosistema (Q1–Q8), arquitectura y ciclo de vida (Q9–Q16), routing, controladores y middleware (Q17–Q25), Eloquent y datos (Q26–Q34), Blade y frontend (Q35–Q43), autenticación, autorización y seguridad (Q44–Q52), APIs e integraciones (Q53–Q61), colas, eventos y caché (Q62–Q69), testing y calidad (Q70–Q77) y rendimiento, despliegue y administración (Q78–Q85), más una plantilla de respuesta para entrevista y referencias.

Contenido

1. Fundamentos, versiones y ecosistema

Q1. ¿Qué es Laravel y para qué tipo de aplicaciones se usa?

Laravel es un framework de desarrollo web en PHP, de código abierto (licencia MIT), creado por Taylor Otwell en 2011. Su propuesta es un desarrollo full-stack productivo con una sintaxis expresiva: enrutamiento, ORM (Eloquent), motor de plantillas (Blade), autenticación/autorización, colas, caché, eventos, validación, testing y CLI (Artisan) integrados y con convenciones claras. Se usa para aplicaciones web monolíticas (SaaS, back-office, portales, eCommerce), APIs REST y proyectos que necesitan entregar rápido con un ecosistema enorme (Livewire, Inertia, Sanctum, Horizon, Forge, Vapor). Es una opción muy fuerte para pymes y startups, y también para empresas que necesitan un framework maduro y mantenido con una comunidad activa.

Q2. ¿Cómo evolucionan las versiones y qué soporte ofrecen?

Laravel publica una versión mayor al año (Laravel 9 en 2022, 10 en 2023, 11 en 2024, 12 en 2025 y siguientes), con correcciones de errores durante aproximadamente 18 meses y correcciones de seguridad durante 2 años desde el lanzamiento; las versiones LTS históricas (6 LTS, 9 LTS) ya no son el esquema actual. Cada release mayor puede introducir cambios estructurales: Laravel 11, por ejemplo, simplificó la estructura de la aplicación (sin app/Http/Kernel.php ni app/Console/Kernel.php, configuración en bootstrap/app.php), exigió PHP 8.2+ y puso SQLite como base por defecto; Laravel 12 mantuvo ese esquema con mejoras en los starter kits (React, Vue, Livewire) y en el ecosistema. Planificar el upgrade anual es parte del mantenimiento: dependencias actualizadas, tests en verde y revisión de deprecaciones.

Q3. ¿Qué requisitos y stack necesita Laravel?

PHP 8.2 o superior para las versiones 11/12 (verificar la matriz de cada release), Composer 2 para dependencias, y extensiones habituales (mbstring, openssl, pdo, tokenizer, xml, ctype, json, bcmath, fileinfo). Base de datos: MySQL/MariaDB, PostgreSQL, SQLite o SQL Server. En desarrollo se puede usar Laravel Sail (Docker) o Herd (entorno local rápido); en producción, Nginx o Apache con PHP-FPM, Redis para caché/colas/sesiones (opcional pero recomendado), y Supervisor o systemd para los workers de cola. El frontend puede compilarse con Vite (estándar desde Laravel 9/10) y desplegarse como estáticos. Para correo, almacenamiento y colas en la nube se configuran drivers específicos (SES, S3, SQS, etc.).

Q4. ¿Cuál es el ecosistema oficial de Laravel?

También existe un ecosistema comunitario enorme (Spatie, Filament, Laracasts) que cubre permisos, administración, medios, multitenancy y más.

Q5. ¿Cómo se compara Laravel con Symfony y otros frameworks?

Laravel destaca por productividad, documentación excelente, comunidad y herramientas oficiales de ciclo completo (desde el entorno local hasta el despliegue y el monitoreo).

Q6. ¿Cómo se estructura una aplicación Laravel moderna?

En Laravel 11/12: app/ (Http/Controllers, Http/Middleware, Http/Requests, Models, Providers, Services si se usa esa capa, Console/Commands, Events, Listeners, Jobs, Policies, Rules, View/Components), bootstrap/ (app.php, providers.php, cache), config/ (archivos por dominio: app, auth, cache, database, queue, mail, filesystems, session, services), database/ (migrations, seeders, factories), public/ (index.php, assets compilados), resources/ (views, css, js), routes/ (web.php, console.php; api.php se genera al instalar Sanctum con php artisan install:api), storage/ (app, framework, logs), tests/ (Feature, Unit), vendor/, .env.example y composer.json. La estructura busca que el framework no estorbe: las carpetas de dominio (Services, Actions, DTOs) se añaden según la complejidad del proyecto, no por obligación.

Q7. ¿Qué es el «Laravel way» y qué convenciones conviene respetar?

Laravel favorece convención sobre configuración: nombres de tablas en plural y snake_case para modelos en singular (User → users), claves id autoincrementales, timestamps created_at/updated_at, rutas RESTful, controladores de recursos, validación en Form Requests, lógica de negocio fuera del controlador (servicios/acciones/jobs), eventos para efectos secundarios y colas para tareas lentas. También: usar el contenedor y la inyección de dependencias en lugar de instanciar clases a mano, Eloquent con relaciones bien definidas y eager loading, migraciones versionadas (nunca cambios manuales en la base), configuración en config/ leída desde .env, y pruebas de feature para los flujos críticos. Respetar estas convenciones hace que cualquier desarrollador Laravel entienda el proyecto rápido; pelearse con ellas suele costar más de lo que ahorra.

Q8. ¿Cómo se crea un proyecto Laravel?

Con el instalador oficial (laravel new mi-app, que permite elegir starter kit y base de datos) o con Composer (composer create-project laravel/laravel mi-app). Luego se configura .env (credenciales de base de datos, APP_KEY generada con php artisan key:generate, mail, cache, queue), se ejecutan las migraciones (php artisan migrate) y se levanta el servidor de desarrollo con php artisan serve o, preferiblemente, ./vendor/bin/sail up si se usa Docker. Para el frontend: npm install && npm run dev (Vite). En Laravel 11/12 el instalador pregunta por base de datos y starter kit, y genera la estructura mínima; el comando php artisan install:api añade rutas de API y Sanctum cuando se necesita una API.

2. Arquitectura y ciclo de vida de la petición

Q9. ¿Cómo es el ciclo de vida de una petición HTTP en Laravel?

El servidor web dirige todas las peticiones a public/index.php (front controller). Este carga el autoload de Composer y arranca la aplicación con bootstrap/app.php, que construye el contenedor de servicios y registra los service providers. La petición entra al kernel HTTP, que la envuelve en un Request de Symfony, la pasa por la cadena de middleware global (incluyendo los de grupo web/api), la entrega al router, que resuelve la ruta y ejecuta el controlador (o closure) con sus dependencias inyectadas; el controlador devuelve una Response (vista, JSON, redirect, archivo). La respuesta atraviesa el middleware en orden inverso (modificándola si aplica) y se envía al cliente; finalmente se ejecutan los terminable middleware y tareas de cierre (por ejemplo, enviar respuestas en cola o registrar métricas). En Laravel 11 el kernel se configura en bootstrap/app.php con ->withMiddleware(), ->withRouting() y ->withExceptions().

Q10. ¿Qué papel cumplen bootstrap/app.php y los kernels?

bootstrap/app.php es el punto de composición de la aplicación: crea la instancia (Application::configure(basePath: ...)), define el routing (web, api, commands, health check /up), registra middleware globales y alias, configura el manejo de excepciones (withExceptions: reportables, renderables, dontReport) y los service providers (en Laravel 11/12, bootstrap/providers.php lista los providers de la app). Antes de Laravel 11 existían app/Http/Kernel.php (middleware, grupos y prioridad) y app/Console/Kernel.php (comandos y scheduler); ahora esa configuración vive en bootstrap/app.php y routes/console.php. Entender este archivo es clave para depurar problemas de middleware, rutas o excepciones que «no se comportan como se espera».

Q11. ¿Cómo funciona el service container?

El contenedor es un registro de cómo construir objetos y resolver sus dependencias. Mediante bind se registra un closure o clase; singleton devuelve siempre la misma instancia; scoped crea una instancia por ciclo de vida (petición o job, clave con Octane); instance registra un objeto ya creado. Si una clase no está registrada, Laravel la resuelve por autowiring: inspecciona el constructor y resuelve recursivamente sus tipos (interfaces deben estar vinculadas a una implementación). También existe el contextual binding (when(Clase)->needs(Interface)->give(Implementacion)) para dar implementaciones distintas según el consumidor. En la práctica: inyectar interfaces en constructores o métodos de controladores, registrar bindings en un service provider y evitar el uso directo de app()/resolve() salvo en puntos de entrada justificados.

Q12. ¿Qué son los service providers y cuál es la diferencia entre register y boot?

Los service providers son el lugar donde se registra y configura todo: bindings del contenedor, event listeners, middleware, rutas, directivas de Blade, componentes, observers, gates, comandos y publicaciones. register() se ejecuta primero para solo registrar cosas en el contenedor (no debe depender de otros servicios ya resueltos ni de la base de datos); boot() se ejecuta después de registrar todos los providers y es donde se puede usar el contenedor y el framework ya inicializado (registrar observers, vistas, gates, etc.). Se registran en bootstrap/providers.php (Laravel 11+) o config/app.php (anteriores) y los paquetes los declaran en su composer.json (extra.laravel.providers). Un error común es hacer consultas a base de datos o resolver servicios en register(), lo que provoca fallos durante el arranque y en comandos de consola.

Q13. ¿Qué son las facades y cómo funcionan?

Una facade es una clase que expone estáticamente un servicio del contenedor: Cache::get() llama en realidad al método get de la instancia registrada con la clave cache. Cada facade define getFacadeAccessor() y hereda __callStatic, que resuelve el servicio y delega. Ventajas: sintaxis corta y legible; desventajas: ocultan dependencias y complican el testing si se abusa (aunque Laravel permite Facade::shouldReceive() y existen las real-time facades para usar cualquier clase como si fuera facade). Buenas prácticas: usar facades en controladores/vistas para servicios de infraestructura (Cache, Log, DB, Storage, Http, Auth) e inyectar dependencias en servicios y clases de dominio; en código testeable, preferir inyección. No hay que confundirlas con el patrón Facade de GoF: aquí son un proxy estático al contenedor.

Q14. ¿Cómo funciona el pipeline de middleware?

El middleware es una capa alrededor de la petición: recibe $request y un $next, ejecuta lógica antes/después y decide si pasa la petición, la modifica o corta la cadena (por ejemplo, autenticación, CSRF, throttle). Se registran como globales (withMiddleware -> append/prepend), en grupos (web, api) o por alias (auth, verified, throttle:60,1, can:update,post) y se aplican en rutas o controladores. Los terminable middleware implementan terminate($request, $response) y se ejecutan después de enviar la respuesta (ideal para logging o métricas sin penalizar al usuario). El orden importa: Laravel define una prioridad (por ejemplo, EnsureTokenIsValid antes de StartSession), y un middleware que dependa de la sesión debe ejecutarse después de StartSession. En Laravel 11 los alias y grupos se configuran en bootstrap/app.php.

Q15. ¿Cómo funciona la configuración y por qué no usar env() fuera de config/?

Los archivos de config/ leen variables de entorno con env() y devuelven arrays; el resto de la aplicación debe leer con config('app.name'). Motivo: al ejecutar php artisan config:cache, Laravel compila la configuración en un solo archivo y las llamadas a env() fuera de config/ dejan de funcionar (devuelven null), porque el archivo .env no se carga en ese modo. Además, centralizar en config/ permite valores por defecto (env('X', 'default')), tipado y organización lógica. El .env nunca se versiona (solo .env.example) y en producción los valores se inyectan por el servidor/plataforma. Tras cambiar config en producción: config:clear y config:cache en el despliegue, y reiniciar workers de cola y Octane para que tomen los cambios.

Q16. ¿Qué implicaciones tiene Octane para el estado de la aplicación?

Octane mantiene la aplicación en memoria entre peticiones (usando RoadRunner, Swoole o FrankenPHP), lo que elimina el bootstrap por petición y mejora mucho el rendimiento. Consecuencia: el estado persiste entre peticiones, algo que en PHP-FPM no ocurría. Riesgos: singletons con datos de usuario, propiedades estáticas que acumulan información, configuraciones mutadas en runtime, conexiones o caches por usuario. Soluciones: usar scoped bindings (se reinician por petición), evitar estado mutable en singletons, limpiar propiedades en listeners de eventos de Octane (RequestReceived, RequestTerminated), no guardar el usuario actual en el contenedor y revisar paquetes de terceros (algunos no son compatibles). Además, Octane requiere reiniciarse en cada despliegue (octane:reload) y vigilar fugas de memoria en procesos de larga vida.

3. Routing, controladores y middleware

Q17. ¿Cómo se define el routing y qué diferencia hay entre web.php y api.php?

Las rutas se declaran con los helpers (Route::get/post/put/patch/delete) o con recursos, en routes/web.php (middleware web: sesiones, CSRF, cookies, vistas) y routes/api.php (middleware api, sin sesión ni CSRF; en Laravel 11 se crea con php artisan install:api e incluye Sanctum). También existen routes/console.php (comandos y scheduler) y routes/channels.php (broadcasting). En bootstrap/app.php se registran los archivos; se pueden añadir rutas con Route::middleware(...)->prefix(...)->group(base_path('routes/admin.php')). La elección web/api determina qué middleware se aplica: un error común es poner una API en web.php y sufrir CSRF/sesiones innecesarias, o poner una ruta que necesita sesión en api.php y no tenerla.

Q18. ¿Qué es el route model binding?

Es la resolución automática de modelos a partir de parámetros de ruta. Implícito: si el tipo del parámetro es un modelo Eloquent, Laravel busca por la clave primaria ({user} → User::findOrFail($id)); se puede personalizar con getRouteKeyName() (por ejemplo, slug) o resolviendo por otro campo con Route::bind. Explícito: se registra un closure en RouteServiceProvider/bootstrap/app.php que recibe el valor y devuelve el modelo (o lanza 404). Si el modelo no existe, se devuelve 404 automáticamente. También se puede usar scoped binding para anidar recursos (/users/{user}/posts/{post} resuelve el post solo si pertenece al usuario, con ->scopeBindings()). Es una de las características que más reduce código repetitivo y errores de autorización.

Q19. ¿Qué son las resource routes y los resource controllers?

Route::resource('posts', PostController::class) genera las siete rutas RESTful: index, create, store, show, edit, update, destroy, con nombres (posts.index, etc.) y parámetros; Route::apiResource omite create y edit (formularios) para APIs. El controlador puede generarse con php artisan make:controller PostController --resource --model=Post y usa route model binding. Se puede limitar con ->only([...]) o ->except([...]), y añadir rutas anidadas con Route::resource('posts.comments', ...) (con shallow nesting cuando conviene). Para acciones únicas existe el controlador invocable (__invoke). Es la forma estándar de mantener APIs y formularios coherentes; si el recurso necesita más acciones, se añaden rutas explícitas con nombres claros en lugar de forzar el recurso.

Q20. ¿Cómo se organizan grupos de rutas, prefijos y nombres?

Con Route::prefix('admin')->name('admin.')->middleware(['auth','can:admin'])->group(function () { ... }). Se pueden combinar domain (subdominios o dominios distintos), controller (agrupar rutas de un mismo controlador), where (restricciones con expresiones regulares en parámetros), scopeBindings y withoutMiddleware. Para rutas comunes a varios archivos se usan Route::middleware(...)->group(base_path('routes/admin.php')) en bootstrap/app.php. Convención útil: prefijo de nombre por área (admin., api.v1.) para que route('admin.posts.index') sea predecible; y helper route() en vistas/controladores en lugar de URLs a mano. Un grupo mal diseñado complica la autorización: conviene que el middleware del grupo sea el mínimo común y que cada ruta añada lo específico.

Q21. ¿Qué es el route caching y qué limitaciones tiene?

php artisan route:cache compila todas las rutas en un archivo PHP para que el registro sea mucho más rápido en producción (se ejecuta en el despliegue, junto con config:cache y view:cache). Limitaciones: las rutas deben ser serializables, por lo que no se permiten closures como acción (hay que usar controladores o controladores invocables), y cualquier cambio de rutas exige regenerar la caché. En desarrollo no se usa (se limpia con route:clear). Si un despliegue falla con «Unable to prepare route for serialization», la causa suele ser un closure en routes/*.php. También existen cachés de eventos (event:cache), vistas (view:cache) y configuración (config:cache); todas deben regenerarse en cada release y limpiarse (optimize:clear) cuando algo se comporta de forma extraña.

Q22. ¿Cómo se crea y registra un middleware?

Con php artisan make:middleware EnsureTokenIsValid, que genera una clase con handle(Request $request, Closure $next); se implementa la lógica y se devuelve $next($request) o una respuesta alternativa (redirect, 403, JSON). Registro: en Laravel 11/12 se añade el alias en bootstrap/app.php (->withMiddleware(fn ($middleware) => $middleware->alias(['token' => EnsureTokenIsValid::class]))), se agrega a un grupo (web, api) o globalmente (append), y se usa en rutas (->middleware('token')) o en el constructor del controlador. Para middleware con parámetros se usa handle($request, $next, $param) y se pasa como throttle:60,1. En versiones anteriores el registro era en app/Http/Kernel.php y la prioridad se definía en $middlewarePriority. Cuidado con middleware que escriben en sesión o confían en el usuario: deben ejecutarse después de StartSession/Authenticate.

Q23. ¿Qué son los Form Requests y por qué usarlos?

Son clases (php artisan make:request StorePostRequest) que encapsulan la autorización (authorize()) y la validación (rules()) de una petición. Al inyectarlas en el controlador, Laravel las valida automáticamente antes de ejecutar la acción: si falla, redirige con errores (web) o responde 422 (JSON/API). Ventajas: controladores más delgados, reglas reutilizables y testeables, mensajes y atributos personalizados (messages(), attributes()), reglas condicionales (Rule::when, sometimes, after), validación de arrays anidados con wildcards (items.*.price) y autorización previa coherente con las policies. Para validaciones complejas se crean Rule objects o reglas custom; para APIs se combina con JsonResource y un formato de error consistente. Es la forma recomendada frente a validar en el controlador o, peor, en el modelo.

Q24. ¿Qué tipos de respuestas puede devolver un controlador?

Vistas (return view('posts.index', compact('posts'))), JSON (return response()->json($data, 201)), arrays/colecciones (Laravel los convierte a JSON), redirecciones (redirect()->route(...)->with('status', ...), back()), respuestas con cabeceras o cookies, descargas (response()->download($path), streamDownload), archivos (response()->file), y API Resources (return new PostResource($post) o PostResource::collection($posts)) para transformar modelos. También se puede devolver un Response desde un middleware o usar abort(404)/abort_if para errores. En APIs conviene un formato uniforme (data, meta, errors), códigos HTTP correctos (200, 201, 204, 422, 404, 403) y no exponer modelos completos. En Laravel 11 los redirects a rutas con parámetros y los helpers siguen igual; lo que cambia es la configuración del manejo de excepciones en bootstrap/app.php.

Q25. ¿Cómo funcionan el rate limiting y las URLs firmadas?

Rate limiting: se define un RateLimiter en un service provider (RateLimiter::for('api', fn (Request $request) => Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()))) y se aplica con el middleware throttle:api; soporta límites por minuto/hora/día, respuestas personalizadas (response()), after y por segmento. Laravel 11 permite límites por segundo. URLs firmadas: URL::temporarySignedRoute('unsubscribe', now()->addMinutes(30), ['user' => $id]) genera una URL con firma HMAC; el middleware signed (->middleware('signed')) valida que no haya sido alterada y que no esté expirada. Se usan para enlaces de verificación, descargas y acciones sin login. Nunca hay que confiar en parámetros de la URL sin firma o sin autorización: son mecanismos distintos (la firma protege integridad/origen, no permisos del usuario).

4. Eloquent ORM, migraciones y datos

Q26. ¿Qué es Eloquent y qué convenciones sigue?

Eloquent es el ORM de Laravel: cada modelo es una clase que representa una tabla y permite consultar y persistir con una API expresiva. Convenciones: tabla en plural snake_case (User → users, BlogPost → blog_posts), clave primaria id autoincremental (personalizable con $primaryKey e $incrementing), timestamps created_at/updated_at (desactivables con $timestamps = false), conexión por defecto configurable ($connection), y llaves foráneas {modelo}_id. El modelo se crea con php artisan make:model Post -mfsc (migración, factory, seeder, controlador) y se configura con $fillable/$guarded, $casts, $hidden, $appends y $with (eager loading por defecto). Eloquent devuelve colecciones (Illuminate\Support\Collection), encadena scopes y relaciones, y usa el query builder por debajo.

Q27. ¿Qué tipos de relaciones soporta Eloquent?

Definir bien las relaciones (y sus índices en la base) evita N+1 y consultas costosas; además habilita el uso de whereHas, withCount y load.

Q28. ¿Qué es el problema N+1 y cómo se resuelve con eager loading?

El N+1 ocurre al recorrer una colección y consultar una relación por cada elemento: una consulta para los N registros y N consultas adicionales (una por fila). Solución: eager loading con ->with('comments') (o load, loadMissing, loadCount), que ejecuta una consulta extra en total; se puede anidar (with('comments.author')) y seleccionar columnas (with('comments:id,post_id,body')). En desarrollo, Model::preventLazyLoading(! app()->isProduction()) lanza excepción al cargar una relación de forma perezosa, lo que ayuda a detectar el problema en tests. Alternativas: withCount para contar sin cargar, withExists, withSum/withAvg, y joins cuando se necesita filtrado. En APIs es especialmente importante porque el N+1 se multiplica con listados paginados. Medir con Telescope/Debugbar (número de consultas) antes y después es la forma de verificar la mejora.

Q29. ¿Cómo funcionan las migraciones y el schema builder?

Las migraciones son archivos PHP versionados en database/migrations con up() y down(); se ejecutan con php artisan migrate y se registran en la tabla migrations. El schema builder permite crear y modificar tablas con una API fluida (Schema::create('posts', function (Blueprint $table) { $table->id(); $table->foreignId('user_id')->constrained()->cascadeOnDelete(); $table->string('title'); $table->timestamps(); })), además de índices, claves compuestas, columnas JSON, enums, generated columns y modificaciones ($table->dropColumn, change()). Comandos: migrate:status, migrate:rollback, migrate:refresh, migrate:fresh (borra todo y recrea), migrate:reset. Buenas prácticas: migraciones pequeñas y reversibles, nunca editar una migración ya ejecutada en producción (crear otra), nombrar índices y llaves explícitamente, y probar en una copia de datos reales los cambios costosos (añadir columna NOT NULL, índices grandes).

Q30. ¿Qué son los seeders y las factories?

Los seeders (database/seeders) insertan datos iniciales o de referencia (roles, países, configuración) y se ejecutan con php artisan db:seed o migrate --seed. Las factories (database/factories) generan datos falsos para pruebas y desarrollo usando Faker: Post::factory()->count(50)->create(), con estados (->published()), relaciones (->for(User::factory()), ->has(Comment::factory()->count(3))) y atributos personalizados. Se pueden encadenar secuencias (Sequence) y afterCreating. Buenas prácticas: seeders idempotentes (firstOrCreate/updateOrCreate) para poder ejecutarlos varias veces, factories realistas que respeten las constraints, y no usar datos de demo en producción. En tests, las factories son la base para crear escenarios rápidos y legibles.

Q31. ¿Qué son los casts, accessors y mutators?

Los casts convierten atributos al leerlos/escribirlos: integer, boolean, decimal:2, string, array, json, collection, datetime, date, encrypted, hashed, enums PHP (AsEnum) y clases de valor personalizadas (AsStringable, cast custom). Los accessors/mutators modernos (clase Attribute) transforman valores al obtenerlos o asignarlos: protected function title(): Attribute { return Attribute::make(get: fn ($value) => ucfirst($value), set: fn ($value) => strtolower($value)); }; los clásicos (getTitleAttribute/setTitleAttribute) siguen funcionando. Con ellos se centraliza el formato (fechas, monedas, JSON, cifrado) y se evita repetir lógica en vistas o controladores. Cuidado: los accessors no se pueden usar en consultas SQL directas (para filtros usar where sobre la columna real o columnas generadas), y los appends deben declararse explícitamente si se quiere que aparezcan en la serialización JSON.

Q32. ¿Qué son los scopes, observers y eventos de modelo?

Scopes locales: métodos reutilizables de consulta (public function scopePublished($query) { return $query->where('published', true); }) que se usan como Post::published()->get(). Scopes globales: se aplican automáticamente a todas las consultas del modelo (por ejemplo, filtro por tenant o SoftDeletes) y se pueden omitir con withoutGlobalScope. Observers: clases que escuchan eventos del ciclo de vida del modelo (creating, created, updating, updated, saving, deleting, deleted, restoring, retrieved) y se registran con #[ObservedBy(PostObserver::class)] o Post::observe(...); ideales para auditoría, generación de slugs o sincronización. Eventos de modelo: el mismo mecanismo basado en eventos; se pueden escuchar con listeners. Cuidado con efectos secundarios pesados o no transaccionales dentro de observers: para tareas lentas, encolar jobs y usar afterCommit para no disparar antes de confirmar la transacción.

Q33. ¿Qué es el mass assignment y cómo protegerse?

Mass assignment es asignar un array de datos a un modelo (Post::create($request->all())); si el array incluye campos que el usuario no debería controlar (por ejemplo is_admin), se produce una vulnerabilidad de escalada de privilegios. Laravel lo mitiga con $fillable (lista blanca de campos asignables) o $guarded (lista negra; por defecto ['*'] en algunos contextos). Recomendaciones: usar $fillable explícito, pasar solo datos validados ($request->validated() o un DTO), no asignar campos sensibles desde formularios, y usar forceFill/forceCreate solo en código interno controlado. En APIs, los Form Requests y Resources ayudan a que el contrato de entrada/salida sea explícito. Es una de las primeras cosas que se revisan en una auditoría de seguridad de una app Laravel.

Q34. ¿Cómo se manejan soft deletes, UUID/ULID y transacciones?

Soft deletes: el trait SoftDeletes añade deleted_at; delete() marca el registro, las consultas lo excluyen por defecto (withTrashed, onlyTrashed, restore, forceDelete). UUID/ULID: con el trait HasUuids/HasUlids (Laravel 9+) y columnas uuid/ulid; útiles para IDs no predecibles, sincronización entre sistemas y merges; considerar el impacto en índices y en el orden de inserción (ULID es ordenable). Transacciones: DB::transaction(function () { ... }) o beginTransaction/commit/rollBack con try/catch; los jobs y eventos deben ejecutarse afterCommit cuando dependen de datos confirmados. Además, lockForUpdate para evitar condiciones de carrera y DB::afterCommit. En sistemas con alta concurrencia, definir bien la unidad transaccional evita datos huérfanos y efectos duplicados.

5. Blade, frontend y componentes

Q35. ¿Qué es Blade y cómo se escapa la salida?

Blade es el motor de plantillas de Laravel: compila a PHP plano y se cachea, por lo que es rápido. Sintaxis principal: {{ $variable }} (escape automático de HTML, protege contra XSS), {!! $html !!} (sin escapar, solo para HTML de confianza), directivas (@if/@elseif/@else/@endif, @foreach/@forelse, @auth/@guest, @can, @csrf, @method, @include, @extends/@section/@yield, @livewire, @vite) y comentarios {{-- --}}. Blade no impone lógica de negocio: los datos deben llegar preparados desde controladores/ViewModels; para cálculos complejos usar componentes o clases. Cuidado con {!! !!} sobre contenido de usuario y con atributos HTML construidos con datos no escapados (usar e() o bindings).

Q36. ¿Cómo funcionan los layouts y los componentes de Blade?

El enfoque moderno son los componentes: php artisan make:component Alert genera una clase y una vista; se usa <x-alert type="error">Mensaje</x-alert> y los datos llegan como atributos/props; los slots permiten contenido e incluso slots con nombre (<x-slot:title>). Existe <x-layouts.app> (componente de layout) para envolver páginas y sustituir el esquema clásico @extends('layouts.app') + @section('content'). También hay componentes anónimos (resources/views/components/) para piezas sin clase PHP, @include para fragmentos simples, @each para listas y Blade::componentNamespace para paquetes. La ventaja de los componentes es encapsular markup, estilos y props con validación; la desventaja, un árbol de componentes mal diseñado que duplica capas. En proyectos con Tailwind, los componentes Blade son el mecanismo natural de reutilización visual.

Q37. ¿Qué son los componentes anónimos y cuándo usarlos?

Son componentes definidos solo con una vista en resources/views/components/ (por ejemplo components/button.blade.php), sin clase PHP asociada; reciben props con @props(['type' => 'button', 'href' => null]) y se usan como <x-button type="submit">Guardar</x-button>. Son ideales para piezas de UI sin lógica compleja: botones, badges, cards, inputs, alertas. Si necesitas lógica (cálculos, acceso a servicios, métodos), conviene un componente de clase o un ViewModel. Se pueden organizar en subcarpetas (components/forms/input.blade.php → <x-forms.input>) y combinar con @aware para heredar datos del componente padre. Es una de las formas más simples de mantener consistencia visual con Tailwind y evitar duplicación de clases CSS.

Q38. ¿Cómo se compilan los assets con Vite?

Laravel usa Vite como bundler (reemplazó a Laravel Mix desde Laravel 9/10): vite.config.js define inputs (resources/css/app.css, resources/js/app.js) y plugins (Laravel, React/Vue), package.json los scripts (npm run dev, npm run build), y en las vistas se usa @vite(['resources/css/app.css', 'resources/js/app.js']), que genera las etiquetas con hash y hot reload en desarrollo. En producción se ejecuta npm run build y se versiona public/build/manifest.json (no el contenido compilado necesariamente) para que el despliegue lo genere; @vite lee el manifest y emite 404 si falta. También se pueden usar CDNs para librerías, importar dinámicamente (import()) y configurar base para servir assets desde otro dominio/CDN. Alternativa antigua: Mix (webpack.mix.js), que aún se ve en proyectos legacy.

Q39. ¿Cómo se integra Tailwind CSS en Laravel?

Los starter kits modernos incluyen Tailwind configurado. El flujo típico: tailwind.config.js con content apuntando a resources/views/**/*.blade.php, resources/js/**/*.js, resources/js/**/*.vue, etc.; directivas en CSS (@tailwind base; @tailwind components; @tailwind utilities; o @import "tailwindcss"; en v4) y compilación con Vite. Con Tailwind se estiliza directamente en el markup, lo que encaja con componentes Blade; para clases reutilizables se usan @apply o componentes. Ventajas: control total, sin CSS muerto (purga), diseño consistente con tokens; desventajas: markup más verboso y necesidad de disciplina. En proyectos grandes conviene definir un sistema de componentes (botones, inputs, cards) y tokens (colores, espaciados) para no dispersar clases.

Q40. ¿Qué es Livewire y cuándo conviene usarlo?

Livewire permite construir interfaces dinámicas con PHP y Blade sin escribir JavaScript a mano: cada componente (php artisan make:livewire Counter) es una clase PHP con propiedades públicas y métodos, y una vista Blade; las interacciones (wire:click, wire:model, wire:submit) hacen peticiones AJAX que re-renderizan el componente en el servidor. Ventajas: una sola mentalidad (PHP) para front y back, validación y estado en el servidor, integración nativa con Eloquent y con Alpine.js para interacciones locales. Limitaciones: latencia por petición al servidor, necesidad de cuidar el tamaño del estado público y la seguridad (propiedades públicas son manipulables), y menos adecuado para UI muy interactiva o compleja. Es ideal para paneles, formularios y apps internas; en páginas públicas de alto tráfico conviene valorar Inertia/SPA o cachear.

Q41. ¿Qué es Inertia.js y cómo se usa con Laravel?

Inertia.js permite construir un SPA (React, Vue o Svelte) sin API separada: los controladores Laravel devuelven Inertia::render('Posts/Index', ['posts' => $posts]), y el cliente Inertia navega entre páginas recibiendo props JSON, manteniendo el estado del componente y la URL. El servidor sigue gestionando rutas, autenticación, validación y sesión; el frontend se organiza en páginas (resources/js/Pages). Ventajas: SPA con un solo repositorio y sin construir una API pública, compartir datos con HandleInertiaRequests, formularios con useForm y validación de Laravel, y code splitting. Se combina con los starter kits (Laravel 12 ofrece React/Vue con Inertia) y con SSR opcional. Requiere equipo cómodo con JS moderno; para equipos solo PHP, Livewire suele ser más directo.

Q42. ¿Qué starter kits y paquetes de autenticación existen?

Q43. ¿Cómo se maneja la localización en Laravel?

Los textos se envuelven con __('messages.welcome') o @lang('...') y viven en lang/{locale}/*.php (o JSON para traducciones por clave); el locale se configura con App::setLocale() por petición (middleware que lee la sesión, el usuario o la URL) y el fallback en config/app.php. Laravel soporta pluralización (trans_choice) y parámetros (:name), y los mensajes de validación se traducen automáticamente si existen los archivos correspondientes (lang/es/validation.php). Para contenido dinámico (productos, páginas) la traducción se modela en base de datos (tablas de traducciones o paquetes como spatie/laravel-translatable). Buenas prácticas: no concatenar frases (rompe la traducción), usar claves semánticas, y cubrir formatos de fecha/número/moneda con Carbon y helpers locales.

6. Autenticación, autorización y seguridad

Q44. ¿Cómo funciona el sistema de autenticación (guards y providers)?

La autenticación se configura en config/auth.php con dos piezas: los guards (cómo se autentica cada petición: web por sesión, api por token o guard personalizado) y los providers (de dónde salen los usuarios: EloquentUserProvider sobre el modelo User o DatabaseUserProvider sobre una tabla). Auth::attempt(['email' => ..., 'password' => ...]) verifica credenciales, regenera la sesión y marca al usuario; Auth::guard('api')->user() consulta un guard concreto y el middleware auth:api protege rutas. Se puede implementar un guard propio (por ejemplo, autenticación contra un SSO o LDAP) registrándolo con Auth::extend. Detalles importantes: regenerar la sesión tras el login (evita fijación), invalidarla en el logout, y no confundir autenticación (quién eres) con autorización (qué puedes hacer).

Q45. ¿Sanctum o Passport? ¿Cuándo usar cada uno?

Regla práctica: si la API la consumen tus propios frontends, Sanctum; si necesitas que otras empresas se integren con OAuth2 y scopes, Passport. En ambos casos, tokens con caducidad, revocación en logout y HTTPS obligatorio.

Q46. ¿Qué diferencias hay entre Fortify, Breeze, Jetstream y Socialite?

No son alternativas equivalentes: Fortify es un backend de autenticación sin interfaz (login, registro, recuperación de contraseña, verificación de email, 2FA, confirmación de contraseña) que se conecta a las vistas que tú construyas; Breeze es un scaffold mínimo con Blade + Tailwind (o React/Vue con Inertia) que implementa los flujos más comunes y sirve como punto de partida; Jetstream es un scaffold más completo (Livewire o Inertia) con equipos, perfiles, API tokens, 2FA y gestión de sesiones, apoyado en Fortify y Sanctum; Socialite resuelve el login social (OAuth con Google, GitHub, Facebook, etc.) y se combina con cualquiera de los anteriores. En proyectos reales conviene elegir Breeze/Fortify si se quiere control total del diseño y evitar el peso de Jetstream si no se usan equipos.

Q47. ¿Qué son los Gates y las Policies y cuándo usar cada uno?

Ambos responden «¿puede este usuario hacer esta acción?». Los Gates son closures registradas en AppServiceProvider (Gate::define('editar-post', fn (User $u, Post $p) => ...)) útiles para permisos globales o sin modelo. Las Policies son clases por modelo (PostPolicy con métodos viewAny, view, create, update, delete, restore) y Laravel las descubre por convención o con Gate::policy(). Se usan con $this->authorize('update', $post) en controladores o Form Requests, con el middleware can:update,post, con @can/@cannot en Blade y con Gate::allows() en código. Ejecutar la autorización en el servidor (nunca solo ocultar botones), cubrir todas las acciones sensibles y usar before para superadmins. Para permisos dinámicos por rol, la Policy puede consultar la base de datos o integrarse con spatie/laravel-permission.

Q48. ¿Cómo se implementan roles y permisos?

La opción más común es spatie/laravel-permission: define roles y permisos en base de datos, los asigna a usuarios o equipos (teams) y expone hasRole, hasPermissionTo, can y el middleware role: / permission:. Los permisos se agrupan por módulo (posts.create, posts.update) y las Policies pueden delegar en ellos. Alternativas: columnas de rol en la tabla de usuarios para casos simples, enum de roles, o integración con un IdP que envíe grupos/claims. Buenas prácticas: permisos granulares y roles como agrupaciones, no hardcodear nombres de rol en el código, cachear la matriz de permisos (el paquete lo hace), invalidar caché al cambiar asignaciones, y auditar quién concede permisos. Evita el antipatrón de un rol único «admin» que puede todo sin trazabilidad.

Q49. ¿Cómo funcionan CSRF y las sesiones en Laravel?

Laravel genera un token CSRF por sesión y el middleware VerifyCsrfToken lo valida en cada POST/PUT/PATCH/DELETE; en Blade se incluye con @csrf y en peticiones AJAX/SPA con el header X-CSRF-TOKEN o la cookie XSRF-TOKEN (Sanctum lo gestiona). Las exclusiones ($except) deben limitarse a webhooks con verificación de firma propia. Las sesiones usan drivers file, cookie, database, redis o memcached; el contenido se cifra y la cookie es HttpOnly y SameSite=Lax por defecto. En producción conviene Redis o base de datos para compartir sesión entre servidores, SESSION_SECURE_COOKIE=true con HTTPS, regenerar el ID tras login (session()->regenerate()), invalidar en logout y usar logoutOtherDevices para cerrar sesiones tras cambiar contraseña.

Q50. ¿Cómo se previenen XSS, inyección SQL y mass assignment?

Complementar con validación estricta de tipos, autorización por objeto y auditoría de dependencias.

Q51. ¿Cómo se gestionan hashes, cifrado, APP_KEY y secretos?

Hash::make() usa bcrypt por defecto (argon2id es configurable) y es irreversible; para verificar, Hash::check(), y needsRehash() permite migrar el algoritmo sin pedir la contraseña. Para datos reversibles, Crypt::encryptString()/decryptString() usa AES-256-GCM con la APP_KEY. Consecuencias: si se pierde o rota la APP_KEY sin re-cifrar, se pierden datos cifrados, sesiones y cookies; si se filtra, todo lo cifrado queda expuesto. Buenas prácticas: clave de 32 bytes generada con php artisan key:generate, nunca en Git, distinta por entorno, almacenada en un gestor de secretos (Vault, SSM, Doppler, variables del proveedor), y .env fuera del repositorio. Contraseñas siempre con hash (nunca cifrado reversible), tokens de API hasheados y secretos rotables con procedimiento documentado.

Q52. ¿Qué checklist de seguridad aplicar antes de poner una app Laravel en producción?

7. APIs, validación e integraciones

Q53. ¿Cómo se estructuran y versionan las rutas de una API?

En Laravel 11/12 el archivo routes/api.php se crea con php artisan install:api y se registra automáticamente con prefijo /api y middleware api (sin sesión ni CSRF). Las rutas se agrupan por versión con Route::prefix('v1')->group(...) o mediante archivos separados, y se nombran con prefijos (api.v1.posts.index). El versionado debe ser explícito desde el inicio: cambios incompatibles (renombrar campos, cambiar tipos, eliminar endpoints) crean v2, mientras que cambios aditivos (nuevos campos opcionales) pueden convivir en la misma versión. Documentar deprecaciones con fechas y cabeceras Deprecation/Sunset, mantener ambas versiones durante una ventana acordada y probar los contratos con tests. Aplicar throttle:api, autenticación por token y respuestas consistentes.

Q54. ¿Qué son los API Resources y por qué conviene usarlos?

Los API Resources (JsonResource, ResourceCollection) son una capa de transformación entre los modelos y el JSON de salida. Permiten controlar qué campos se exponen, renombrar claves, formatear fechas/números y añadir relaciones o metadatos condicionales con when(), whenHas() y mergeWhen(). Ventajas: no acoplar la API a la estructura interna de la base de datos, evitar filtrar columnas sensibles, mantener contratos estables ante refactors y reutilizar la misma representación en varios endpoints. Se generan con php artisan make:resource PostResource y se devuelven desde el controlador; las colecciones paginadas incluyen data, links y meta automáticamente. En APIs grandes conviene versionar los Resources junto con las rutas y probar el JSON con tests de contrato.

Q55. ¿Cómo se implementan paginación, filtros y ordenamiento?

paginate($perPage) devuelve paginación con total y enlaces; simplePaginate evita el count para listados grandes; cursorPaginate es ideal para volúmenes altos o scroll infinito porque usa un cursor estable y no requiere offset. Los filtros se validan con Form Requests y se aplican con query scopes o un objeto de filtros dedicado, siempre con allowlist de campos (?status=open&created_after=...); nunca interpolar nombres de columna del usuario. El ordenamiento se restringe a una lista blanca (?sort=-created_at), con dirección validada. Para relaciones se usa ?include=author,comments con allowlist y eager loading, y se limita per_page (por ejemplo, máximo 100). Documentar parámetros, valores por defecto y códigos de error; en listados grandes, indexar las columnas de filtro/orden y considerar caché.

Q56. ¿Cómo se hace validación avanzada y se devuelven errores en una API?

Los Form Requests centralizan rules(), authorize(), messages() y attributes(); permiten reglas condicionales (sometimes, Rule::when), validación después de otras reglas (after), objetos Rule personalizados, exclude, prohibited y validación de arrays anidados con comodines. Para APIs, el exception handler debe responder JSON con código 422 y una estructura estable: message y errors por campo; también conviene diferenciar 400 (petición mal formada), 401/403 (autenticación/autorización), 404, 409 (conflicto), 422 (validación), 429 (rate limit) y 500. Se puede adoptar un formato tipo Problem Details (RFC 7807) o uno propio documentado. No devolver mensajes internos de excepciones, registrar el error con un ID de correlación y probar los casos límite con tests.

Q57. ¿Cómo funcionan el rate limiting y la idempotencia?

El rate limiting se define en RateLimiter::for('api', fn (Request $r) => Limit::perMinute(60)->by($r->user()?->id ?: $r->ip())) y se aplica con el middleware throttle:api; para endpoints sensibles (login, envío de emails, pagos) se usan limitadores más estrictos y respuestas 429 con Retry-After. La idempotencia protege operaciones POST que no deben duplicarse (pagos, creación de pedidos): el cliente envía una clave Idempotency-Key, el servidor la registra junto con la respuesta y, si llega la misma clave, devuelve el resultado original en lugar de reprocesar. Se implementa con una tabla o caché con TTL y bloqueo atómico, más un índice único por clave. Conviene combinarlo con reintentos del cliente, timeouts y colas para tareas lentas; documentar el comportamiento y los códigos de error.

Q58. ¿Cuándo usar GraphQL y cómo se implementa en Laravel?

GraphQL conviene cuando múltiples clientes necesitan consultar formas distintas de los mismos datos y se quiere evitar el over/under-fetching de REST; no sustituye automáticamente a REST en todos los casos. En Laravel el paquete más usado es Lighthouse, que parte de un schema SDL (graphql/schema.graphql) y resuelve con directivas (@guard, @can, @paginate, @hasMany) y resolvers personalizados. Riesgos a controlar: consultas costosas (límites de complejidad/profundidad, persisted queries), N+1 (batch loading, @with), autorización por campo, y exposición accidental de datos. Alternativa: Rebing GraphQL o una capa propia. Documentar el schema como contrato, versionarlo con evolución compatible y monitorear latencias por consulta.

Q59. ¿Cómo se consumen APIs externas con el cliente HTTP?

El facade Http (basado en Guzzle) ofrece una API fluida: Http::timeout(10)->connectTimeout(3)->withToken($token)->retry(3, 200, fn ($e) => $e instanceof ConnectionException)->post($url, $data). Configurar siempre timeouts (de conexión y total), reintentos con backoff exponencial solo para errores transitorios (5xx, timeouts), throw() si se quiere excepción ante 4xx/5xx, y registrar request/response sin datos sensibles. Para llamadas concurrentes, Http::pool() reduce latencia. En tests, Http::fake() con patrones de URL y Http::assertSent(); preventStrayRequests() evita llamadas reales. En producción, considerar circuit breaker, colas para procesos lentos, idempotencia y una capa de servicios que aísle al proveedor (facilita cambiar de API y probar).

Q60. ¿Cómo se implementan webhooks de forma segura?

Para recibir webhooks: exponer un endpoint sin CSRF (pero con autenticación por firma), verificar la firma HMAC del proveedor con el secreto compartido y comparación en tiempo constante, validar el payload, responder rápido (2xx) y procesar en cola para evitar timeouts y reintentos duplicados. La idempotencia es clave: guardar el ID del evento y descartar duplicados. Para enviar webhooks: firmar el cuerpo, incluir timestamp para evitar replay, reintentar con backoff, registrar entregas y ofrecer un endpoint de reenvío. Documentar el contrato (eventos, payload, códigos) y monitorizar fallos. Ejemplos típicos: Stripe (Stripe-Signature), GitHub (X-Hub-Signature-256) y Shopify. Nunca confiar en el payload sin verificar la firma ni exponer secretos en el cliente.

Q61. ¿Cómo se documenta una API Laravel?

Opciones habituales: Scramble (genera OpenAPI 3 a partir de rutas, Form Requests y Resources, sin anotaciones), Scribe (documentación legible + colección Postman, con anotaciones opcionales) y l5-swagger (anotaciones OpenAPI en los controladores). Lo importante es que la documentación se genere desde el código o se valide contra él en CI, para que no quede desactualizada. Incluir autenticación, parámetros, ejemplos de request/response, códigos de error, paginación y versionado. Un contrato OpenAPI permite generar clientes y tests de contrato, y sirve como acuerdo con los equipos consumidores. Añadir una página de estado/cambios (changelog) y mantener ejemplos reales anonimizados.

8. Colas, eventos, caché y tareas programadas

Q62. ¿Cómo funcionan las colas y los jobs?

Las colas permiten diferir trabajo pesado (emails, informes, llamadas a terceros, procesamiento de imágenes) para que la petición responda rápido. Se configura un driver en config/queue.php (database, Redis, SQS, Beanstalkd) y se crea un job con php artisan make:job ProcessInvoice; el job implementa handle() y se encola con ProcessInvoice::dispatch($invoice) o dispatch()->onQueue('high')->delay(...). El worker se ejecuta con php artisan queue:work; en producción se supervisa con Supervisor o Horizon. Buenas prácticas: jobs pequeños e idempotentes, pasar IDs y no modelos completos, $tries/$timeout definidos, failed_jobs monitorizado, colas separadas por prioridad y un worker por cola. En tests se usa Queue::fake() para verificar despachos sin ejecutar.

Q63. ¿Cómo funcionan reintentos, backoff, failed jobs y Horizon?

Un job puede declarar public $tries = 3, public $backoff = [10, 60, 300] (segundos) y public $timeout; retryUntil() fija una fecha límite. Si supera los intentos, se registra en failed_jobs y se puede reintentar con queue:retry o queue:failed, o descartar. El método failed(Throwable $e) permite limpiar o alertar. Horizon es un dashboard y supervisor para colas Redis: muestra throughput, tiempos, jobs fallidos y permite configurar procesos por cola, balanceo y métricas; requiere Redis y se despliega como proceso supervisado. Alternativas gestionadas: SQS + workers, Laravel Vapor (Lambda), o servicios externos. La clave operativa es distinguir errores transitorios (reintentar con backoff) de errores permanentes (fallar rápido y alertar) y evitar jobs no idempotentes que dupliquen efectos al reintentarse.

Q64. ¿Qué son los batches, chains y jobs únicos?

Con batches conviene definir allowFailures si se toleran fallos parciales y monitorizar los jobs fallidos del lote; con chains, diseñar compensaciones si un paso falla después de efectos secundarios.

Q65. ¿Cómo funcionan eventos, listeners y broadcasting?

Los eventos desacoplan el código: se crean con make:event, se disparan con event(new OrderPlaced($order)) y los listeners reaccionan (enviar email, actualizar stock, notificar). Se registran en EventServiceProvider o por auto-descubrimiento; pueden implementar ShouldQueue para ejecutarse en cola y ShouldBroadcast para enviarse al frontend. Laravel incluye eventos de modelo (created, updated, deleted) y observers para agrupar lógica. El broadcasting usa canales públicos, privados o de presencia y drivers como Pusher, Ably, Redis o Reverb (servidor WebSocket oficial, compatible con el protocolo Pusher). En el cliente, Laravel Echo se suscribe a canales; la autorización de canales privados se define en routes/channels.php. Cuidado: no poner lógica crítica sólo en listeners asíncronos sin idempotencia ni trazabilidad.

Q66. ¿Cómo se programan tareas con el scheduler?

En Laravel 11/12 el scheduler se define en routes/console.php (o app/Console/Kernel.php en versiones anteriores) con Schedule::command('reports:daily')->dailyAt('02:00'), Schedule::job(new Cleanup)->hourly() o Schedule::call(fn () => ...)->everyFiveMinutes(). Frecuencias disponibles: everyMinute, hourly, daily, weekly, monthly, cron(), weekdays(), timezone(), withoutOverlapping(), onOneServer() y runInBackground(). En el servidor solo hay que añadir una entrada cron: * * * * * cd /ruta && php artisan schedule:run >> /dev/null 2>&1. Para probar: schedule:list y schedule:test. En clústeres, onOneServer() requiere caché compartida; los solapamientos se evitan con withoutOverlapping(), y conviene monitorizar que el cron realmente se ejecuta.

Q67. ¿Cómo funciona la caché (drivers, tags y locks)?

La caché se configura en config/cache.php: file, database, Redis, Memcached, array (tests) o DynamoDB. Se usa con Cache::get/put/remember/forget; remember($key, $ttl, fn () => ...) es el patrón cache-aside más común. Los tags (solo soportados por Redis/Memcached) permiten invalidar grupos: Cache::tags(['posts'])->flush(). Los locks atómicos (Cache::lock('job', 10)->get()) evitan ejecuciones concurrentes y se usan en jobs y comandos. Consideraciones: claves con namespace/versión, TTL acorde al negocio, invalidación explícita al escribir, cachear datos costosos y no datos por usuario en claves compartidas; en producción, Redis con persistencia adecuada y monitorización de memoria. Evitar cachear respuestas que dependen de permisos sin incluir al usuario en la clave.

Q68. ¿Cómo se envían notificaciones y correos?

Los Mailables (make:mail OrderShipped) encapsulan asunto, vistas Blade/Markdown, adjuntos y destinatarios; se envían con Mail::to($user)->send(new OrderShipped($order)) o en cola con queue(). Las notificaciones (make:notification) son multicanal: mail, database, broadcast, SMS (Vonage, Twilio) y canales personalizados; se envían con $user->notify(new InvoicePaid($invoice)) y pueden implementar ShouldQueue. Los drivers de correo (SMTP, Mailgun, Postmark, SES, log/array en tests) se configuran en config/mail.php; conviene usar servicios transaccionales con DKIM/SPF/DMARC, plantillas y webhooks de rebotes. En tests, Mail::fake() y Notification::fake(). Para altos volúmenes, enviar en cola y separar la cola de correo del resto.

Q69. ¿Cómo se gestionan archivos y almacenamiento?

El filesystem abstraction (Storage) permite usar discos locales, S3, GCS, Azure o FTP con la misma API: Storage::disk('s3')->putFile('avatars', $file), url(), temporaryUrl(), delete(), exists(). Los discos se definen en config/filesystems.php; en producción es habitual almacenar en S3 y servir mediante CDN o URLs firmadas. Para subidas grandes se usan direct uploads (URL prefirmada) o chunked uploads, y se valida tipo/tamaño con Form Requests (image, mimes, max). Nunca confiar en la extensión ni ejecutar archivos subidos; almacenar fuera del webroot público, con nombres aleatorios, y usar colas para procesamiento (miniaturas, virus scan). En tests, Storage::fake('s3') y assertExists. Definir políticas de retención y limpieza de archivos huérfanos.

9. Testing, calidad y depuración

Q70. ¿PHPUnit o Pest? ¿Cómo se estructuran los tests?

Laravel incluye PHPUnit por defecto, pero desde Laravel 11 el scaffold pregunta por Pest (sintaxis más expresiva, basada en funciones: it('creates a post')->expect(...), con beforeEach, datasets y plugins). Ambos usan el mismo runner y las mismas utilidades del framework. La estructura típica separa Feature (peticiones HTTP completas, rutas, middleware, base de datos) de Unit (clases y lógica aislada). Se usa php artisan make:test, el archivo phpunit.xml define suites y variables de entorno (base de datos de test, cola sync, mail array), y php artisan test ejecuta con salida legible (Pest o PHPUnit). Buenas prácticas: tests rápidos y deterministas, nombres que describan comportamiento, factories para datos, un assert claro por test y cubrir caminos felices, validaciones, autorización y errores.

Q71. ¿Qué hacen RefreshDatabase, las factories y los seeders en tests?

RefreshDatabase migra la base de datos de test y la envuelve en transacciones (o la refresca) para que cada test empiece limpio; es la opción más habitual, aunque en suites grandes se puede usar DatabaseTransactions si el esquema ya está migrado. Las factories (User::factory()->create()) generan modelos con datos falsos y permiten estados (->unverified(), ->admin()) y relaciones; make() crea sin persistir. Los seeders cargan datos de referencia (roles, países) y pueden invocarse en tests concretos. Para tests de autorización se combinan factories con roles/permisos; para endpoints, actingAs($user) y Sanctum::actingAs($user, ['abilities']). Evitar depender del orden de tests y de datos persistentes entre ejecuciones.

Q72. ¿Cómo se usan los fakes y el mocking?

Laravel ofrece fakes para los servicios del framework: Http::fake([...]), Queue::fake(), Event::fake(), Mail::fake(), Notification::fake(), Storage::fake(), Bus::fake() y Cache::spy(). Permiten aislar el test y verificar interacciones con assertSent, assertPushed, assertDispatched, assertQueued, etc. Para colaboradores propios se usa Mockery ($this->mock(Service::class)) o inyección de una implementación falsa; en Laravel se prefiere $this->instance() o bindings en el contenedor antes que mocks profundos. Regla: mockear la frontera (HTTP, colas, correo, reloj, filesystem) y no la lógica propia; si un test necesita muchos mocks, probablemente la clase tiene demasiadas responsabilidades. Carbon::setTestNow() congela el tiempo para fechas deterministas.

Q73. ¿Qué son las pruebas de navegador con Dusk?

Laravel Dusk automatiza un navegador real (Chrome/Chromium) para probar flujos end-to-end: login, formularios, JavaScript, modales, arrastrar y soltar. Se instala con composer require laravel/dusk --dev y php artisan dusk:install; los tests usan $browser->visit('/')->type('email', ...)->press('Login')->assertSee('Dashboard'). Dusk puede usar un entorno .env.dusk.local, base de datos dedicada y DatabaseMigrations. Es más lento y frágil que los tests Feature, así que se reserva para flujos críticos (checkout, onboarding, pago). Alternativas: Playwright o Cypress en proyectos con mucho frontend. Ejecutar en CI con Chrome headless, capturas/vídeos en fallos, y limpiar datos entre pruebas. No sustituye a tests de unidad/integración: los complementa.

Q74. ¿Para qué sirven Telescope, Pulse y Debugbar?

Complementar con logs estructurados, Sentry/Bugsnag para excepciones y APM (New Relic, Datadog, Blackfire) para trazas y profiling.

Q75. ¿Cómo se configuran logging y el manejo de excepciones?

El logging usa canales en config/logging.php: single, daily, stderr, syslog, slack, papertrail o stack (varios a la vez). En producción conviene daily o stderr (contenedores) con nivel warning/error, logs estructurados en JSON y correlación por request. Log::info/error con contexto, y Log::withContext() para añadir request_id/user_id. El manejo de excepciones se centraliza en bootstrap/app.php (Laravel 11/12) con withExceptions: render para respuestas personalizadas, report para enviar a Sentry y dontReport para ruido esperado. No registrar contraseñas, tokens ni datos personales; definir páginas 404/500/503; y en APIs devolver JSON consistente. Para errores de negocio, usar excepciones propias y convertirlas en respuestas 4xx.

Q76. ¿Qué herramientas de calidad se usan (Pint, PHPStan/Larastan, Rector)?

Se integran en pre-commit (Husky/lint-staged o CaptainHook) y en CI; el nivel de PHPStan debe acordarse y subir gradualmente, no bloquear el equipo con miles de errores heredados.

Q77. ¿Cómo se monta un pipeline de CI/CD para Laravel?

Un pipeline típico (GitHub Actions, GitLab CI) ejecuta en cada PR: instalar PHP/Composer con caché, composer install --no-interaction, generar .env y APP_KEY, crear la base de datos de test (MySQL/PostgreSQL como servicio), php artisan migrate, php artisan test, Pint en modo test, PHPStan/Larastan y composer audit. Opcionalmente build de assets con Node (npm ci && npm run build) y Dusk en un job aparte. Para despliegue: construir artefacto, ejecutar migraciones, config:cache, route:cache, view:cache, queue:restart y recargar workers; en Vapor/Forge/Envoyer el flujo se integra con Git o CLI. Usar entornos separados, secretos del repositorio, ramas protegidas, revisión obligatoria y despliegues con rollback.

10. Rendimiento, despliegue y administración

Q78. ¿Qué hacen config:cache, route:cache, view:cache y opcache?

En cada petición Laravel carga configuración, rutas y vistas; los comandos de caché precompilan esos artefactos:

En despliegues hay que limpiar (optimize:clear) antes de regenerar si cambian rutas/config. Además, habilitar OPcache de PHP con opcache.validate_timestamps=0 en producción (y reiniciar PHP-FPM al desplegar) reduce el coste de compilación. Los comandos no hacen que la app sea más rápida por sí solos: eliminan trabajo repetido por request.

Q79. ¿Qué es Laravel Octane y qué cuidados exige?

Octane mantiene la aplicación en memoria y atiende peticiones con workers persistentes usando Swoole, OpenSwoole, RoadRunner o FrankenPHP, lo que reduce el arranque por request y mejora el rendimiento. Implica que el estado del contenedor sobrevive entre peticiones: hay que evitar guardar estado de usuario en propiedades de servicios singleton, limpiar variables estáticas y usar Octane::scoped() o singleton correctamente; los servicios con estado por request deben registrarse como scoped y los listeners de eventos flush. También cambian cosas como request() global, archivos subidos y sesiones. Requiere supervisar workers, reiniciarlos en cada despliegue (octane:reload) y probar compatibilidad de paquetes. No es magia: primero medir, y sólo adoptarlo si el cuello de botella es el bootstrap de PHP.

Q80. ¿Cómo se optimizan las consultas y el acceso a datos?

Primero medir: Telescope, Debugbar, Pulse, DB::listen o un APM para detectar consultas lentas y N+1. Luego: eager loading (with('author', 'comments')) y preventLazyLoading() en desarrollo; seleccionar solo columnas necesarias (select('id','name')); índices en columnas de filtro, join y orden; evitar whereHas costosos sin índice y preferir whereIn/joins cuando aplique; usar chunkById o lazyById para procesar grandes volúmenes sin cargar todo en memoria; exists() en lugar de count() > 0; updateOrCreate/upserts para escrituras masivas; y paginación por cursor en listados grandes. Revisar el plan de ejecución (EXPLAIN), el tamaño de las tablas y la configuración de conexiones. Evitar consultas dentro de loops y cachear agregados costosos con TTL e invalidación clara.

Q81. ¿Cómo se hace un despliegue sin downtime?

Patrón general: construir la release en un directorio nuevo (releases/2026-10-16), instalar dependencias con composer install --no-dev --optimize-autoloader, compilar assets, enlazar storage y .env, ejecutar migraciones compatibles hacia atrás y cambiar el symlink current de forma atómica; luego recargar PHP-FPM/Octane y reiniciar workers. Herramientas: Envoyer (despliegue zero-downtime con rollback), Forge (servidores y recetas), Vapor (serverless en AWS Lambda con assets en S3/CloudFront), Laravel Cloud, o Docker + Kubernetes/ECS. Las migraciones deben ser seguras: añadir columnas nullable o con default, separar cambios destructivos en releases posteriores, y probar rollback. Mantener compatibilidad de caché entre versiones, evitar cambios de esquema incompatibles simultáneos y monitorizar errores tras el cambio.

Q82. ¿Cómo se operan colas y scheduler en producción?

Los workers se gestionan con Supervisor (o systemd, Horizon, Kubernetes) para reiniciarlos si fallan, con numprocs por cola y logs separados. El comando base es php artisan queue:work --queue=high,default --tries=3 --timeout=90 --sleep=1; queue:listen es más cómodo pero menos eficiente. En cada despliegue se ejecuta queue:restart para que los workers carguen el código nuevo; con Horizon, horizon:terminate. El scheduler se activa con una única entrada cron que ejecuta schedule:run cada minuto; en clústeres, onOneServer(). Monitorizar longitud de colas, jobs fallidos, tiempos de espera y workers caídos; alertar si la cola crece. Configurar failed_jobs, reintentos manuales y una política de retención. Cuidado con timeouts de PHP/DB y con jobs que bloquean la cola.

Q83. ¿Qué monitoreo y alertas necesita una aplicación Laravel?

Capas: infraestructura (CPU, memoria, disco, PHP-FPM, base de datos, Redis, latencia de red), aplicación (throughput, latencia p95/p99, tasa de errores 5xx, excepciones, consultas lentas), negocio (pedidos, pagos, registros) y colas/scheduler (profundidad, fallos, retrasos). Herramientas: Pulse para métricas internas, Sentry/Bugsnag para excepciones con contexto, New Relic/Datadog/Blackfire para APM y profiling, y el proveedor cloud para infraestructura. Definir health checks (/up, /health) y alertas con umbrales accionables (no ruido), runbooks y dashboards. Centralizar logs (ELK, Loki, CloudWatch) con request_id y sin datos sensibles. Ensayar respuesta a incidentes: rollback, desactivar features, restaurar backups y comunicar.

Q84. ¿Cómo se actualiza Laravel de versión y se gestionan dependencias?

Seguir la guía oficial de upgrade (Laravel 11 → 12): revisar requisitos de PHP (12 requiere PHP 8.2+), actualizar composer.json (laravel/framework, laravel/*, PHPUnit/Pest, Collision), ejecutar composer update y resolver conflictos, revisar el changelog y los UPGRADE.md de los paquetes, y correr la suite de tests. Usar laravel/framework actualizado y composer audit/Dependabot para seguridad. En proyectos grandes, hacer la actualización en una rama, con Rector para cambios mecánicos, pruebas en staging y un plan de rollback. Fijar versiones (composer.lock en Git), revisar dependencias abandonadas y evitar paquetes que no soporten la versión objetivo. Programar actualizaciones periódicas en lugar de saltar varias versiones a la vez.

Q85. ¿Qué buenas prácticas de arquitectura y gobierno aplicar en un proyecto Laravel?

Mantener los controladores delgados: validación en Form Requests, autorización en Policies, lógica de negocio en clases de acción/servicios y acceso a datos en modelos/repositorios cuando aporte. Usar route model binding, Resources para APIs, eventos para efectos secundarios desacoplados y colas para trabajo pesado. Definir convenciones (nombres, estructura de carpetas, estándares de código con Pint, análisis estático), revisar PRs y documentar decisiones (ADRs). Evitar sobreingeniería: no introducir DDD/CQRS si el dominio no lo justifica; priorizar claridad, tests y bajo acoplamiento. Gestionar configuración por entorno, secretos fuera del código, migraciones versionadas y backups. Medir rendimiento antes de optimizar y mantener dependencias y framework al día. En equipos, automatizar todo lo posible en CI y cuidar la experiencia de desarrollo (Sail/Docker, seeders, docs).

11. Cómo responder preguntas de Laravel en una entrevista (plantilla)

Apertura (sí + estructura): «Sí. He trabajado con Laravel [versión 10/11/12] durante [X] años en [tipo de producto: SaaS, e-commerce, API, back-office], cubriendo desarrollo backend, APIs, base de datos, pruebas y despliegue en [Forge/Envoyer/Vapor/Docker/Kubernetes]».

Ciclo de vida y arquitectura: «Conozco el recorrido de una petición: public/index.php → bootstrap/app.php → kernel → middleware → router → controlador; uso el contenedor de servicios y providers para desacoplar, facades con criterio y middleware para cross-cutting concerns. Los controladores delegan en Form Requests, Policies y clases de acción/servicio».

Datos: «Modelo con Eloquent: relaciones, casts, scopes, observers y migraciones versionadas; controlo N+1 con eager loading y preventLazyLoading, uso transacciones, índices y paginación por cursor en listados grandes. Para cambios de esquema sigo el flujo migrate/rollback y pruebas con RefreshDatabase y factories».

APIs e integraciones: «Construyo APIs versionadas con rutas en routes/api.php, Sanctum o Passport, Form Requests, API Resources, códigos de error consistentes, rate limiting e idempotencia. Consumo servicios con el cliente HTTP con timeouts/reintentos y fakes en tests, y proceso webhooks con verificación de firma y colas».

Frontend: «He trabajado con Blade y componentes, Vite/Tailwind, y [Livewire o Inertia + React/Vue]; uso Breeze/Fortify o Jetstream según el proyecto y cuido la autenticación, la autorización y la validación del lado servidor».

Calidad y operación: «Escribo tests Feature/Unit con PHPUnit o Pest, uso factories y fakes, y complemento con Dusk en flujos críticos. En CI ejecuto tests, Pint, PHPStan/Larastan y composer audit; en producción uso colas con Supervisor/Horizon, scheduler, caché de configuración/rutas/vistas, OPcache y monitoreo con Pulse/Sentry».

Cierre (resultados + aplicación al rol): «En mis proyectos logré [métrica: reducción de latencia, aumento de cobertura, migración de versión sin downtime, integración de N servicios]. En este rol aportaría [desarrollo de APIs/arquitectura/rendimiento] y ayudaría a mantener un código simple, probado y fácil de desplegar».

Evidencia sugerida por área (elige lo que realmente hayas hecho)

ÁreaQué mencionarImpacto demostrable
Backend y arquitecturaContenedor/providers, middleware, servicios/acciones, eventos, patrón de capasMódulos desacoplados, menor tiempo de cambio, código reutilizable
Base de datosEloquent, migraciones, índices, transacciones, eager loading, paginaciónConsultas más rápidas, migraciones sin downtime, integridad de datos
APIsSanctum/Passport, Resources, validación, versionado, rate limiting, OpenAPIAPIs estables consumidas por N clientes, menos incidencias
FrontendBlade/componentes, Vite, Tailwind, Livewire/Inertia, autenticaciónInterfaces funcionales, tiempos de carga mejores
Testing y calidadPHPUnit/Pest, factories, fakes, Dusk, Pint, PHPStan/LarastanCobertura, menos regresiones, CI confiable
Colas y schedulerJobs, reintentos, batches/chains, Horizon, tareas programadasProcesos pesados fuera de la request, fiabilidad operativa
RendimientoCaché de config/rutas/vistas, OPcache, Redis, Octane, profilingReducción medible de latencia y coste de infraestructura
Despliegue y operaciónForge/Envoyer/Vapor/Docker, migraciones, cero downtime, monitoreoReleases predecibles, rollback y menor MTTR
SeguridadSanctum, Policies, CSRF, XSS/SQLi, secretos, auditoría de dependenciasVulnerabilidades corregidas, cumplimiento y menos riesgo

Consejos

12. Referencias

ReferenciaQué cubre
Laravel Documentation (laravel.com/docs)Guía oficial: ciclo de vida, contenedor, Eloquent, validación, autenticación, colas, testing, despliegue y upgrades.
Laravel API Reference (api.laravel.com)Firmas de clases, métodos e interfaces del framework; útil para detalles exactos.
LaracastsCursos y screencasts prácticos sobre Laravel, PHP moderno, testing y patrones.
Laravel NewsNovedades de versiones, paquetes y buenas prácticas de la comunidad.
Symfony DocumentationComponentes subyacentes (HttpFoundation, Routing, Console, EventDispatcher) que Laravel reutiliza.
PHP Manual (php.net)Referencia de lenguaje, tipos, sesiones, seguridad y extensiones.
PHP-FIG PSRsEstándares de interoperabilidad (PSR-4, PSR-7, PSR-12, PSR-15) usados en el ecosistema.
Composer DocumentationGestión de dependencias, versionado semántico, scripts y autoload.
PHPUnit / Pest DocumentationEscritura de tests, fixtures, mocking y ejecución en CI.
Laravel Horizon, Telescope y Pulse docsSupervisión de colas Redis, depuración y métricas de producción.
Laravel Octane y Deployment docsWorkers persistentes (Swoole/RoadRunner/FrankenPHP), Forge, Envoyer, Vapor y optimización de despliegue.
Laravel Security Policy y advisoriesVersiones soportadas, proceso de reporte y parches de seguridad.
OWASP Top 10 y Cheat SheetsRiesgos web (XSS, SQLi, CSRF, control de acceso) y mitigaciones aplicables a Laravel.