Preguntas de Entrevista Laravel 2026: Eloquent, Queues y Patrones de Arquitectura

Guía completa de preguntas técnicas de entrevista Laravel en 2026. Domina Eloquent ORM, queues, patrones de arquitectura y mejores prácticas para tu próxima entrevista de trabajo.

Preguntas de Entrevista Laravel 2026

En 2026, Laravel continúa siendo el framework PHP más utilizado para el desarrollo de aplicaciones web empresariales. Con el lanzamiento de Laravel 12 y las constantes mejoras del ecosistema, los reclutadores esperan que los candidatos demuestren un dominio profundo de Eloquent ORM, sistemas de queues y patrones de arquitectura modernos. Este artículo presenta las preguntas más frecuentes en entrevistas técnicas de Laravel, acompañadas de ejemplos de código prácticos y explicaciones detalladas.

Lo que buscan los reclutadores

Las empresas buscan desarrolladores Laravel capaces de diseñar aplicaciones escalables y mantenibles. Más allá de la sintaxis, es fundamental demostrar comprensión de los conceptos subyacentes: el ciclo de vida de las peticiones, la inyección de dependencias, el Service Container y los patrones de diseño. Los candidatos exitosos son aquellos que pueden explicar el "por qué" detrás de cada decisión técnica.

Eloquent ORM: Preguntas avanzadas sobre relaciones

Eloquent está en el núcleo de Laravel y generalmente es el primer tema que se aborda en las entrevistas. Los reclutadores evalúan la capacidad de optimizar consultas y evitar problemas de rendimiento comunes.

¿Cómo se resuelve el problema de N+1 queries?

El problema N+1 ocurre cuando el código ejecuta una consulta adicional por cada elemento de una colección. Esta situación puede degradar rápidamente el rendimiento de la aplicación.

php
// Problema N+1: Una consulta para posts + N consultas para autores
$posts = Post::all();
foreach ($posts as $post) {
    echo $post->author->name; // Consulta adicional en cada iteración
}

// Solución: Eager Loading con with()
$posts = Post::with('author')->get();
foreach ($posts as $post) {
    echo $post->author->name; // Sin consultas adicionales
}

// Eager Loading condicional para necesidades específicas
$posts = Post::with(['author' => function ($query) {
    $query->select('id', 'name', 'email');
}])->get();

// Eager Loading anidado para relaciones múltiples
$posts = Post::with(['author.profile', 'comments.user'])->get();

El uso de with() permite cargar las relaciones en solo dos consultas, independientemente del tamaño de la colección. Para aplicaciones críticas, activar Model::preventLazyLoading() en desarrollo permite detectar automáticamente estos problemas.

Explica la diferencia entre hasOne, hasMany, belongsTo y belongsToMany

La comprensión de las relaciones Eloquent es fundamental para modelar correctamente una base de datos.

php
class User extends Model
{
    // Un usuario tiene un perfil (1:1)
    public function profile(): HasOne
    {
        return $this->hasOne(Profile::class);
    }

    // Un usuario puede tener varios artículos (1:N)
    public function posts(): HasMany
    {
        return $this->hasMany(Post::class);
    }

    // Un usuario puede tener varios roles (N:N)
    public function roles(): BelongsToMany
    {
        return $this->belongsToMany(Role::class)
            ->withPivot('assigned_at')
            ->withTimestamps();
    }
}

class Post extends Model
{
    // Un artículo pertenece a un usuario
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }

    // Relación polimórfica: un artículo puede tener comentarios
    public function comments(): MorphMany
    {
        return $this->morphMany(Comment::class, 'commentable');
    }
}

¿Cómo crear Query Scopes reutilizables?

Los scopes permiten encapsular la lógica de consulta para reutilizarla en toda la aplicación.

php
class Post extends Model
{
    // Scope local: filtrar artículos publicados
    public function scopePublished(Builder $query): Builder
    {
        return $query->whereNotNull('published_at')
            ->where('published_at', '<=', now());
    }

    // Scope con parámetro
    public function scopeByAuthor(Builder $query, int $authorId): Builder
    {
        return $query->where('author_id', $authorId);
    }

    // Scope dinámico basado en una condición
    public function scopePopular(Builder $query, int $minViews = 1000): Builder
    {
        return $query->where('views_count', '>=', $minViews)
            ->orderByDesc('views_count');
    }
}

// Uso encadenado de scopes
$trendingPosts = Post::published()
    ->popular(5000)
    ->with('author')
    ->limit(10)
    ->get();

Sistema de Queues: Procesamiento asíncrono y Jobs

Las queues son esenciales para mantener tiempos de respuesta rápidos delegando tareas pesadas a workers en segundo plano.

¿Cómo crear y despachar un Job?

app/Jobs/ProcessPodcastUpload.phpphp
class ProcessPodcastUpload implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function __construct(
        public readonly Podcast $podcast,
        public readonly string $filePath
    ) {}

    public function handle(AudioProcessor $processor): void
    {
        // Procesamiento del archivo de audio
        $processor->normalize($this->filePath);
        $processor->generateWaveform($this->filePath);
        
        // Actualización del estado
        $this->podcast->update(['status' => 'processed']);
        
        // Notificación al usuario
        $this->podcast->author->notify(new PodcastProcessed($this->podcast));
    }

    // Manejo de fallos
    public function failed(Throwable $exception): void
    {
        $this->podcast->update(['status' => 'failed']);
        Log::error('Podcast processing failed', [
            'podcast_id' => $this->podcast->id,
            'error' => $exception->getMessage()
        ]);
    }

    // Configuración de reintentos
    public int $tries = 3;
    public int $backoff = 60; // Segundos entre intentos
}

// Despacho del job
ProcessPodcastUpload::dispatch($podcast, $filePath)
    ->onQueue('audio-processing')
    ->delay(now()->addMinutes(5));

¿Cómo manejar Job Batches y cadenas de jobs?

Laravel permite agrupar varios jobs y monitorear su progreso.

php
use Illuminate\Bus\Batch;
use Illuminate\Support\Facades\Bus;

// Creación de un batch de jobs
$batch = Bus::batch([
    new ProcessImage($image1),
    new ProcessImage($image2),
    new ProcessImage($image3),
])->then(function (Batch $batch) {
    // Todos los jobs completados exitosamente
    Log::info('Batch completed successfully');
})->catch(function (Batch $batch, Throwable $e) {
    // Primer fallo detectado
    Log::error('Batch failed', ['error' => $e->getMessage()]);
})->finally(function (Batch $batch) {
    // Ejecutado en todos los casos
    Cache::forget('image-processing-status');
})->onQueue('images')
  ->dispatch();

// Cadena de jobs secuenciales
Bus::chain([
    new ExtractVideoFrames($video),
    new GenerateThumbnails($video),
    new NotifyUser($video->user),
])->onQueue('video-processing')
  ->dispatch();

Patrones de Arquitectura: Service Layer y Repository Pattern

Los patrones de arquitectura permiten estructurar el código de manera mantenible y testeable.

¿Cómo implementar el Repository Pattern?

app/Repositories/Contracts/UserRepositoryInterface.phpphp
interface UserRepositoryInterface
{
    public function find(int $id): ?User;
    public function findByEmail(string $email): ?User;
    public function create(array $data): User;
    public function update(int $id, array $data): bool;
    public function delete(int $id): bool;
    public function paginate(int $perPage = 15): LengthAwarePaginator;
}

// app/Repositories/EloquentUserRepository.php
class EloquentUserRepository implements UserRepositoryInterface
{
    public function __construct(
        private readonly User $model
    ) {}

    public function find(int $id): ?User
    {
        return $this->model->find($id);
    }

    public function findByEmail(string $email): ?User
    {
        return $this->model->where('email', $email)->first();
    }

    public function create(array $data): User
    {
        return $this->model->create($data);
    }

    public function update(int $id, array $data): bool
    {
        return $this->model->where('id', $id)->update($data) > 0;
    }

    public function delete(int $id): bool
    {
        return $this->model->destroy($id) > 0;
    }

    public function paginate(int $perPage = 15): LengthAwarePaginator
    {
        return $this->model->latest()->paginate($perPage);
    }
}

// Binding en AppServiceProvider
public function register(): void
{
    $this->app->bind(
        UserRepositoryInterface::class,
        EloquentUserRepository::class
    );
}

¿Cómo estructurar un Service Layer efectivo?

app/Services/OrderService.phpphp
class OrderService
{
    public function __construct(
        private readonly OrderRepositoryInterface $orderRepository,
        private readonly InventoryService $inventoryService,
        private readonly PaymentGateway $paymentGateway
    ) {}

    public function createOrder(User $user, array $items): Order
    {
        return DB::transaction(function () use ($user, $items) {
            // Verificación de stock
            foreach ($items as $item) {
                if (!$this->inventoryService->checkAvailability($item['product_id'], $item['quantity'])) {
                    throw new InsufficientStockException($item['product_id']);
                }
            }

            // Creación del pedido
            $order = $this->orderRepository->create([
                'user_id' => $user->id,
                'status' => OrderStatus::PENDING,
                'total' => $this->calculateTotal($items),
            ]);

            // Agregar líneas del pedido
            foreach ($items as $item) {
                $order->items()->create($item);
                $this->inventoryService->reserve($item['product_id'], $item['quantity']);
            }

            // Evento de creación
            event(new OrderCreated($order));

            return $order;
        });
    }

    private function calculateTotal(array $items): float
    {
        return collect($items)->sum(fn ($item) => $item['price'] * $item['quantity']);
    }
}

Middleware y Request Lifecycle

Comprender el ciclo de vida de una petición es esencial para depurar y optimizar aplicaciones Laravel.

¿Cómo crear un Middleware personalizado?

app/Http/Middleware/EnsureApiVersion.phpphp
class EnsureApiVersion
{
    public function handle(Request $request, Closure $next, string $version = 'v1'): Response
    {
        $requestedVersion = $request->header('Accept-Version', $version);
        
        if (!in_array($requestedVersion, ['v1', 'v2'])) {
            return response()->json([
                'error' => 'Unsupported API version',
                'supported' => ['v1', 'v2']
            ], 400);
        }

        // Agregar la versión al contexto de la petición
        $request->attributes->set('api_version', $requestedVersion);

        $response = $next($request);

        // Agregar headers de respuesta
        return $response->withHeaders([
            'X-API-Version' => $requestedVersion,
            'X-RateLimit-Remaining' => RateLimiter::remaining($request->ip(), 60)
        ]);
    }
}

// Registro en bootstrap/app.php (Laravel 11+)
return Application::configure(basePath: dirname(__DIR__))
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->alias([
            'api.version' => EnsureApiVersion::class,
        ]);
    });

Tests: Mejores prácticas para aplicaciones Laravel

Los tests son un tema frecuente en entrevistas, ya que demuestran un enfoque profesional del desarrollo.

¿Cómo testear un Controller con dependencias?

php
class OrderControllerTest extends TestCase
{
    use RefreshDatabase;

    public function test_user_can_create_order(): void
    {
        // Arrange
        $user = User::factory()->create();
        $product = Product::factory()->create(['stock' => 10, 'price' => 99.99]);
        
        // Mock del servicio de pago
        $this->mock(PaymentGateway::class, function ($mock) {
            $mock->shouldReceive('charge')
                ->once()
                ->andReturn(new PaymentResult(success: true, transactionId: 'txn_123'));
        });

        // Act
        $response = $this->actingAs($user)
            ->postJson('/api/orders', [
                'items' => [
                    ['product_id' => $product->id, 'quantity' => 2]
                ]
            ]);

        // Assert
        $response->assertStatus(201)
            ->assertJsonStructure([
                'data' => ['id', 'status', 'total', 'items']
            ]);

        $this->assertDatabaseHas('orders', [
            'user_id' => $user->id,
            'status' => 'pending'
        ]);
    }

    public function test_order_fails_with_insufficient_stock(): void
    {
        $user = User::factory()->create();
        $product = Product::factory()->create(['stock' => 1]);

        $response = $this->actingAs($user)
            ->postJson('/api/orders', [
                'items' => [
                    ['product_id' => $product->id, 'quantity' => 5]
                ]
            ]);

        $response->assertStatus(422)
            ->assertJsonValidationErrors(['items.0.quantity']);
    }
}

¿Listo para aprobar tus entrevistas de Laravel?

Practica con nuestros simuladores interactivos, flashcards y tests técnicos.

Preguntas conceptuales comunes

Más allá del código, los reclutadores evalúan la comprensión de conceptos fundamentales.

Explica el Service Container y la inyección de dependencias

El Service Container de Laravel es un contenedor de inversión de control (IoC) que gestiona las dependencias de las clases. Cuando una clase necesita una dependencia, el container la resuelve automáticamente.

php
// Resolución automática por type-hinting
class ReportController extends Controller
{
    public function __construct(
        private readonly ReportGenerator $generator, // Resuelto automáticamente
        private readonly CacheManager $cache
    ) {}
}

// Binding personalizado para interfaces
$this->app->bind(ReportGeneratorInterface::class, function ($app) {
    return new PdfReportGenerator(
        $app->make(PdfRenderer::class),
        config('reports.default_format')
    );
});

// Singleton para instancias compartidas
$this->app->singleton(MetricsCollector::class, function ($app) {
    return new MetricsCollector(
        $app->make(RedisConnection::class)
    );
});

¿Cómo optimizar el rendimiento de una aplicación Laravel?

La optimización del rendimiento abarca varios aspectos:

1. Cache de configuraciones y rutasphp
// php artisan config:cache
// php artisan route:cache
// php artisan view:cache

// 2. Uso de cache para datos frecuentemente accedidos
$users = Cache::remember('active-users', 3600, function () {
    return User::where('active', true)
        ->with('profile')
        ->get();
});

// 3. Paginación para grandes colecciones
$posts = Post::with('author')
    ->published()
    ->cursorPaginate(20); // Más eficiente que paginate() para grandes datasets

// 4. Índices de base de datos en migraciones
Schema::table('posts', function (Blueprint $table) {
    $table->index(['published_at', 'author_id']);
    $table->index('status');
});

// 5. Chunking para procesamiento masivo
User::where('newsletter', true)
    ->chunkById(1000, function ($users) {
        foreach ($users as $user) {
            SendNewsletter::dispatch($user);
        }
    });

Conclusión

La preparación para entrevistas Laravel en 2026 requiere un dominio profundo de Eloquent ORM, sistemas de queues y patrones de arquitectura. Los reclutadores buscan desarrolladores capaces de escribir código eficiente, mantenible y testeable. La práctica regular con proyectos reales sigue siendo la mejor manera de consolidar estos conocimientos. Se recomienda contribuir a proyectos open source y explorar las nuevas funcionalidades de Laravel 12 para mantenerse actualizado con la evolución del framework.

Reto diario

¿Sabrías detectar el bug en Laravel?

Un fragmento real, un bug oculto, un intento al día. Sin cuenta para probar.

Anthony Fillion-Maillet

Escrito por

Anthony Fillion-Maillet

Fundador de SharpSkill

Desarrollador fullstack desde hace más de 10 años. Dirige SharpSkill y responde por todo lo que se publica aquí.

Actualizado el 16 de septiembre de 2026

Etiquetas

#laravel
#php
#eloquent
#queues
#arquitectura
#entrevista

Compartir

Artículos relacionados