Chapitre 22
22 — Simulations d'Entretiens Techniques
22 — Simulations d'Entretiens Techniques
Cours 22 — Interviews PHP
22.1 Questions Comportementales (STAR Method)
Méthode STAR
S — Situation : Contexte dans lequel vous étiez
T — Task : Tâche que vous deviez accomplir
A — Action : Actions que vous avez entreprises
R — Result : Résultats obtenus (chiffrés si possible)
30 Questions Comportementales
Question 1 : Parlez-moi d'un projet complexe que vous avez mené.
S : Projet de migration d'une application legacy PHP 5.6 vers PHP 8.2 avec 500k+ utilisateurs. T : Moderniser l'architecture sans downtime, remplacer le code procédural par OOP. A : J'ai proposé une migration progressive par modules, mis en place PHPStan niveau max, ajouté des tests de régression (Pest), créé un ADR pour documenter les décisions. R : Migration terminée en 4 mois, 0 incident, performance améliorée de 40%, dette technique réduite de 60%.
Question 2 : Décrivez une situation où vous avez géré un conflit technique.
S : Désaccord avec un collègue sur l'utilisation de Livewire vs Inertia.js pour un nouveau module. T : Choisir la bonne technologie et maintenir la cohésion d'équipe. A : J'ai organisé un coding dojo de 2 jours où nous avons prototype les deux approches, puis comparé : performance, DX, maintenabilité. R : L'équipe a choisi Livewire à l'unanimité, le projet a livré 2 semaines avant la date prévue.
Question 3 : Avez-vous déjà dû livrer sous une pression extrême ?
S : Bug critique en production un vendredi 18h — les paiements étaient doublés. T : Corriger le bug et rembourser les clients impactés. A : J'ai identifié le bug (race condition sur les webhooks Stripe), appliqué un hotfix avec queue déduplication, écrit un script de remboursement automatisé, ajouté un test de régression. R : Bug corrigé en 45 minutes, 120 clients remboursés automatiquement, 0 perte pour l'entreprise.
Question 4 : Comment gérez-vous l'échec ?
S : J'ai déployé une mise à jour qui a cassé l'API pour 10% des utilisateurs. T : Résoudre l'incident et éviter qu'il ne se reproduise. A : J'ai immédiatement revert le déploiement, communiqué sur le statut, analysé la cause (manque de tests d'intégration), mis en place une CI avec tests automatisés. R : Plus aucun déploiement cassé depuis. J'ai aussi présenté un REX lors du rétrospective d'équipe.
Question 5 : Décrivez comment vous avez mentoré un développeur junior.
S : Nouveau développeur junior dans l'équipe, bon en PHP mais découvrait Laravel. T : L'aider à monter en compétence rapidement. A : Mise en place de pair programming 2x/semaine, revue de code systématique, création de guides internes. R : Après 3 mois, le junior livrait en autonomie. Il a été promu confirmé à 6 mois.
Question 6 : Comment priorisez-vous la dette technique ?
S : Projet avec 40% de dette technique, tests lents (45 min), code non typé. T : Réduire la dette sans bloquer les features. A : Création d'un backlog dette technique, règle des 20% (20% de chaque sprint pour la dette), prioritisation par risque business. R : Dette réduite de 60% en 6 mois, tests passés à 5 minutes, PHPStan niveau max.
Question 7 : Parlez-moi d'une initiative d'amélioration continue que vous avez menée.
S : L'équipe n'avait pas de standard de code. T : Implémenter des outils de qualité automatisés. A : J'ai proposé et configuré : Pint (PSR-12), PHPStan niveau 9, Pint dans pre-commit hook, CI avec tests. R : Réduction de 80% des erreurs de style en PR, reviews plus rapides.
Question 8 : Comment gérez-vous le désaccord avec un supérieur ?
S : Mon CTO voulait utiliser MongoDB, mais les données étaient résolument relationnelles. T : Proposer une meilleure solution sans créer de conflit. A : J'ai préparé une comparaison (MongoDB vs PostgreSQL) avec des benchmarks sur nos données réelles, montré les avantages coûts/maintenance. R : PostgreSQL adopté, économie de 30% sur l'infrastructure.
Question 9 : Racontez un projet où vous avez dû apprendre une nouvelle technologie rapidement.
S : Projet client nécessitant Livewire que je ne connaissais pas. T : Livrer un module Livewire en 2 semaines. A : Formation intensive via Laracasts (2 jours), POC, pair programming avec un expert. R : Module livré dans les temps, utilisé comme référence pour les projets suivants.
Question 10 : Comment assurez-vous la qualité du code en équipe ?
S : Équipe en croissance rapide, qualité hétérogène. T : Standardiser la qualité. A : Mise en place de PR template, checklist qualité, code review guidelines, CI avec analyse statique, mob programming sur les sujets complexes. R : Qualité homogène, vélocité augmentée de 20%.
Question 11 : Décrivez un moment où vous avez dû refactorer un code legacy.
S : Classe de 3000 lignes avec 15 responsabilités. T : Refactorer sans casser le comportement. A : Écriture de tests de caractérisation, extraction progressive par responsabilité, Strangler Fig Pattern. R : Classe divisée en 8 classes, tests couvrant 95% du code.
Question 12 : Comment gérez-vous le stress en période de crise ?
S : Crash base de données un lundi matin. T : Rétablir le service. A : J'ai coordonné l'équipe (1 personne aux logs, 1 à la restauration, 1 à la communication), communiqué toutes les 15 minutes. R : Service rétabli en 2h, client informé en continu, plan d'action correctif mis en place.
Question 13 : Racontez une situation où vous avez dû faire un compromis technique.
S : Deadline serrée, tests E2E trop longs. T : Livrer à temps sans sacrifier la qualité. A : Compromis : réduire les tests E2E aux parcours critiques, augmenter les tests unitaires, planifier les tests E2E manquants après la release. R : Livraison à l'heure, tests E2E critiques passés, backlog pour les tests restants.
Question 14 : Comment avez-vous contribué à l'agilité de l'équipe ?
S : Daily meetings trop longs (45 min), rétrospectives inefficaces. T : Améliorer les rituels agiles. A : Proposition de timeboxing (15 min daily), format de rétro "Start/Stop/Continue", utilisation de outils visuels. R : Daily réduits à 15 min, rétrospectives plus actionnables, vélocité +15%.
Question 15 : Décrivez une situation où vous avez fait preuve de leadership technique.
S : Projet bloqué par des choix architecturaux. T : Débloquer l'équipe. A : Organisation d'un Architecture Decision Record (ADR) workshop, facilitation de la décision collective. R : Décision prise en 2 jours, implémentation démarrée, ADR documenté pour référence future.
Question 16 : Parlez-moi d'un bug complexe que vous avez résolu.
S : Bug intermittent en production, impossible à reproduire en dev. T : Trouver la cause racine. A : Ajout de logging contextuel, analyse des patterns (heure, utilisateur, données), instrumentation avec Blackfire. R : Bug identifié : race condition sur cache Redis. Solution : atomic locks.
Question 17 : Comment gérez-vous le feedback négatif ?
S : Review sévère sur une PR. T : Améliorer la qualité de mon code. A : J'ai remercié le reviewer, listé les points, créé une checklist personnelle, demandé une session pair programming. R : Mes PR suivantes ont été approuvées plus rapidement, j'ai adopté ces standards dans mon workflow.
Question 18 : Racontez un projet réussi grâce à la collaboration.
S : Construction d'une API publique avec 3 équipes. T : Coordination inter-équipes. A : Mise en place de API guild, spécifications partagées (OpenAPI), contrat d'interface, démos bi-hebdomadaires. R : API livrée à temps, 0 breaking change après la release.
Question 19 : Comment restez-vous à jour techniquement ?
- Veille quotidienne (15 min) : newsletters, Reddit, Twitter
- Conférences : 2/an (Laracon, SymfonyCon)
- Open source : contribution à Spatie packages, Laravel core
- Formation : Laracasts, SymfonyCasts, livres techniques
- Projets personnels : side projects pour expérimenter
Question 20 : Parlez-moi d'un échec technique et ce que vous en avez appris.
S : Indexation ElasticSearch sans versioning. T : Corriger les données corrompues. A : Ajout de versioning, migration, tests de régression. R : Leçon : toujours versionner les schémas de données. Mis en place dans toutes les équipes.
Question 21 : Comment abordez-vous l'estimation d'un projet ?
S : Projet complexe, fortes incertitudes. T : Estimer réalistement. A : Découpage en user stories, planning poker, identification des risques, marge de 20%. R : Estimation à 3 semaines → livré en 3.5 semaines (dans la marge).
Question 22 : Décrivez une situation où vous avez dû dire non.
S : Feature urgente demandée par le CEO sans analyse d'impact. T : Éviter une décision précipitée. A : Proposition d'une analyse d'impact rapide (2h) avant de dire oui, identification des risques. R : Feature reportée au sprint suivant, développement plus simple que prévu grâce à l'analyse.
Question 23 : Comment gérez-vous plusieurs priorités concurrentes ?
S : 3 features bloquantes en parallèle. T : Prioriser sans mécontenter les stakeholders. A : Matrice impact/effort, communication transparente, négociation des délais, focus sur une priorité à la fois. R : Priorisation acceptée par tous, livraison sans burnout.
Question 24 : Parlez-moi d'une innovation que vous avez apportée.
S : Processus de déploiement manuel long et risqué. T : Automatiser. A : Mise en place de CI/CD avec GitHub Actions + Deployer, déploiements blue/green. R : Déploiement passé de 1h à 5 minutes, zéro downtime.
Question 25 : Comment intégrez-vous la sécurité dans votre développement ?
- Security review dans la définition of done
- OWASP Top 10 checklist en PR
- Tests de sécurité automatisés (SQL injection, XSS)
- Revue de dépendances avec
composer audit- Sessions de sécurité avec l'équipe
Question 26 : Décrivez votre gestion du temps au quotidien.
- Pomodoro (25 min focus / 5 min break)
- Matin : tâches complexes (code, architecture)
- Midi : veille technique
- Après-midi : meetings, reviews, documentation
- Blocage de 2h sans meeting
Question 27 : Racontez un projet où vous avez dépassé les attentes.
S : Projet de migration cloud. T : Migrer vers AWS. A : Au-delà de la migration, j'ai optimisé les coûts (réserved instances, auto-scaling), amélioré la sécurité (WAF, Secrets Manager). R : Coûts réduits de 40% vs prévision, sécurité améliorée, documentation complète.
Question 28 : Comment gérez-vous le code des autres ?
- Bienveillance : "improve not blame"
- Suggestions avec explications (pourquoi)
- PR reviews : équilibrer qualité et vélocité
- Standards clairs (checklist)
- Mentorat si nécessaire
Question 29 : Parlez-moi de votre expérience avec la revue de code.
- Approche : revue fonctionnelle + technique + style
- 30 min max par PR (> 400 lignes → décomposer)
- Suggestions de code plutôt qu'ordres
- Approbation conditionnelle si mineur
- Objectif : partager la connaissance
Question 30 : Où vous voyez-vous dans 5 ans ?
- Lead Developer / Architecte PHP
- Contribution open-source significative (Laravel core, Spatie)
- Speaker en conférence
- Mentorat de développeurs juniors
- Veille continue sur l'évolution du langage
22.2 Questions Techniques — PHP 8 Features
PHP 8.0
<?php
// Q1 : Qu'est-ce que les named arguments ?
// R : Permettent de passer des arguments par nom
function createUser(
string $name,
string $email,
bool $isAdmin = false,
?string $phone = null,
): User {}
createUser(
name: 'John',
email: 'john@example.com',
isAdmin: true,
);
// Q2 : Qu'est-ce que les attributes ?
// R : Métadonnées structurées pour classes, méthodes, propriétés
#[Route('/api/users', methods: ['GET'])]
class UserController {}
// Q3 : Match expression vs switch
$status = match ($code) {
200, 201 => 'success',
400, 422 => 'client_error',
500 => 'server_error',
default => 'unknown',
};
// Q4 : Nullsafe operator
$city = $user?->address?->city;
// Q5 : Constructor promotion
class User
{
public function __construct(
readonly public string $name,
readonly public string $email,
) {}
}
// Q6 : Union types
function process(int|string $input): int|float {}
// Q7 : Stringable interface
class User implements \Stringable
{
public function __toString(): string
{
return $this->name;
}
}
PHP 8.1
<?php
// Q8 : Enums
enum Status: string
{
case Pending = 'pending';
case Approved = 'approved';
case Rejected = 'rejected';
public function label(): string
{
return match ($this) {
self::Pending => 'En attente',
self::Approved => 'Approuvé',
self::Rejected => 'Rejeté',
};
}
}
// Q9 : Readonly properties (avant PHP 8.2)
class Configuration
{
public function __construct(
readonly public array $values,
) {}
}
// Q10 : Fiber (async)
$fiber = new Fiber(function (): void {
$result = Fiber::suspend('starting');
// traitement
});
$value = $fiber->start();
// Q11 : Array unpacking avec string keys
$array1 = ['a' => 1];
$array2 = ['b' => 2, ...$array1];
// Q12 : First-class callable syntax
$callback = strlen(...);
// Q13 : Pure intersection types
function process(Countable&Iterator $collection): void {}
PHP 8.2
<?php
// Q14 : Readonly classes
readonly class Configuration
{
public function __construct(
public string $apiKey,
public string $apiUrl,
) {}
}
// Q15 : DNF types
function process((Countable&ArrayAccess)|null $collection): void {}
// Q16 : true type
function isAdmin(): true
{
return true;
}
// Q17 : Constants in traits
trait HttpStatus
{
final const OK = 200;
final const CREATED = 201;
}
PHP 8.3
<?php
// Q18 : json_validate
$valid = json_validate('{"name": "John"}'); // true
// Q19 : Deep cloning readonly
readonly class Config
{
public function __construct(
public User $user,
) {}
}
// Q20 : Randomizer
$random = new \Random\Randomizer();
$token = bin2hex($random->getBytes(32));
PHP 8.4
<?php
// Q21 : Property hooks
class User
{
public string $name {
set => ucfirst(strtolower($value));
}
public string $email {
set (string $value) {
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException('Email invalide');
}
$this->email = $value;
}
get => strtolower($this->email);
}
}
// Q22 : Asymmetric visibility
class Post
{
public private(set) string $title;
public function __construct(string $title)
{
$this->title = $title;
}
}
// Q23 : Lazy objects
$initializer = function (User $user): User {
$user->name = 'John';
$user->email = 'john@example.com';
return $user;
};
$user = \Symfony\Component\VarExporter\LazyGhostTrait::createLazyGhost(
User::class,
$initializer,
);
22.3 Questions Techniques — OOP & SOLID
SOLID
<?php
// Q24 : Single Responsibility Principle
// ❌ Une classe qui fait tout
class Invoice
{
public function calculateTotal(): void {}
public function saveToDatabase(): void {}
public function sendEmail(): void {}
public function generatePdf(): void {}
}
// ✅ Classes séparées
class Invoice {}
class InvoiceRepository {}
class InvoiceMailer {}
class InvoicePdfGenerator {}
// Q25 : Open-Closed Principle
// ❌ Modification pour ajouter un type
class PaymentProcessor
{
public function process(string $type): void
{
if ($type === 'stripe') { /* ... */ }
elseif ($type === 'paypal') { /* ... */ }
}
}
// ✅ Extension sans modification
interface PaymentMethod
{
public function process(): void;
}
class StripePayment implements PaymentMethod
{
public function process(): void {}
}
class PaypalPayment implements PaymentMethod
{
public function process(): void {}
}
class PaymentProcessor
{
public function __construct(
private PaymentMethod $method,
) {}
public function process(): void
{
$this->method->process();
}
}
// Q26 : Liskov Substitution
abstract class Bird
{
abstract public function eat(): void;
}
abstract class FlyingBird extends Bird
{
abstract public function fly(): void;
}
class Sparrow extends FlyingBird
{
public function eat(): void {}
public function fly(): void {}
}
class Penguin extends Bird
{
public function eat(): void {}
// Penguin ne vole pas → pas de fly()
}
// Q27 : Interface Segregation
// ❌ Interface trop grosse
interface Worker
{
public function work(): void;
public function eat(): void;
public function sleep(): void;
}
// ✅ Interfaces spécifiques
interface Workable
{
public function work(): void;
}
interface Eatable
{
public function eat(): void;
}
interface Sleepable
{
public function sleep(): void;
}
class Human implements Workable, Eatable, Sleepable {}
class Robot implements Workable {}
// Q28 : Dependency Inversion
// ❌ Dépendance concrète
class OrderService
{
private StripeGateway $gateway;
public function __construct()
{
$this->gateway = new StripeGateway();
}
}
// ✅ Dépendance abstraite
class OrderService
{
public function __construct(
private PaymentGateway $gateway,
) {}
}
Design Patterns
<?php
// Q29 : Factory Pattern
interface Logger
{
public function log(string $message): void;
}
class FileLogger implements Logger
{
public function log(string $message): void
{
file_put_contents('app.log', $message . PHP_EOL, FILE_APPEND);
}
}
class CloudLogger implements Logger
{
public function log(string $message): void
{
// Envoyer vers CloudWatch
}
}
class LoggerFactory
{
public static function create(string $type): Logger
{
return match ($type) {
'file' => new FileLogger(),
'cloud' => new CloudLogger(),
default => throw new \InvalidArgumentException("Type $type inconnu"),
};
}
}
// Q30 : Repository Pattern
interface UserRepositoryInterface
{
public function find(int $id): ?User;
public function findByEmail(string $email): ?User;
public function save(User $user): void;
public function delete(User $user): void;
}
class EloquentUserRepository implements UserRepositoryInterface
{
public function find(int $id): ?User
{
return User::find($id);
}
}
22.4 Questions Techniques — Laravel Internals
Service Container
<?php
// Q31 : Comment fonctionne le Service Container ?
// R : Conteneur d'injection de dépendances qui gère l'instanciation et la résolution
// Binding simple
app()->bind('PaymentGateway', function () {
return new StripeGateway(config('services.stripe.secret'));
});
// Binding singleton
app()->singleton('cache', function () {
return new CacheManager();
});
// Binding d'interface
app()->bind(UserRepositoryInterface::class, EloquentUserRepository::class);
// Résolution
$gateway = app('PaymentGateway');
$gateway = resolve('PaymentGateway');
$gateway = app()->make('PaymentGateway');
// Auto-resolution
class UserController
{
public function __construct(
private UserRepositoryInterface $users, // Résolu automatiquement
) {}
}
// Contextual binding
app()->when(PhotoController::class)
->needs(Filesystem::class)
->give(function () {
return Storage::disk('s3');
});
Service Providers
<?php
// Q32 : Cycle de vie des Service Providers
// R : Enregistrement → Boot
class AppServiceProvider extends ServiceProvider
{
// 1. register() : bindings dans le container (ne pas utiliser de services résolus)
public function register(): void
{
$this->app->bind(PaymentGateway::class, StripeGateway::class);
$this->app->singleton(Logger::class, CloudLogger::class);
}
// 2. boot() : après que tous les providers sont enregistrés
public function boot(): void
{
// Services résolus disponibles ici
$gateway = $this->app->make(PaymentGateway::class);
// Publisher de config
$this->publishes([
__DIR__.'/../config/monpackage.php' => config_path('monpackage.php'),
], 'config');
}
}
// Ordre d'exécution :
// 1. Tous les register()
// 2. Tous les boot()
// 3. Les providers sont cached en production
Facades
<?php
// Q33 : Comment fonctionnent les Facades ?
// R : Proxy statique vers une instance résolue du container
// Définition
class Cache extends Facade
{
protected static function getFacadeAccessor(): string
{
return 'cache'; // Clé dans le container
}
}
// Utilisation
Cache::get('key'); // Équivalent à : app('cache')->get('key')
Cache::put('key', 'value', 3600);
// Résolution réelle
// Cache::get() → __callStatic → app('cache')->get()
// Avantages : syntaxe concise, testable (Facade::shouldReceive)
// Inconvénients :耦合 statique, moins explicite
Eloquent
<?php
// Q34 : Expliquez l'Eloquent ORM
// R : Active Record implementation — chaque modèle = une table
class User extends Model
{
// Table associée (déduite : 'users' pour User)
protected $table = 'users';
// Relations
public function posts(): HasMany
{
return $this->hasMany(Post::class);
}
public function roles(): BelongsToMany
{
return $this->belongsToMany(Role::class)
->withTimestamps();
}
// Scopes
public function scopeActive(Builder $query): Builder
{
return $query->where('active', true);
}
// Accessors / Mutators
protected function fullName(): Attribute
{
return Attribute::make(
get: fn ($value) => "{$this->first_name} {$this->last_name}",
);
}
}
// Utilisation
$activeUsers = User::active()->get();
$user = User::with('posts.comments')->find(1);
Queues
<?php
// Q35 : Comment fonctionne le système de queues ?
// R : Délégation de tâches asynchrones
// Job
class SendWelcomeEmail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(
private User $user,
) {}
public function handle(): void
{
Mail::to($this->user)->send(new WelcomeMail($this->user));
}
public function failed(\Throwable $e): void
{
Log::error('Welcome email failed', [
'user' => $this->user->id,
'error' => $e->getMessage(),
]);
}
}
// Dispatch
SendWelcomeEmail::dispatch($user)->onQueue('emails');
dispatch(new SendWelcomeEmail($user));
// Configuration
// config/queue.php : 'default' => env('QUEUE_CONNECTION', 'redis')
// Horizon pour monitoring
22.5 Questions Techniques — Performance
<?php
// Q36 : Optimisation des requêtes Eloquent
// ❌ N+1 problem
$posts = Post::all();
foreach ($posts as $post) {
echo $post->author->name; // N queries !
}
// ✅ Eager loading
$posts = Post::with('author')->get();
// ✅ Lazy eager loading
$posts = Post::all();
$posts->load('author');
// ✅ Subqueries
$users = User::addSelect([
'last_post_at' => Post::select('created_at')
->whereColumn('user_id', 'users.id')
->latest()
->limit(1),
])->get();
// Q37 : Cache
// Mise en cache des requêtes fréquentes
$users = Cache::remember('active_users', 3600, function () {
return User::active()->with('posts')->get();
});
// Cache tags (Redis/Memcached)
Cache::tags(['users', 'stats'])->put('active_count', 150, 3600);
// Q38 : Indexation
Schema::table('posts', function (Blueprint $table) {
$table->index('user_id');
$table->index(['published_at', 'status']);
$table->fullText('content');
// Éviter les indexes composites inutiles
});
// Q39 : Chunk pour gros volumes
User::chunk(1000, function ($users) {
foreach ($users as $user) {
// Traitement par lots
}
});
// lazy() pour économiser la mémoire
foreach (User::lazy(500) as $user) {
// Traitement sans tout charger
}
// Q40 : JIT PHP 8
// Activation dans php.ini
opcache.jit = tracing
opcache.jit_buffer_size = 256M
// Benchmarks
// PHP 7.4 → PHP 8.4 : jusqu'à 3x plus rapide sur CPU-bound
// Application web : 20-40% d'amélioration
22.6 Questions Techniques — Sécurité
<?php
// Q41 : SQL Injection
// ❌ Vulnérable
$users = DB::select("SELECT * FROM users WHERE id = $id");
// ✅ Safe
$users = DB::select('SELECT * FROM users WHERE id = ?', [$id]);
// ✅ Eloquent (safe)
User::where('id', $id)->get();
// Q42 : XSS
// ❌ Vulnérable
<div>{{ $userInput }}</div>
// → <script>alert('xss')</script>
// ✅ Blade échappe automatiquement
<div>{{ $userInput }}</div>
// ❌ Raw non échappé
{!! $userInput !!}
// Q43 : CSRF
// Protection automatique via @csrf
<form method="POST">
@csrf
</form>
// Exception pour API
Route::get('/api/users', fn() => ...)->withoutMiddleware([VerifyCsrfToken::class]);
// Q44 : Mass assignment
class User extends Model
{
// ✅ Whitelist
protected $fillable = ['name', 'email'];
// ❌ Blacklist (moins sûr)
protected $guarded = ['is_admin'];
}
22.7 System Design
Concevoir une API REST
**1. Définir les ressources**
- /api/users — CRUD utilisateurs
- /api/posts — CRUD articles
- /api/posts/{post}/comments — CRUD commentaires
**2. Choix techniques**
- Laravel + Sanctum (authentication)
- API Resources (transformation)
- Rate limiting : 100 req/min
- Pagination : cursor-based
- Versioning : Accept header (application/vnd.api.v1+json)
**3. Patterns**
- Repository pattern
- Action classes
- Form requests
- API Resources
- Events pour actions secondaires
**4. Sécurité**
- Sanctum tokens
- Scopes (posts:read, posts:write)
- Throttling
- Validation
- SQL injection protection
**5. Performance**
- Eager loading
- Sparse fieldsets
- Include (relations)
- Cache
- Pagination
- Indexation base de données
Concevoir un CMS modulaire
**1. Architecture**
- Modules : Pages, Posts, Media, Users, Settings
- Module system (Laravel modules : nwidart/laravel-modules)
- Event-driven : ModuleEvents
**2. Data Model**
- Page : id, title, slug, content, status, author_id
- Post : id, title, slug, excerpt, content, category_id, tags
- Media : id, name, path, type, size, user_id
- User : id, name, email, role_id
**3. Admin**
- Filament admin panel
- Permissions (spatie/laravel-permission)
- Activity log (spatie/laravel-activitylog)
- Media library (spatie/laravel-medialibrary)
**4. Cache**
- Page cache (full response)
- Fragment cache (menus, widgets)
- Query cache
- Static site generation
Concevoir une SaaS
**1. Architecture Multi-tenant**
- Database per tenant
- Schema per tenant
- Tenancy (stancl/tenancy)
**2. Plan management**
- Plans table : id, name, price, features (JSON)
- Subscription : Stripe/Billable
- Features : feature flags
**3. Scaling**
- Read replicas
- Queues (Horizon)
- Cache (Redis)
- CDN assets
- Auto-scaling
**4. Monitoring**
- Pulse pour métriques en temps réel
- Telescope pour débogage
- Sentry pour erreurs
- Horizon pour queues
- Logs centralisés
22.8 Coding Challenges
FizzBuzz en PHP 8
<?php
$fizzbuzz = fn (int $n): string => match (true) {
$n % 15 === 0 => 'FizzBuzz',
$n % 3 === 0 => 'Fizz',
$n % 5 === 0 => 'Buzz',
default => (string) $n,
};
// Avec itérateur
$results = array_map($fizzbuzz, range(1, 100));
echo implode("\n", $results);
Livewire Component
<?php
namespace App\Livewire;
use Livewire\Component;
use Livewire\WithPagination;
use App\Models\Post;
class PostList extends Component
{
use WithPagination;
public string $search = '';
public string $sortField = 'created_at';
public string $sortDirection = 'desc';
public int $perPage = 10;
protected $queryString = [
'search' => ['except' => ''],
'sortField' => ['except' => 'created_at'],
'sortDirection' => ['except' => 'desc'],
];
public function sortBy(string $field): void
{
if ($this->sortField === $field) {
$this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc';
} else {
$this->sortField = $field;
$this->sortDirection = 'asc';
}
}
public function updatingSearch(): void
{
$this->resetPage();
}
public function render()
{
$posts = Post::query()
->when($this->search, fn ($q) => $q
->where('title', 'like', "%{$this->search}%")
->orWhere('content', 'like', "%{$this->search}%")
)
->orderBy($this->sortField, $this->sortDirection)
->paginate($this->perPage);
return view('livewire.post-list', [
'posts' => $posts,
]);
}
}
<div>
<div class="mb-4">
<input wire:model.live.debounce.300ms="search" type="search"
placeholder="Rechercher..." class="form-input">
</div>
<table class="min-w-full">
<thead>
<tr>
<th wire:click="sortBy('title')" class="cursor-pointer">
Titre @include('includes.sort-indicator', ['field' => 'title'])
</th>
<th wire:click="sortBy('created_at')" class="cursor-pointer">
Date @include('includes.sort-indicator', ['field' => 'created_at'])
</th>
</tr>
</thead>
<tbody>
@foreach($posts as $post)
<tr>
<td>{{ $post->title }}</td>
<td>{{ $post->created_at->format('d/m/Y') }}</td>
</tr>
@endforeach
</tbody>
</table>
{{ $posts->links() }}
</div>
API Design Challenge
**Concevoir une API de réservation de rendez-vous médicaux**
Endpoints :
- GET /api/doctors — Liste des médecins (avec filtres : spécialité, ville)
- GET /api/doctors/{doctor} — Détail d'un médecin avec créneaux
- GET /api/doctors/{doctor}/slots — Créneaux disponibles
- POST /api/appointments — Créer un rendez-vous
- GET /api/appointments — Mes rendez-vous
- DELETE /api/appointments/{appointment} — Annuler
Contraintes :
- Créneaux de 30 minutes
- Plage horaire 9h-18h
- 1 patient par créneau
- Annulation jusqu'à 2h avant
- Notification par email
Modèle de données :
- Doctor : id, name, speciality, city, email
- Slot : id, doctor_id, start_time, end_time, is_available
- Appointment : id, doctor_id, patient_id, slot_id, status, created_at
Réponses :
- GET /api/doctors/{doctor}/slots?date=2025-06-15
{
"data": [
{"start": "09:00", "end": "09:30", "available": true},
{"start": "09:30", "end": "10:00", "available": false},
]
}
- POST /api/appointments
{
"doctor_id": 1,
"slot_id": 5,
"patient": {"name": "John", "email": "john@example.com"}
}