Laravel framework patterns for PHP applications including Eloquent ORM, migrations, routing, queues, and Blade templates. Use when building Laravel applications or working with Laravel projects.
Laravel framework patterns for PHP applications including Eloquent ORM, migrations, routing, queues, and Blade templates. Use when building Laravel applications or working with Laravel projects.
/plugin marketplace add FortiumPartners/ensemble/plugin install ensemble-development@ensembleThis skill inherits all available tools. When active, it can use any tool Claude has access to.
README.mdREFERENCE.mdVALIDATION.mdexamples/api_resource.example.phpexamples/artisan_patterns.example.phpexamples/eloquent_patterns.example.phpexamples/multi_tenant.example.phpexamples/passport_oauth.example.phpexamples/permission_acl.example.phpexamples/queue_workflow.example.phptemplates/command.template.phptemplates/command_consumer.template.phptemplates/command_scheduled.template.phptemplates/controller.template.phptemplates/custom_rule.template.phptemplates/event.template.phptemplates/factory.template.phptemplates/form_request.template.phptemplates/job.template.phptemplates/listener.template.phpLaravel framework patterns for modern PHP applications. For PHP language fundamentals, see the PHP skill. For advanced patterns, see REFERENCE.md.
app/
├── Console/Commands/ # Artisan commands
├── Http/
│ ├── Controllers/ # Request handlers
│ ├── Middleware/ # Request/response filters
│ └── Requests/ # Form validation
├── Jobs/ # Queueable jobs
├── Models/ # Eloquent models
├── Providers/ # Service providers
└── Services/ # Business logic
config/ # Configuration files
database/
├── factories/ # Model factories
├── migrations/ # Database migrations
└── seeders/ # Database seeders
routes/
├── api.php # API routes
└── web.php # Web routes
tests/
├── Feature/ # Integration tests
└── Unit/ # Unit tests
// Basic routes
Route::get('/users', [UserController::class, 'index']);
Route::post('/users', [UserController::class, 'store']);
// Resource routes (all CRUD)
Route::resource('posts', PostController::class);
Route::apiResource('comments', CommentController::class);
// Route groups with middleware
Route::prefix('api/v1')->middleware(['auth:sanctum'])->group(function () {
Route::get('/profile', [ProfileController::class, 'show']);
});
class UserController extends Controller
{
public function index()
{
return UserResource::collection(
User::with(['profile', 'roles'])->paginate(20)
);
}
public function store(StoreUserRequest $request)
{
return new UserResource(User::create($request->validated()));
}
public function show(User $user) // Route model binding
{
return new UserResource($user->load('profile'));
}
}
class Post extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = ['title', 'slug', 'content', 'status', 'author_id'];
protected $casts = ['status' => PostStatus::class, 'published_at' => 'datetime'];
protected $with = ['author']; // Always eager load
// Relationships
public function author(): BelongsTo
{
return $this->belongsTo(User::class, 'author_id');
}
public function tags(): BelongsToMany
{
return $this->belongsToMany(Tag::class)->withTimestamps();
}
// Scopes
public function scopePublished(Builder $query): void
{
$query->where('status', PostStatus::Published)
->where('published_at', '<=', now());
}
// Accessors (Laravel 9+)
protected function excerpt(): Attribute
{
return Attribute::make(
get: fn () => Str::limit(strip_tags($this->content), 150),
);
}
}
| Method | Relationship | Example |
|---|---|---|
hasOne | 1:1 | User has one Profile |
belongsTo | 1:1 inverse | Profile belongs to User |
hasMany | 1:n | User has many Posts |
belongsToMany | n:n | Post has many Tags |
morphMany | 1:n polymorphic | Post has many Comments |
Advanced: For hasOneThrough, hasManyThrough, polymorphic relationships, see REFERENCE.md
// Filtering
$posts = Post::where('status', 'published')
->whereHas('tags', fn($q) => $q->where('name', 'laravel'))
->with(['author', 'comments'])
->latest('published_at')
->paginate(10);
// Aggregates
$count = Post::where('status', 'published')->count();
$avg = Order::avg('total');
// Chunking for large datasets
Post::chunk(100, fn($posts) => $posts->each->process());
// Closure-based (auto commit/rollback)
$order = DB::transaction(function () use ($data) {
$order = Order::create($data['order']);
foreach ($data['items'] as $item) {
$order->items()->create($item);
}
return $order;
});
class StorePostRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user()->can('create', Post::class);
}
public function rules(): array
{
return [
'title' => ['required', 'string', 'max:255'],
'slug' => ['required', Rule::unique('posts')->ignore($this->post)],
'content' => ['required', 'string', 'min:100'],
'status' => ['required', Rule::enum(PostStatus::class)],
'category_id' => ['required', 'exists:categories,id'],
'tags' => ['array'],
'tags.*' => ['exists:tags,id'],
];
}
public function messages(): array
{
return ['title.required' => 'A post title is required.'];
}
}
| Rule | Description |
|---|---|
required | Must be present and not empty |
nullable | Can be null |
string, integer, boolean | Type validation |
email | Valid email format |
unique:table,column | Unique in database |
exists:table,column | Must exist in database |
in:a,b,c | Must be one of values |
min:n, max:n | Size constraints |
Advanced: For custom validation rules and complex conditional validation, see REFERENCE.md
class EnsureUserIsActive
{
public function handle(Request $request, Closure $next): Response
{
if (!$request->user()?->isActive()) {
return response()->json(['message' => 'Account inactive.'], 403);
}
return $next($request);
}
}
// With parameters
class CheckRole
{
public function handle(Request $request, Closure $next, string ...$roles): Response
{
if (!$request->user()?->hasAnyRole($roles)) {
abort(403);
}
return $next($request);
}
}
// Usage: Route::middleware('role:admin,moderator')
Advanced: For tenant-scoping middleware and session management, see REFERENCE.md
// Login and issue token
public function login(LoginRequest $request)
{
$user = User::where('email', $request->email)->first();
if (!$user || !Hash::check($request->password, $user->password)) {
throw ValidationException::withMessages([
'email' => ['Invalid credentials.'],
]);
}
$user->tokens()->delete(); // Revoke existing
$token = $user->createToken('api-token', ['read', 'write'])->plainTextToken;
return response()->json(['user' => new UserResource($user), 'token' => $token]);
}
// Protected routes
Route::middleware('auth:sanctum')->group(function () {
Route::get('/user', fn(Request $request) => $request->user());
});
Advanced: For Passport OAuth, spatie/permission RBAC, see REFERENCE.md
class ProcessContacts extends Command
{
protected $signature = 'contacts:process
{--status=active : Filter by status}
{--limit=100 : Maximum to process}
{--dry-run : Simulate without changes}';
protected $description = 'Process contacts based on criteria';
public function handle(): int
{
$query = Contact::where('status', $this->option('status'))
->limit((int) $this->option('limit'));
if (!$this->confirm("Process {$query->count()} contacts?")) {
return Command::SUCCESS;
}
$this->withProgressBar($query->cursor(), fn($c) => $this->process($c));
$this->newLine();
$this->info('Done.');
return Command::SUCCESS;
}
}
| Command | Description |
|---|---|
php artisan make:model -mfc | Model + migration + factory + controller |
php artisan make:controller --api | API resource controller |
php artisan make:request | Form request validation |
php artisan make:job | Queueable job |
php artisan queue:work | Process queue jobs |
php artisan schedule:run | Run scheduled tasks |
Advanced: For long-running consumers and graceful shutdown, see REFERENCE.md
class SendWelcomeEmail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public int $tries = 3;
public int $timeout = 60;
public array $backoff = [30, 60, 120];
public function __construct(public User $user) {}
public function handle(Mailer $mailer): void
{
$mailer->to($this->user->email)->send(new WelcomeMail($this->user));
}
public function failed(\Throwable $e): void
{
Log::error('Welcome email failed', ['user' => $this->user->id, 'error' => $e->getMessage()]);
}
}
// Basic dispatch
SendWelcomeEmail::dispatch($user);
// With options
SendWelcomeEmail::dispatch($user)
->onQueue('emails')
->delay(now()->addMinutes(10));
// Conditional
SendWelcomeEmail::dispatchIf($user->wantsEmails(), $user);
Advanced: For job batching, chaining, and rate limiting, see REFERENCE.md
class PostPublished implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public function __construct(public Post $post) {}
public function broadcastOn(): array
{
return [new PrivateChannel("user.{$this->post->author_id}")];
}
}
// Dispatch: event(new PostPublished($post));
class PostControllerTest extends TestCase
{
use RefreshDatabase;
public function test_user_can_create_post(): void
{
$user = User::factory()->create();
$response = $this->actingAs($user)->postJson('/api/posts', [
'title' => 'Test Post',
'content' => 'Test content for the post.',
]);
$response->assertStatus(201)
->assertJson(['data' => ['title' => 'Test Post']]);
$this->assertDatabaseHas('posts', [
'title' => 'Test Post',
'author_id' => $user->id,
]);
}
}
public function test_order_processing(): void
{
Queue::fake();
$gateway = $this->mock(PaymentGateway::class);
$gateway->shouldReceive('charge')->once()->andReturn(['id' => 'ch_123']);
$this->postJson('/api/orders', [...]);
Queue::assertPushed(ProcessOrder::class);
}
Advanced: For testing commands, complex mocking, see REFERENCE.md
class AppServiceProvider extends ServiceProvider
{
public function register(): void
{
// Bind interfaces to implementations
$this->app->bind(UserRepositoryInterface::class, UserRepository::class);
// Singleton binding
$this->app->singleton(PaymentGateway::class, fn($app) =>
new StripeGateway(config('services.stripe.key'))
);
}
public function boot(): void
{
Model::preventLazyLoading(!app()->isProduction());
}
}
Advanced: For contextual binding and deferred providers, see REFERENCE.md
// app/Console/Kernel.php
protected function schedule(Schedule $schedule): void
{
$schedule->command('queue:work --stop-when-empty')
->everyMinute()
->withoutOverlapping();
$schedule->command('reports:generate')
->dailyAt('00:00')
->onOneServer()
->emailOutputOnFailure('admin@example.com');
$schedule->job(new ProcessPendingOrders)
->hourly()
->between('08:00', '22:00');
}
For advanced patterns including multi-tenancy, repository pattern, performance optimization, and debugging, see REFERENCE.md
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.