25 คำถามสัมภาษณ์งาน Laravel และ PHP ยอดนิยมในปี 2026
รวม 25 คำถามสัมภาษณ์งาน Laravel และ PHP ที่พบบ่อยที่สุด ครอบคลุม Service Container, Eloquent ORM, middleware, queues และการ deploy ระบบ production พร้อมคำตอบและตัวอย่างโค้ดครบถ้วน

การสัมภาษณ์งาน Laravel ประเมินความเชี่ยวชาญใน PHP framework ที่ได้รับความนิยมสูงสุด ความเข้าใจเกี่ยวกับ Eloquent ORM สถาปัตยกรรม MVC และความสามารถในการสร้างแอปพลิเคชันที่แข็งแกร่งและดูแลรักษาได้ง่าย คู่มือนี้ครอบคลุม 25 คำถามที่ถูกถามบ่อยที่สุด ตั้งแต่พื้นฐานของ Laravel ไปจนถึงรูปแบบการ deploy ระบบ production ขั้นสูง
ผู้สัมภาษณ์ให้ความสำคัญกับผู้สมัครที่สามารถอธิบายการตัดสินใจด้านสถาปัตยกรรมของ Laravel ได้ การเข้าใจว่าทำไม framework จึงใช้แนวทางบางอย่าง (Convention over Configuration, Service Container) สร้างความแตกต่างอย่างแท้จริงในการสัมภาษณ์
พื้นฐาน Laravel
คำถามที่ 1: อธิบายวงจรชีวิตของ Request ใน Laravel
วงจรชีวิตของ request ใน Laravel ผ่านหลายชั้นก่อนจะถึง controller การเข้าใจวงจรนี้มีความสำคัญอย่างยิ่งสำหรับการ debug และการปรับปรุงประสิทธิภาพ
// Entry point for all HTTP requests
require __DIR__.'/../vendor/autoload.php';
// Load the Laravel application
$app = require_once __DIR__.'/../bootstrap/app.php';
// The HTTP kernel handles the request
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);
$response->send();
$kernel->terminate($request, $response);วงจรที่สมบูรณ์: index.php -> autoload -> bootstrap -> service providers -> middlewares -> routing -> controller -> response -> terminate แต่ละขั้นตอนสามารถสกัดกั้นและปรับแต่งได้
คำถามที่ 2: Service Container คืออะไร และ Dependency Injection ทำงานอย่างไร?
Service Container เป็นหัวใจหลักของ Laravel ทำหน้าที่จัดการการสร้าง instance ของ class และ resolve dependency โดยอัตโนมัติผ่าน constructor injection
// Service with automatically injected dependencies
namespace App\Services;
use App\Contracts\PaymentGatewayInterface;
use App\Repositories\OrderRepository;
use Illuminate\Support\Facades\Log;
class PaymentService
{
public function __construct(
private PaymentGatewayInterface $gateway, // Interface resolved by container
private OrderRepository $orders // Concrete class auto-resolved
) {}
public function processPayment(int $orderId, float $amount): bool
{
$order = $this->orders->find($orderId);
try {
$result = $this->gateway->charge($amount, $order->customer);
$order->markAsPaid($result->transactionId);
return true;
} catch (PaymentException $e) {
Log::error('Payment failed', ['order' => $orderId, 'error' => $e->getMessage()]);
return false;
}
}
}// Binding an interface to a concrete implementation
public function register(): void
{
// Simple binding: new instance on each injection
$this->app->bind(
PaymentGatewayInterface::class,
StripeGateway::class
);
// Singleton: same instance shared everywhere
$this->app->singleton(
CacheService::class,
fn($app) => new CacheService($app['config']['cache.driver'])
);
}Dependency injection แยก class ออกจากกันและช่วยให้การทำ unit testing ผ่าน mocking ง่ายขึ้น
คำถามที่ 3: Facades กับ Dependency Injection แตกต่างกันอย่างไร?
Facades ให้ syntax แบบ static เพื่อเข้าถึง service ใน container ขณะที่ dependency injection ทำให้ dependency ชัดเจน
// Using Facades: concise syntax but implicit dependencies
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
class ReportController extends Controller
{
public function generate()
{
// Facades: static access to services
$data = Cache::remember('report_data', 3600, fn() => $this->fetchData());
Log::info('Report generated');
return view('report', compact('data'));
}
}
// Dependency Injection: explicit and testable dependencies
use Illuminate\Contracts\Cache\Repository as CacheContract;
use Psr\Log\LoggerInterface;
class ReportController extends Controller
{
public function __construct(
private CacheContract $cache,
private LoggerInterface $logger
) {}
public function generate()
{
// Same functionality, explicit dependencies
$data = $this->cache->remember('report_data', 3600, fn() => $this->fetchData());
$this->logger->info('Report generated');
return view('report', compact('data'));
}
}ควรใช้ dependency injection ใน business class เพื่อความสะดวกในการทดสอบ Facades เหมาะสำหรับ helper และโค้ดแบบ ad-hoc
คำถามที่ 4: Service Providers ทำงานอย่างไรใน Laravel?
Service Providers เป็นจุดกำหนดค่ากลางของแอปพลิเคชัน แต่ละ provider ลงทะเบียน service กำหนดค่า binding และเริ่มต้นส่วนประกอบต่างๆ
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Services\PaymentService;
use App\Contracts\PaymentGatewayInterface;
use App\Gateways\StripeGateway;
class PaymentServiceProvider extends ServiceProvider
{
// Register method: bindings and registrations
// Don't access other services here (not yet loaded)
public function register(): void
{
$this->app->singleton(PaymentGatewayInterface::class, function ($app) {
return new StripeGateway(
config('services.stripe.key'),
config('services.stripe.secret')
);
});
}
// Boot method: initialization after all providers
// Full access to all application services
public function boot(): void
{
// Register macros, event listeners, routes, etc.
$this->loadRoutesFrom(__DIR__.'/../routes/payment.php');
$this->loadViewsFrom(__DIR__.'/../resources/views', 'payment');
// Publish files for packages
$this->publishes([
__DIR__.'/../config/payment.php' => config_path('payment.php'),
], 'payment-config');
}
}ลำดับการทำงาน: method register() ทั้งหมดทำงานก่อน จากนั้น method boot() ทั้งหมดจึงทำงาน ลำดับนี้ทำให้มั่นใจว่า dependency พร้อมใช้งานในระหว่าง boot
Eloquent ORM
คำถามที่ 5: อธิบายความสัมพันธ์ของ Eloquent และความแตกต่าง
Eloquent มีหลายประเภทของ relationship เพื่อจำลองความสัมพันธ์ระหว่างตาราง แต่ละประเภทมีกรณีการใช้งานที่เฉพาะเจาะจง
class User extends Model
{
// A user has one profile (1:1)
public function profile(): HasOne
{
return $this->hasOne(Profile::class);
}
// A user has many articles (1:N)
public function articles(): HasMany
{
return $this->hasMany(Article::class);
}
// A user has many roles via pivot table (N:N)
public function roles(): BelongsToMany
{
return $this->belongsToMany(Role::class)
->withPivot('assigned_at') // Additional pivot columns
->withTimestamps(); // created_at/updated_at on pivot
}
// A user has many comments through articles (HasManyThrough)
public function comments(): HasManyThrough
{
return $this->hasManyThrough(
Comment::class, // Final model
Article::class // Intermediate model
);
}
}
// app/Models/Article.php
class Article extends Model
{
// An article belongs to a user (inverse of hasMany)
public function author(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id');
}
// Polymorphic relationship: an article can have tags, like other models
public function tags(): MorphToMany
{
return $this->morphToMany(Tag::class, 'taggable');
}
}Polymorphic relationships (morphOne, morphMany, morphToMany) ช่วยให้ model หนึ่งสามารถเชื่อมโยงกับ model หลายประเภทผ่าน relationship เดียว
คำถามที่ 6: ปัญหา N+1 คืออะไร และแก้ไขด้วย Eager Loading อย่างไร?
ปัญหา N+1 เกิดขึ้นเมื่อ query หลักหนึ่งรายการสร้าง N query เพิ่มเติมเพื่อโหลด relationship นี่เป็นสาเหตุที่พบบ่อยที่สุดของความช้าในแอปพลิเคชัน Laravel
// ❌ PROBLEM: N+1 queries
// 1 query for articles + 1 query PER article for the author
$articles = Article::all();
foreach ($articles as $article) {
echo $article->author->name; // SQL query on each iteration!
}
// ✅ SOLUTION 1: with() - Eager Loading
// Only 2 queries (articles + users with IN clause)
$articles = Article::with('author')->get();
foreach ($articles as $article) {
echo $article->author->name; // Already loaded, no query
}
// ✅ SOLUTION 2: Nested Eager Loading
// Loads articles, their authors, and author roles
$articles = Article::with(['author.roles', 'comments.user'])->get();
// ✅ SOLUTION 3: Eager Loading with constraints
$articles = Article::with([
'comments' => function ($query) {
$query->where('approved', true)
->orderBy('created_at', 'desc')
->limit(5);
}
])->get();
// ✅ SOLUTION 4: Default Eager Loading in the model
class Article extends Model
{
// These relationships are always loaded automatically
protected $with = ['author', 'category'];
}ใช้ php artisan telescope:prune ร่วมกับ Laravel Telescope เพื่อตรวจจับปัญหา N+1 ระหว่างการพัฒนา
คำถามที่ 7: วิธีสร้าง Query Scopes และควรใช้เมื่อไหร่?
Query Scopes ห่อหุ้มเงื่อนไข query ที่สามารถนำกลับมาใช้ซ้ำได้ในระดับ model ทำให้โค้ดอ่านง่ายขึ้นและเป็นไปตามหลัก DRY
class Article extends Model
{
// Global Scope: automatically applied to ALL queries
protected static function booted(): void
{
// Excludes soft-deleted articles by default
static::addGlobalScope('published', function (Builder $builder) {
$builder->where('status', 'published');
});
}
// Local Scope: called explicitly via scopeScopeName
public function scopePopular(Builder $query, int $minViews = 1000): Builder
{
return $query->where('view_count', '>=', $minViews);
}
public function scopeByAuthor(Builder $query, User $author): Builder
{
return $query->where('user_id', $author->id);
}
public function scopeRecent(Builder $query, int $days = 7): Builder
{
return $query->where('created_at', '>=', now()->subDays($days));
}
public function scopeWithStats(Builder $query): Builder
{
return $query->withCount('comments')
->withSum('reactions', 'score');
}
}
// Usage: fluent scope chaining
$articles = Article::popular(500)
->recent(30)
->byAuthor($user)
->withStats()
->orderByDesc('comment_count')
->paginate(20);
// Ignore a Global Scope
$allArticles = Article::withoutGlobalScope('published')->get();Local scopes ช่วยเพิ่มความอ่านง่ายและรวมศูนย์ logic ของ query Global scopes เหมาะสำหรับ multi-tenancy หรือ soft delete
พร้อมที่จะพิชิตการสัมภาษณ์ Laravel แล้วหรือยังครับ?
ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ
Middleware และ Routing
คำถามที่ 8: Middleware ทำงานอย่างไรใน Laravel?
Middleware กรอง HTTP request ขาเข้าและสามารถแก้ไข response ขาออกได้ ทุก request ผ่าน middleware stack
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class CheckSubscription
{
public function handle(Request $request, Closure $next, string $plan = 'basic'): Response
{
$user = $request->user();
// Check before the controller
if (!$user || !$user->hasActiveSubscription($plan)) {
if ($request->expectsJson()) {
return response()->json(['error' => 'Subscription required'], 403);
}
return redirect()->route('subscription.plans');
}
// Pass to next middleware or controller
$response = $next($request);
// Modify response after the controller
$response->headers->set('X-Subscription-Plan', $user->subscription->plan);
return $response;
}
}
// bootstrap/app.php (Laravel 11+)
return Application::configure(basePath: dirname(__DIR__))
->withMiddleware(function (Middleware $middleware) {
// Global middleware (all requests)
$middleware->append(LogRequestMiddleware::class);
// Aliases for use in routes
$middleware->alias([
'subscription' => CheckSubscription::class,
'role' => EnsureUserHasRole::class,
]);
// Middleware groups
$middleware->group('api', [
ThrottleRequests::class.':api',
SubstituteBindings::class,
]);
});// Applying middlewares to routes
Route::middleware(['auth', 'subscription:premium'])->group(function () {
Route::get('/dashboard', DashboardController::class);
Route::resource('projects', ProjectController::class);
});ลำดับของ middleware มีความสำคัญ: ทำงานจากบนลงล่างเมื่อเข้า และจากล่างขึ้นบนเมื่อออก
คำถามที่ 9: อธิบาย Route Model Binding และรูปแบบต่างๆ
Route Model Binding inject Eloquent model เข้าสู่ controller โดยอัตโนมัติตาม parameter ใน URL
// Implicit binding: Laravel automatically resolves by ID
Route::get('/articles/{article}', [ArticleController::class, 'show']);
// Binding by slug instead of ID
Route::get('/articles/{article:slug}', [ArticleController::class, 'show']);
// Binding with relationship (automatic scope)
Route::get('/users/{user}/articles/{article}', function (User $user, Article $article) {
// Laravel automatically verifies that the article belongs to the user
return $article;
})->scopeBindings();class Article extends Model
{
// Customize the default resolution key
public function getRouteKeyName(): string
{
return 'slug'; // Resolves by slug instead of id
}
// Customize the resolution query
public function resolveRouteBinding($value, $field = null): ?Model
{
return $this->where($field ?? 'slug', $value)
->where('status', 'published')
->firstOrFail();
}
}// Explicit custom binding
public function boot(): void
{
Route::bind('article', function (string $value) {
return Article::where('slug', $value)
->published()
->with('author')
->firstOrFail();
});
}Route Model Binding ลดโค้ด boilerplate และรวมศูนย์ logic การ resolve
Queues และ Jobs
คำถามที่ 10: วิธีใช้งาน Jobs และ Queues ใน Laravel?
Queues ช่วยเลื่อนงานหนักไปทำงานในเบื้องหลัง ทำให้แอปพลิเคชันตอบสนองได้เร็วขึ้น
namespace App\Jobs;
use App\Models\Podcast;
use App\Services\AudioProcessor;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class ProcessPodcast implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
// Number of attempts before final failure
public int $tries = 3;
// Timeout in seconds
public int $timeout = 600;
// Delay between attempts (exponential backoff)
public array $backoff = [30, 60, 120];
public function __construct(
public Podcast $podcast
) {}
public function handle(AudioProcessor $processor): void
{
// The job executes in the background
$processor->transcode($this->podcast->audio_path);
$processor->generateWaveform($this->podcast);
$this->podcast->update(['status' => 'processed']);
}
// Failure handling
public function failed(\Throwable $exception): void
{
$this->podcast->update(['status' => 'failed']);
// Admin notification, logging, etc.
}
// Conditions for retrying the job
public function retryUntil(): \DateTime
{
return now()->addHours(24);
}
}// Dispatching the job
ProcessPodcast::dispatch($podcast); // Default queue
ProcessPodcast::dispatch($podcast)->onQueue('audio'); // Specific queue
ProcessPodcast::dispatch($podcast)->delay(now()->addMinutes(10)); // Delay
// Job chaining (sequential execution)
Bus::chain([
new ProcessPodcast($podcast),
new GenerateThumbnail($podcast),
new NotifySubscribers($podcast),
])->dispatch();
// Job batch (parallel execution with tracking)
Bus::batch([
new ProcessPodcast($podcast1),
new ProcessPodcast($podcast2),
new ProcessPodcast($podcast3),
])->then(function (Batch $batch) {
// All jobs succeeded
})->catch(function (Batch $batch, \Throwable $e) {
// First failure
})->finally(function (Batch $batch) {
// All jobs finished (success or failure)
})->dispatch();เริ่ม worker ด้วย php artisan queue:work --queue=high,default เพื่อประมวลผลหลาย queue ตามลำดับความสำคัญ
คำถามที่ 11: Jobs, Events และ Listeners แตกต่างกันอย่างไร?
ทั้งสามแนวคิดนี้แยก code ออกจากกัน แต่มีจุดประสงค์ที่แตกต่างกัน
// Jobs: single task to execute
// Used for heavy or deferred operations
class SendWelcomeEmail implements ShouldQueue
{
public function handle(Mailer $mailer): void
{
$mailer->send(new WelcomeEmail($this->user));
}
}
// Events: notification that something happened
// The event contains only data, not logic
class UserRegistered
{
public function __construct(
public User $user,
public string $source
) {}
}
// Listeners: react to events
// An event can have multiple listeners
class SendWelcomeNotification implements ShouldQueue
{
public function handle(UserRegistered $event): void
{
$event->user->notify(new WelcomeNotification());
}
}
class TrackRegistration
{
public function handle(UserRegistered $event): void
{
Analytics::track('user_registered', [
'user_id' => $event->user->id,
'source' => $event->source,
]);
}
}protected $listen = [
UserRegistered::class => [
SendWelcomeNotification::class, // Queued
TrackRegistration::class, // Sync
CreateDefaultSettings::class, // Sync
],
];
// Triggering the event
event(new UserRegistered($user, 'web'));
// Or
UserRegistered::dispatch($user, 'web');Events สนับสนุนสถาปัตยกรรมแบบหลวม: โค้ดที่ trigger event ไม่จำเป็นต้องรู้ผลที่ตามมา
ความปลอดภัยและการยืนยันตัวตน
คำถามที่ 12: Laravel ป้องกันการโจมตี CSRF อย่างไร?
Laravel สร้าง token CSRF เฉพาะสำหรับแต่ละ session โดยอัตโนมัติ และตรวจสอบ token นี้ในทุก request ประเภท POST, PUT, PATCH, DELETE
// In Blade forms
<form method="POST" action="/profile">
@csrf {{-- Generates a hidden field with the token --}}
@method('PUT') {{-- HTTP method spoofing --}}
<input type="text" name="name" value="{{ $user->name }}">
<button type="submit">Update</button>
</form>
// For AJAX requests, the token is in the meta tag
<meta name="csrf-token" content="{{ csrf_token() }}">
// Axios configuration to automatically send the token
axios.defaults.headers.common['X-CSRF-TOKEN'] =
document.querySelector('meta[name="csrf-token"]').content;// Exclude routes from CSRF verification (external webhooks)
return Application::configure(basePath: dirname(__DIR__))
->withMiddleware(function (Middleware $middleware) {
$middleware->validateCsrfTokens(except: [
'stripe/webhook', // Stripe webhook authenticated by signature
'api/*', // API authenticated by token
]);
});ห้ามปิดการป้องกัน CSRF ทั้งระบบ ใช้ข้อยกเว้นเฉพาะสำหรับ endpoint ที่มีการยืนยันตัวตนด้วยวิธีอื่น
คำถามที่ 13: วิธีใช้งาน Authentication ด้วย Laravel Sanctum?
Laravel Sanctum ให้การยืนยันตัวตนแบบเบาสำหรับ SPA แอปมือถือ และ API แบบ token
// Installation and configuration
// php artisan install:api (Laravel 11+)
// app/Models/User.php
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
}class AuthController extends Controller
{
public function login(Request $request): JsonResponse
{
$credentials = $request->validate([
'email' => 'required|email',
'password' => 'required',
]);
if (!Auth::attempt($credentials)) {
return response()->json(['message' => 'Invalid credentials'], 401);
}
$user = Auth::user();
// Create a token with abilities (permissions)
$token = $user->createToken('api-token', [
'articles:read',
'articles:write',
'profile:update',
]);
return response()->json([
'user' => $user,
'token' => $token->plainTextToken,
]);
}
public function logout(Request $request): JsonResponse
{
// Revoke the current token
$request->user()->currentAccessToken()->delete();
// Or revoke all tokens
// $request->user()->tokens()->delete();
return response()->json(['message' => 'Logged out']);
}
}
// routes/api.php
Route::post('/login', [AuthController::class, 'login']);
Route::middleware('auth:sanctum')->group(function () {
Route::get('/user', fn(Request $r) => $r->user());
Route::post('/logout', [AuthController::class, 'logout']);
// Ability verification
Route::middleware('ability:articles:write')->group(function () {
Route::post('/articles', [ArticleController::class, 'store']);
});
});Sanctum ยังรองรับการยืนยันตัวตนด้วย cookie สำหรับ SPA บนโดเมนเดียวกัน ซึ่งให้การป้องกัน CSRF โดยอัตโนมัติ
คำถามที่ 14: วิธีรักษาความปลอดภัย API ด้วย Policies และ Gates?
Policies และ Gates รวมศูนย์ logic การอนุญาต แยกกฎเกณฑ์ทางธุรกิจออกจาก controller
namespace App\Policies;
use App\Models\Article;
use App\Models\User;
class ArticlePolicy
{
// Pre-check: admins have all rights
public function before(User $user, string $ability): ?bool
{
if ($user->isAdmin()) {
return true; // Allow everything
}
return null; // Continue to specific method
}
public function view(?User $user, Article $article): bool
{
// Published articles visible to all
if ($article->status === 'published') {
return true;
}
// Drafts visible only to author
return $user?->id === $article->user_id;
}
public function update(User $user, Article $article): bool
{
return $user->id === $article->user_id;
}
public function delete(User $user, Article $article): bool
{
return $user->id === $article->user_id
&& $article->comments()->count() === 0;
}
}class ArticleController extends Controller
{
public function update(Request $request, Article $article)
{
// Check policy, throws 403 if unauthorized
$this->authorize('update', $article);
$article->update($request->validated());
return redirect()->route('articles.show', $article);
}
}
// In Blade
@can('update', $article)
<a href="{{ route('articles.edit', $article) }}">Edit</a>
@endcan
// Gates for non-model authorizations
Gate::define('access-admin', function (User $user) {
return $user->role === 'admin';
});
// Usage
if (Gate::allows('access-admin')) {
// ...
}Policies ผูกกับ model ส่วน Gates ใช้สำหรับการอนุญาตทั่วไป
Validation และ Forms
คำถามที่ 15: วิธีสร้าง Custom Validation Rules?
Laravel มีหลายวิธีในการสร้าง validation แบบกำหนดเอง ขึ้นอยู่กับความซับซ้อนและความต้องการในการนำกลับมาใช้
// Custom rule as a class (reusable)
namespace App\Rules;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
class StrongPassword implements ValidationRule
{
public function validate(string $attribute, mixed $value, Closure $fail): void
{
$errors = [];
if (strlen($value) < 12) {
$errors[] = 'at least 12 characters';
}
if (!preg_match('/[A-Z]/', $value)) {
$errors[] = 'at least one uppercase letter';
}
if (!preg_match('/[a-z]/', $value)) {
$errors[] = 'at least one lowercase letter';
}
if (!preg_match('/[0-9]/', $value)) {
$errors[] = 'at least one digit';
}
if (!preg_match('/[@$!%*?&#]/', $value)) {
$errors[] = 'at least one special character';
}
if (!empty($errors)) {
$fail("The password must contain: " . implode(', ', $errors) . '.');
}
}
}// Form Request with complex validation
namespace App\Http\Requests;
use App\Rules\StrongPassword;
use Illuminate\Foundation\Http\FormRequest;
class RegisterRequest extends FormRequest
{
public function authorize(): bool
{
return true; // Or authorization logic
}
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'email', 'unique:users,email'],
'password' => ['required', 'confirmed', new StrongPassword()],
'company' => ['required_if:account_type,business', 'string'],
'vat_number' => [
'nullable',
'string',
// Closure for inline validation
function ($attribute, $value, $fail) {
if ($value && !$this->isValidVatNumber($value)) {
$fail('The VAT number is invalid.');
}
},
],
];
}
public function messages(): array
{
return [
'email.unique' => 'This email address is already in use.',
'password.confirmed' => 'The passwords do not match.',
];
}
protected function prepareForValidation(): void
{
// Normalization before validation
$this->merge([
'email' => strtolower(trim($this->email)),
]);
}
}Form Requests รวมศูนย์ validation การอนุญาต และข้อความ error ทำให้ controller เบาลง
พร้อมที่จะพิชิตการสัมภาษณ์ Laravel แล้วหรือยังครับ?
ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ
Testing
คำถามที่ 16: วิธีจัดโครงสร้าง Tests ใน Laravel?
Laravel มี PHPUnit พร้อม helper เฉพาะสำหรับทดสอบแต่ละชั้นของแอปพลิเคชัน
namespace Tests\Feature;
use App\Models\Article;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ArticleControllerTest extends TestCase
{
use RefreshDatabase; // Resets DB between each test
public function test_guest_can_view_published_articles(): void
{
$article = Article::factory()->published()->create();
$response = $this->get('/articles/' . $article->slug);
$response->assertStatus(200);
$response->assertSee($article->title);
}
public function test_authenticated_user_can_create_article(): void
{
$user = User::factory()->create();
$response = $this->actingAs($user)
->post('/articles', [
'title' => 'My new article',
'content' => 'Test content',
]);
$response->assertRedirect();
$this->assertDatabaseHas('articles', [
'title' => 'My new article',
'user_id' => $user->id,
]);
}
public function test_user_cannot_update_others_article(): void
{
$owner = User::factory()->create();
$other = User::factory()->create();
$article = Article::factory()->for($owner)->create();
$response = $this->actingAs($other)
->put('/articles/' . $article->id, [
'title' => 'Modified title',
]);
$response->assertStatus(403);
}
}namespace Tests\Unit\Services;
use App\Services\PaymentService;
use App\Contracts\PaymentGatewayInterface;
use App\Repositories\OrderRepository;
use Mockery;
use Tests\TestCase;
class PaymentServiceTest extends TestCase
{
public function test_process_payment_charges_correct_amount(): void
{
// Mock dependencies
$gateway = Mockery::mock(PaymentGatewayInterface::class);
$gateway->shouldReceive('charge')
->once()
->with(99.99, Mockery::any())
->andReturn((object) ['transactionId' => 'tx_123']);
$orders = Mockery::mock(OrderRepository::class);
$orders->shouldReceive('find')
->with(1)
->andReturn($this->createOrder());
$service = new PaymentService($gateway, $orders);
$result = $service->processPayment(1, 99.99);
$this->assertTrue($result);
}
}แยก Feature tests (HTTP, integration) ออกจาก Unit tests (class แบบแยกส่วนพร้อม mocks)
คำถามที่ 17: วิธีใช้ Factories และ Seeders อย่างมีประสิทธิภาพ?
Factories สร้างข้อมูลทดสอบที่สมจริง Seeders เติมข้อมูลลงในฐานข้อมูล
namespace Database\Factories;
use App\Models\Article;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
class ArticleFactory extends Factory
{
protected $model = Article::class;
public function definition(): array
{
return [
'user_id' => User::factory(),
'title' => fake()->sentence(6),
'slug' => fake()->unique()->slug(),
'content' => fake()->paragraphs(5, true),
'status' => 'draft',
'view_count' => fake()->numberBetween(0, 10000),
'created_at' => fake()->dateTimeBetween('-1 year'),
];
}
// States for different configurations
public function published(): static
{
return $this->state(fn(array $attr) => [
'status' => 'published',
'published_at' => fake()->dateTimeBetween('-6 months'),
]);
}
public function draft(): static
{
return $this->state(['status' => 'draft', 'published_at' => null]);
}
public function popular(): static
{
return $this->state(['view_count' => fake()->numberBetween(10000, 100000)]);
}
// Relationship configuration
public function configure(): static
{
return $this->afterCreating(function (Article $article) {
// Create tags after article creation
$article->tags()->attach(
\App\Models\Tag::factory()->count(3)->create()
);
});
}
}
// Usage in tests
$article = Article::factory()->published()->create();
$articles = Article::factory()->count(10)->for($user)->create();
$articleWithComments = Article::factory()
->has(Comment::factory()->count(5))
->create();States ช่วยให้สร้างรูปแบบต่างๆ ได้โดยไม่ต้องเขียน logic ของ factory ซ้ำ
สถาปัตยกรรมและ Design Patterns
คำถามที่ 18: วิธีใช้งาน Repository Pattern ใน Laravel?
Repository Pattern ทำให้การเข้าถึงข้อมูลเป็นนามธรรมและช่วยให้การทดสอบง่ายขึ้นโดยอนุญาตให้ mock query ได้
namespace App\Contracts;
use App\Models\Article;
use Illuminate\Pagination\LengthAwarePaginator;
interface ArticleRepositoryInterface
{
public function find(int $id): ?Article;
public function findBySlug(string $slug): ?Article;
public function getPublished(int $perPage = 20): LengthAwarePaginator;
public function getByAuthor(int $userId, int $perPage = 20): LengthAwarePaginator;
public function create(array $data): Article;
public function update(Article $article, array $data): Article;
public function delete(Article $article): bool;
}namespace App\Repositories;
use App\Contracts\ArticleRepositoryInterface;
use App\Models\Article;
use Illuminate\Pagination\LengthAwarePaginator;
class EloquentArticleRepository implements ArticleRepositoryInterface
{
public function __construct(
private Article $model
) {}
public function find(int $id): ?Article
{
return $this->model->with('author')->find($id);
}
public function findBySlug(string $slug): ?Article
{
return $this->model
->where('slug', $slug)
->with(['author', 'tags'])
->firstOrFail();
}
public function getPublished(int $perPage = 20): LengthAwarePaginator
{
return $this->model
->published()
->with('author')
->orderByDesc('published_at')
->paginate($perPage);
}
public function create(array $data): Article
{
return $this->model->create($data);
}
public function update(Article $article, array $data): Article
{
$article->update($data);
return $article->fresh();
}
public function delete(Article $article): bool
{
return $article->delete();
}
}
// Binding in the ServiceProvider
$this->app->bind(
ArticleRepositoryInterface::class,
EloquentArticleRepository::class
);Repository Pattern มีประโยชน์สำหรับแอปพลิเคชันที่ซับซ้อน แต่อาจมากเกินไปสำหรับโปรเจกต์ง่ายๆ ควรประเมินอัตราส่วนต้นทุน/ผลประโยชน์
คำถามที่ 19: วิธีจัดการ Transactions และ Concurrency ใน Laravel?
Transactions รับประกันความถูกต้องของข้อมูลระหว่างการดำเนินการหลายรายการ Laravel ทำให้การจัดการง่ายขึ้น
use Illuminate\Support\Facades\DB;
class OrderService
{
public function processOrder(Cart $cart, User $user): Order
{
// Transaction with closure: automatic rollback on exception
return DB::transaction(function () use ($cart, $user) {
// Create the order
$order = Order::create([
'user_id' => $user->id,
'total' => $cart->total(),
'status' => 'pending',
]);
// Create order items
foreach ($cart->items as $item) {
$order->items()->create([
'product_id' => $item->product_id,
'quantity' => $item->quantity,
'price' => $item->product->price,
]);
// Decrement stock with pessimistic locking
$product = Product::lockForUpdate()->find($item->product_id);
if ($product->stock < $item->quantity) {
throw new InsufficientStockException($product);
}
$product->decrement('stock', $item->quantity);
}
// Clear the cart
$cart->clear();
return $order;
}, attempts: 3); // 3 attempts in case of deadlock
}
public function updateOrderStatus(Order $order, string $status): void
{
// Optimistic locking with version/timestamp
$updated = DB::table('orders')
->where('id', $order->id)
->where('updated_at', $order->updated_at) // Version check
->update([
'status' => $status,
'updated_at' => now(),
]);
if ($updated === 0) {
throw new ConcurrencyException('The order was modified in the meantime');
}
}
}lockForUpdate() ป้องกันการอ่านพร้อมกันระหว่าง transaction ใช้อย่างระมัดระวังเพื่อหลีกเลี่ยง deadlock
คำถามที่ 20: วิธีใช้งาน Caching อย่างมีประสิทธิภาพใน Laravel?
Caching ช่วยเพิ่มประสิทธิภาพอย่างมากโดยหลีกเลี่ยง query ที่ซ้ำซ้อน
// Caching strategies
use Illuminate\Support\Facades\Cache;
class ArticleService
{
public function getPopularArticles(): Collection
{
// Cache-Aside: check cache, otherwise load and store
return Cache::remember('articles:popular', 3600, function () {
return Article::published()
->popular()
->with('author')
->limit(10)
->get();
});
}
public function getArticle(string $slug): Article
{
// Cache by dynamic key
return Cache::remember("article:{$slug}", 1800, function () use ($slug) {
return Article::where('slug', $slug)
->with(['author', 'comments.user'])
->firstOrFail();
});
}
public function updateArticle(Article $article, array $data): Article
{
$article->update($data);
// Cache invalidation after modification
Cache::forget("article:{$article->slug}");
Cache::forget('articles:popular');
// Tags for grouped invalidation (Redis only)
Cache::tags(['articles', "user:{$article->user_id}"])->flush();
return $article;
}
public function getArticleWithLock(int $id): Article
{
// Atomic lock to avoid cache stampede
return Cache::lock("article-lock:{$id}", 10)->block(5, function () use ($id) {
return Cache::remember("article:{$id}", 3600, fn() => Article::findOrFail($id));
});
}
}'stores' => [
'redis' => [
'driver' => 'redis',
'connection' => 'cache',
'lock_connection' => 'default',
],
],ใช้ cache tags สำหรับการยกเลิกแบบกลุ่มอย่างมีประสิทธิภาพ ระวัง cache stampede เมื่อหมดอายุพร้อมกัน
ประสิทธิภาพและการปรับแต่ง
คำถามที่ 21: วิธีปรับปรุงประสิทธิภาพแอปพลิเคชัน Laravel?
การปรับแต่งครอบคลุมหลายระดับ: query, cache, การกำหนดค่า และโครงสร้างพื้นฐาน
$articles = Article::query()
->select(['id', 'title', 'slug', 'published_at', 'user_id']) // Specific columns
->with(['author:id,name,avatar']) // Selective eager loading
->withCount('comments') // Count in one query
->published()
->latest('published_at')
->cursorPaginate(20); // Cursor pagination (more performant)
// 2. Chunking for mass operations
Article::query()
->where('status', 'published')
->chunkById(1000, function ($articles) {
foreach ($articles as $article) {
// Processing in batches of 1000
ProcessArticle::dispatch($article);
}
});
// 3. Mass update without models
Article::where('published_at', '<', now()->subYear())
->update(['status' => 'archived']); // Single SQL query# Optimization commands for production
php artisan config:cache # Cache configuration
php artisan route:cache # Cache routes
php artisan view:cache # Compile Blade views
php artisan event:cache # Cache event mappings
php artisan optimize # Run all optimizations
# Optimized autoloader
composer install --optimize-autoloader --no-devการปรับแต่งเหล่านี้สามารถลดเวลา boot ได้ถึง 50% หรือมากกว่าในสภาพแวดล้อม production
คำถามที่ 22: วิธี Debug และ Profile แอปพลิเคชัน Laravel?
Laravel มีเครื่องมือหลายอย่างสำหรับระบุปัญหาด้านประสิทธิภาพและ bug
// Laravel Telescope for development debugging
// Captures requests, jobs, exceptions, etc.
// Query debugging
DB::enableQueryLog();
$articles = Article::with('author')->get();
$queries = DB::getQueryLog();
dump($queries); // Shows all SQL queries
// Debug bar integrated with Blade
@dump($variable) // Display and continue
@dd($variable) // Dump and die
// Structured logging
use Illuminate\Support\Facades\Log;
Log::channel('slack')->critical('Payment failed', [
'user_id' => $user->id,
'amount' => $amount,
'error' => $exception->getMessage(),
'trace' => $exception->getTraceAsString(),
]);
// Context logging
Log::withContext(['request_id' => request()->id()]);
Log::info('Processing order', ['order_id' => $order->id]);'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['daily', 'slack'],
'ignore_exceptions' => false,
],
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/laravel.log'),
'level' => 'debug',
'days' => 14,
],
],ใช้ Telescope ในขั้นตอนการพัฒนาและ APM (New Relic, Datadog) ใน production สำหรับการตรวจสอบอย่างต่อเนื่อง
Deployment และ Production
คำถามที่ 23: วิธีจัดการ Migrations ใน Production โดยไม่มี Downtime?
Migrations ใน production ต้องได้รับความใส่ใจเป็นพิเศษเพื่อหลีกเลี่ยงการหยุดชะงักของบริการ
// Safe migration: add a nullable column first
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
// Step 1: Add nullable column
$table->string('role')->nullable()->after('email');
});
}
// Step 2: Data migration (separate job)
// php artisan tinker
// User::whereNull('role')->update(['role' => 'user']);
// Step 3: Second migration for constraint
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('role')->nullable(false)->default('user')->change();
});
}// For column deletions (3 deployments)
// Deployment 1: Stop using the column in code
// Deployment 2: Delete the column
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('deprecated_field');
});
// Migration with timeout for large tables
public function up(): void
{
DB::statement('SET lock_timeout TO \'5s\'');
Schema::table('large_table', function (Blueprint $table) {
$table->index('status'); // Concurrent index if PostgreSQL
});
}กลยุทธ์ "expand-contract" ช่วยให้เพิ่มคอลัมน์โดยไม่มี downtime: เพิ่ม nullable -> migrate ข้อมูล -> ทำให้เป็น non-nullable
คำถามที่ 24: วิธีตั้งค่า Laravel สำหรับ High Availability?
สถาปัตยกรรม high availability ต้องการการแยกส่วนประกอบ stateless และการจัดการ state ที่ใช้ร่วมกัน
'driver' => env('SESSION_DRIVER', 'redis'),
'connection' => 'session',
// config/cache.php - Shared cache
'default' => env('CACHE_DRIVER', 'redis'),
// config/queue.php - Redis queues for distribution
'default' => env('QUEUE_CONNECTION', 'redis'),
'connections' => [
'redis' => [
'driver' => 'redis',
'connection' => 'default',
'queue' => 'default',
'retry_after' => 90,
'block_for' => null,
],
],
// config/filesystems.php - S3 storage for files
'disks' => [
's3' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET'),
],
],// Health check endpoint for load balancer
Route::get('/health', function () {
try {
DB::connection()->getPdo();
Cache::store('redis')->ping();
return response()->json(['status' => 'healthy']);
} catch (\Exception $e) {
return response()->json(['status' => 'unhealthy'], 503);
}
});ทุก instance ต้องเป็น stateless Session, cache และ queue ควรใช้ Redis หรือ shared storage
คำถามที่ 25: แนวทางปฏิบัติที่ดีที่สุดสำหรับ Deployment Laravel?
การ deploy ที่เชื่อถือได้รวมการทำงานอัตโนมัติ การตรวจสอบ และการ rollback ที่ง่ายดาย
# deploy.sh - Typical deployment script
#!/bin/bash
set -e
echo "Pulling latest code..."
git pull origin main
echo "Installing dependencies..."
composer install --no-dev --optimize-autoloader
echo "Running migrations..."
php artisan migrate --force
echo "Caching configuration..."
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache
echo "Restarting queue workers..."
php artisan queue:restart
echo "Clearing old cache..."
php artisan cache:clear
echo "Deployment complete!"APP_ENV=production
APP_DEBUG=false
APP_KEY=base64:... # Generated with php artisan key:generate
LOG_CHANNEL=stack
LOG_LEVEL=warning
// Never expose credentials in plain text
// Use secret managers (Vault, AWS Secrets Manager)Checklist deployment:
- php artisan config:cache - แคชการกำหนดค่า
- php artisan route:cache - แคช routes
- php artisan view:cache - คอมไพล์ views
- composer install --no-dev - Dependencies สำหรับ production
- ทดสอบอัตโนมัติก่อน deploy
- ตั้งค่า health checks แล้ว
- ระบบ monitoring และ alerting พร้อมใช้งาน
สรุป
25 คำถามเหล่านี้ครอบคลุมสาระสำคัญของการสัมภาษณ์งาน Laravel และ PHP ตั้งแต่พื้นฐาน Service Container ไปจนถึงรูปแบบการ deploy ระบบ production
Checklist การเตรียมตัว:
- Service Container และ dependency injection
- Eloquent ORM: relationships, scopes, eager loading
- Middleware, routing และความปลอดภัย
- Queues, jobs และ events แบบ asynchronous
- Testing: Feature tests, Unit tests, Factories
- Patterns ขั้นสูง: Repository, Transactions, Caching
- Deployment: migrations, การปรับแต่ง, high availability
แต่ละคำถามควรศึกษาเพิ่มเติมจากเอกสารทางการของ Laravel ผู้สัมภาษณ์ให้ความสำคัญกับผู้สมัครที่เข้าใจรายละเอียดปลีกย่อยของ framework และสามารถอธิบายเหตุผลของการเลือกทางเทคนิคได้
เริ่มฝึกซ้อมเลย!
ทดสอบความรู้ของคุณด้วยตัวจำลองสัมภาษณ์และแบบทดสอบเทคนิคครับ
แท็ก
แชร์
บทความที่เกี่ยวข้อง

Laravel 11: สร้างแอปพลิเคชันสมบูรณ์ตั้งแต่เริ่มต้น
คู่มือครบวงจรสำหรับสร้างแอปพลิเคชัน Laravel 11 พร้อมระบบยืนยันตัวตน, REST API, Eloquent ORM และการ Deploy บทเรียนเชิงปฏิบัติสำหรับนักพัฒนาระดับเริ่มต้นถึงกลาง

คำถามสัมภาษณ์งาน Django และ Python: 25 คำถามยอดนิยมประจำปี 2026
25 คำถามสัมภาษณ์งาน Django และ Python ที่พบบ่อยที่สุด ORM, views, middleware, DRF, signals และการปรับแต่งประสิทธิภาพ พร้อมคำตอบละเอียดและตัวอย่างโค้ด

คำถามสัมภาษณ์ Rust: คู่มือฉบับสมบูรณ์ 2026
25 คำถามสัมภาษณ์ Rust ที่พบบ่อยที่สุด Ownership, borrowing, lifetime, trait, async และ concurrency พร้อมคำตอบละเอียดและตัวอย่างโค้ด