
developing-with-laravel
by FortiumPartners
Ensemble Plugin Ecosystem - Modular Claude Code plugins for AI-augmented development workflows
SKILL.md
name: developing-with-laravel description: 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 Skill - Quick Reference
Laravel framework patterns for modern PHP applications. For PHP language fundamentals, see the PHP skill. For advanced patterns, see REFERENCE.md.
Table of Contents
- Project Structure
- Routing & Controllers
- Eloquent ORM
- Validation
- Middleware
- Authentication
- Artisan CLI
- Queues & Jobs
- Events & Listeners
- Testing
- Service Providers
- Task Scheduling
Project Structure
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
Routing & Controllers
Route Definitions
// 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']);
});
Controllers
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'));
}
}
Eloquent ORM
Model Definition
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),
);
}
}
Relationships Quick Reference
| 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
Query Builder
// 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());
Transactions
// 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;
});
Validation
Form Requests
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.'];
}
}
Common Validation Rules
| 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
Middleware
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
Authentication
Sanctum (API Tokens)
// 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
Artisan CLI
Custom Command
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;
}
}
Common Artisan Commands
| 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
Queues & Jobs
Job Definition
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()]);
}
}
Dispatching Jobs
// 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
Events & Listeners
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));
Testing
Feature Test
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,
]);
}
}
Mocking
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
Service Providers
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
Task Scheduling
// 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
Score
Total Score
Based on repository quality metrics
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 100以上
1ヶ月以内に更新
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
Reviews
Reviews coming soon


