Câu Hỏi Phỏng Vấn PHP Laravel Framework 2026: Eloquent, Queues và Kiến Trúc Patterns
Câu hỏi phỏng vấn PHP Laravel framework cho năm 2026. Nắm vững Eloquent ORM, kiến trúc queue, các pattern service container và thử thách coding cho vị trí senior.

Câu hỏi phỏng vấn PHP Laravel framework đánh giá nhiều hơn kiến thức về cú pháp. Các công ty tuyển dụng năm 2026 kỳ vọng ứng viên giải thích về tối ưu relationship Eloquent, xử lý lỗi queue và service container bindings với mức độ chi tiết production. Laravel 13 giới thiệu AI tooling, JSON:API resources và khả năng vector search, tạo thêm chiều sâu mới cho các cuộc phỏng vấn kỹ thuật.
Vị trí senior Laravel tập trung vào ba lĩnh vực: hiệu suất Eloquent (ngăn chặn N+1, chunking, cursor pagination), độ tin cậy queue (retries, dead letter handling, job batching) và các quyết định kiến trúc (service providers, facades vs injection, repository patterns).
Eloquent ORM: Relationship Loading và Query Optimization
Câu hỏi phỏng vấn Eloquent thường xuyên nhắm vào vấn đề N+1. Ứng viên cần chứng minh khi nào sử dụng with(), load() và loadMissing(), cùng với giải thích về đánh đổi bộ nhớ khi eager loading dataset lớn.
// Eager load với constraints để tránh load dữ liệu không cần thiết
$users = User::query()
->with(['posts' => function (Builder $query) {
$query->where('published', true)
->select('id', 'user_id', 'title', 'published_at');
}])
->withCount('posts')
->paginate(25);
// Xấu: kích hoạt N+1 khi truy cập posts trong vòng lặp
foreach ($users as $user) {
$user->posts; // Mỗi lần lặp thực hiện query đến database
}Phương thức withCount() thêm subquery đếm các related records mà không load chúng. Điều này tránh overhead bộ nhớ khi hiển thị counts trong trang listing. Với những relationships có thể không được truy cập, loadMissing() ngăn các query thừa khi relationship đã được eager load trước đó.
Câu hỏi follow-up phổ biến hỏi về cursor pagination vs offset pagination. Cursor pagination sử dụng cursorPaginate() và tránh performance cliff mà offset pagination gặp phải trên các bảng lớn. Đánh đổi: cursor pagination không hỗ trợ điều hướng trực tiếp đến trang cụ thể.
// Cursor pagination cho dataset lớn
$posts = Post::query()
->where('status', 'published')
->orderBy('published_at', 'desc')
->cursorPaginate(50);
// Chunk processing cho batch operations
User::query()
->where('last_login_at', '<', now()->subYear())
->chunkById(1000, function (Collection $users) {
foreach ($users as $user) {
$user->markAsInactive();
}
});Phương thức chunkById() xử lý records theo batch mà không gây memory exhaustion. Không giống chunk(), phương thức này duy trì ordering nhất quán khi records bị sửa đổi trong quá trình lặp.
Query Scopes và Reusable Query Logic
Nhà tuyển dụng kỳ vọng ứng viên tổ chức query logic sử dụng local và global scopes. Local scopes đóng gói các điều kiện filter thường dùng, trong khi global scopes áp dụng constraints tự động cho tất cả queries.
class Post extends Model
{
// Local scope cho nội dung đã publish
public function scopePublished(Builder $query): Builder
{
return $query->where('status', 'published')
->whereNotNull('published_at')
->where('published_at', '<=', now());
}
// Local scope với tham số
public function scopeByAuthor(Builder $query, int $authorId): Builder
{
return $query->where('author_id', $authorId);
}
// Scope có thể chain
public function scopeRecent(Builder $query, int $days = 7): Builder
{
return $query->where('published_at', '>=', now()->subDays($days));
}
}
// Sử dụng: chaining multiple scopes
$recentPosts = Post::published()
->byAuthor($userId)
->recent(14)
->get();Global scopes áp dụng cho tất cả queries trên model. Các trường hợp sử dụng phổ biến bao gồm soft deletes, tenant isolation trong ứng dụng multi-tenant và status filtering.
class ActiveScope implements Scope
{
public function apply(Builder $builder, Model $model): void
{
$builder->where('is_active', true);
}
}
// app/Models/Subscription.php
class Subscription extends Model
{
protected static function booted(): void
{
static::addGlobalScope(new ActiveScope());
}
}
// Bypass global scope khi cần
$allSubscriptions = Subscription::withoutGlobalScope(ActiveScope::class)->get();Queue System: Job Handling và Failure Recovery
Kiến trúc queue của Laravel cung cấp độ tin cậy thông qua retries, timeouts và dead letter queues. Câu hỏi phỏng vấn tập trung vào xử lý lỗi và đảm bảo job idempotency.
class ProcessPayment implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public int $tries = 3;
public int $maxExceptions = 2;
public int $timeout = 120;
public int $backoff = 60;
public function __construct(
public readonly Order $order,
public readonly string $idempotencyKey
) {}
public function handle(PaymentGateway $gateway): void
{
// Kiểm tra idempotency để ngăn xử lý trùng lặp
if (Cache::has("payment_processed:{$this->idempotencyKey}")) {
return;
}
$result = $gateway->charge(
$this->order->total,
$this->order->payment_method_id
);
$this->order->update([
'payment_status' => $result->status,
'payment_id' => $result->id,
]);
Cache::put(
"payment_processed:{$this->idempotencyKey}",
true,
now()->addDays(7)
);
}
public function failed(Throwable $exception): void
{
Log::error('Payment processing failed', [
'order_id' => $this->order->id,
'error' => $exception->getMessage(),
]);
$this->order->update(['payment_status' => 'failed']);
Notification::route('slack', config('services.slack.payments_channel'))
->notify(new PaymentFailedNotification($this->order, $exception));
}
public function retryUntil(): DateTime
{
return now()->addHours(6);
}
}Property $maxExceptions giới hạn số lần job có thể throw exception trước khi bị coi là thất bại, tách biệt với $tries đếm tổng số attempts. Property $backoff xác định độ trễ giữa các lần retry.
Job Batching và Chaining
Laravel cung cấp job batching để xử lý nhiều jobs như một đơn vị với tracking tiến độ và xử lý lỗi.
// Dispatch batch of jobs
$batch = Bus::batch([
new ProcessPodcastEpisode($episode1),
new ProcessPodcastEpisode($episode2),
new ProcessPodcastEpisode($episode3),
])
->then(function (Batch $batch) {
Log::info('All episodes processed successfully');
})
->catch(function (Batch $batch, Throwable $e) {
Log::error('Batch processing failed', ['error' => $e->getMessage()]);
})
->finally(function (Batch $batch) {
// Cleanup resources
})
->allowFailures()
->dispatch();
// Monitor batch progress
$progress = Bus::findBatch($batch->id);
echo $progress->progress(); // Percentage complete
echo $progress->failedJobs; // Count of failed jobsJob chaining chạy các jobs theo tuần tự, job tiếp theo chỉ thực thi nếu job trước đó thành công.
// Job chain cho multi-step workflow
Bus::chain([
new ValidateOrder($order),
new ProcessPayment($order),
new SendConfirmationEmail($order),
new UpdateInventory($order),
])->dispatch();Service Container và Dependency Injection
Hiểu service container rất quan trọng cho vị trí senior. Nhà tuyển dụng kiểm tra kiến thức về binding types, contextual binding và khi nào sử dụng singleton vs transient bindings.
class PaymentServiceProvider extends ServiceProvider
{
public function register(): void
{
// Singleton: cùng instance trong toàn bộ request
$this->app->singleton(PaymentGateway::class, function ($app) {
return new StripeGateway(
config('services.stripe.secret'),
$app->make(HttpClient::class)
);
});
// Binding interface tới implementation
$this->app->bind(
PaymentProcessorInterface::class,
StripePaymentProcessor::class
);
// Contextual binding: implementation khác nhau dựa trên consumer
$this->app->when(SubscriptionController::class)
->needs(PaymentProcessorInterface::class)
->give(RecurringPaymentProcessor::class);
$this->app->when(CheckoutController::class)
->needs(PaymentProcessorInterface::class)
->give(OneTimePaymentProcessor::class);
}
}Contextual bindings cho phép giải quyết interface khác nhau dựa trên class nào yêu cầu dependency. Điều này tránh service locator pattern và duy trì dependency injection sạch.
Repository Pattern và Service Layer
Mặc dù Eloquent cung cấp query building mạnh mẽ, các team thường triển khai repository pattern để abstraction và testability.
interface UserRepositoryInterface
{
public function findById(int $id): ?User;
public function findByEmail(string $email): ?User;
public function createWithProfile(array $userData, array $profileData): User;
public function getActiveWithSubscriptions(): Collection;
}
// app/Repositories/EloquentUserRepository.php
class EloquentUserRepository implements UserRepositoryInterface
{
public function __construct(
private readonly User $model
) {}
public function findById(int $id): ?User
{
return $this->model->find($id);
}
public function findByEmail(string $email): ?User
{
return $this->model->where('email', $email)->first();
}
public function createWithProfile(array $userData, array $profileData): User
{
return DB::transaction(function () use ($userData, $profileData) {
$user = $this->model->create($userData);
$user->profile()->create($profileData);
return $user->load('profile');
});
}
public function getActiveWithSubscriptions(): Collection
{
return $this->model
->where('status', 'active')
->with(['subscriptions' => fn($q) => $q->active()])
->get();
}
}Service layer chứa business logic và điều phối multiple repositories.
class UserRegistrationService
{
public function __construct(
private readonly UserRepositoryInterface $userRepository,
private readonly NotificationService $notificationService,
private readonly EventDispatcher $events
) {}
public function register(RegisterUserDTO $dto): User
{
$user = $this->userRepository->createWithProfile(
$dto->toUserArray(),
$dto->toProfileArray()
);
$this->notificationService->sendWelcomeEmail($user);
$this->events->dispatch(new UserRegistered($user));
return $user;
}
}Sẵn sàng chinh phục phỏng vấn Laravel?
Luyện tập với mô phỏng tương tác, flashcards và bài kiểm tra kỹ thuật.
Event-Driven Architecture và Observers
Laravel events tách rời các components và cho phép multiple listeners phản hồi các state changes. Nhà tuyển dụng kỳ vọng ứng viên giải thích khi nào sử dụng events vs observers vs model hooks.
class OrderPlaced implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public function __construct(
public readonly Order $order
) {}
public function broadcastOn(): array
{
return [
new PrivateChannel('orders.' . $this->order->user_id),
];
}
}
// app/Listeners/SendOrderConfirmation.php
class SendOrderConfirmation implements ShouldQueue
{
public function handle(OrderPlaced $event): void
{
Mail::to($event->order->user)
->send(new OrderConfirmationMail($event->order));
}
public function shouldQueue(OrderPlaced $event): bool
{
return $event->order->total > 0;
}
}
// app/Providers/EventServiceProvider.php
protected $listen = [
OrderPlaced::class => [
SendOrderConfirmation::class,
UpdateInventory::class,
NotifyWarehouse::class,
RecordAnalytics::class,
],
];Model observers nhóm các event handlers liên quan đến lifecycle của model.
class OrderObserver
{
public function created(Order $order): void
{
$order->generateOrderNumber();
Cache::tags(['orders', "user:{$order->user_id}"])->flush();
}
public function updated(Order $order): void
{
if ($order->wasChanged('status')) {
event(new OrderStatusChanged($order));
}
}
public function deleted(Order $order): void
{
$order->lineItems()->delete();
Storage::delete($order->attachments);
}
}Testing Ứng Dụng Laravel
Phỏng vấn senior bao gồm câu hỏi về chiến lược testing. Laravel cung cấp helpers cho feature testing, unit testing và mocking external services.
class OrderControllerTest extends TestCase
{
use RefreshDatabase;
public function test_user_can_place_order(): void
{
$user = User::factory()->create();
$product = Product::factory()->create(['price' => 5000]);
$this->actingAs($user)
->postJson('/api/orders', [
'items' => [
['product_id' => $product->id, 'quantity' => 2]
],
'shipping_address_id' => $user->addresses()->first()->id,
])
->assertCreated()
->assertJsonStructure([
'data' => ['id', 'total', 'status', 'items']
]);
$this->assertDatabaseHas('orders', [
'user_id' => $user->id,
'total' => 10000,
]);
}
public function test_payment_failure_does_not_create_order(): void
{
$this->mock(PaymentGateway::class, function ($mock) {
$mock->shouldReceive('charge')
->once()
->andThrow(new PaymentFailedException('Card declined'));
});
$user = User::factory()->create();
$this->actingAs($user)
->postJson('/api/orders', $this->validOrderData())
->assertStatus(422)
->assertJsonPath('message', 'Payment failed: Card declined');
$this->assertDatabaseMissing('orders', ['user_id' => $user->id]);
}
}Middleware và Request Lifecycle
Hiểu request lifecycle và middleware pipeline rất quan trọng cho debugging và optimization.
class EnsureTeamAccess
{
public function handle(Request $request, Closure $next, string $permission): Response
{
$team = $request->route('team');
$user = $request->user();
if (!$user->belongsToTeam($team)) {
abort(403, 'You are not a member of this team');
}
if (!$user->hasTeamPermission($team, $permission)) {
abort(403, "Missing permission: {$permission}");
}
return $next($request);
}
}
// routes/web.php
Route::middleware(['auth', 'team.access:manage-projects'])
->prefix('teams/{team}')
->group(function () {
Route::resource('projects', ProjectController::class);
});Kết Luận
Chuẩn bị phỏng vấn Laravel đòi hỏi sự sâu sắc về Eloquent optimization, queue reliability và architectural patterns. Nhà tuyển dụng năm 2026 tập trung vào khả năng ứng viên xử lý workloads quy mô production: ngăn chặn N+1 thông qua eager loading chiến lược, job idempotency để ngăn xử lý trùng lặp và service container mastery cho maintainable code. Hiểu được các đánh đổi giữa repository pattern vs Eloquent trực tiếp, event-driven architecture vs procedural code và cursor vs offset pagination phân biệt ứng viên senior với junior. Thực hành với các code examples này và diễn đạt lý luận đằng sau mỗi quyết định kiến trúc.
Bạn có tìm ra lỗi trong Laravel không?
Một đoạn mã thật, một lỗi ẩn, mỗi ngày một lượt. Không cần tài khoản để thử.

Viết bởi
Anthony Fillion-MailletNgười sáng lập SharpSkill
Lập trình viên fullstack hơn 10 năm. Anh điều hành SharpSkill và chịu trách nhiệm về mọi nội dung đăng tại đây.
Cập nhật ngày 16 tháng 9, 2026
Chia sẻ
Bài viết liên quan

Laravel Octane trong 2026: Swoole, RoadRunner và Tối ưu Hiệu năng
Làm chủ Laravel Octane 2.19 với FrankenPHP, Swoole và RoadRunner. Tìm hiểu cách chọn server, phòng tránh memory leak, concurrent tasks và các pattern deployment production cho ứng dụng PHP hiệu năng cao.

Câu Hỏi Phỏng Vấn PHP Laravel Developer 2026: Hướng Dẫn Chuẩn Bị Toàn Diện
Hướng dẫn toàn diện để chuẩn bị cho buổi phỏng vấn PHP Laravel developer năm 2026. Bao gồm các câu hỏi kỹ thuật về Eloquent ORM, Service Container, authentication, queue và testing.

Laravel Solutions: Các Pattern Nâng Cao, Debug và Câu Hỏi Phỏng Vấn 2026
Tìm hiểu các pattern kiến trúc Laravel nâng cao, kỹ thuật debug với Telescope, và chuẩn bị câu hỏi phỏng vấn cho vị trí senior developer Laravel năm 2026.