Back to list
FortiumPartners

developing-with-laravel

by FortiumPartners

Ensemble Plugin Ecosystem - Modular Claude Code plugins for AI-augmented development workflows

0🍴 1📅 Jan 22, 2026

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

  1. Project Structure
  2. Routing & Controllers
  3. Eloquent ORM
  4. Validation
  5. Middleware
  6. Authentication
  7. Artisan CLI
  8. Queues & Jobs
  9. Events & Listeners
  10. Testing
  11. Service Providers
  12. 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

MethodRelationshipExample
hasOne1:1User has one Profile
belongsTo1:1 inverseProfile belongs to User
hasMany1:nUser has many Posts
belongsToManyn:nPost has many Tags
morphMany1:n polymorphicPost 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

RuleDescription
requiredMust be present and not empty
nullableCan be null
string, integer, booleanType validation
emailValid email format
unique:table,columnUnique in database
exists:table,columnMust exist in database
in:a,b,cMust be one of values
min:n, max:nSize 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

CommandDescription
php artisan make:model -mfcModel + migration + factory + controller
php artisan make:controller --apiAPI resource controller
php artisan make:requestForm request validation
php artisan make:jobQueueable job
php artisan queue:workProcess queue jobs
php artisan schedule:runRun 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

65/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

1ヶ月以内に更新

+10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

+5

Reviews

💬

Reviews coming soon