Skip to main content
skill Claude Code CX Codex Cursor GitHub Copilot

laravel-patterns — Claude Code skill: what it does, how to install

This skill guides Laravel architecture, routing, Eloquent, queues, and API resources. Use it when building or reviewing Laravel apps.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add affaan-m/ECC --skill laravel-patterns -a claude-code
Open on GitHub

What laravel-patterns does

laravel-patterns organizes Laravel code around clear boundaries between controllers, services, actions, and models. It favors thin controllers, single-purpose actions, service-provider bindings, resource controllers, and route-model binding. The routing guidance covers scoped bindings for parent-child relationships and explicit model binding when a parameter needs a different class.

The skill also covers typed Eloquent models, casts, enums, value objects, eager loading, query objects, scopes, and SoftDeletes. It includes patterns for API resources, pagination, queues for IO-heavy work, events, caching, and explicit configuration in config/*. It can be installed for Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, or OpenCode. Use it for Laravel implementation or review work; it is a poor fit for projects outside Laravel.

What it helps with

  • separate controllers from service and action logic
  • define nested routes with scoped bindings
  • model relationships and casts in Eloquent
  • avoid N+1 queries with eager loading
  • shape APIs with resources and pagination

Example prompts for the agent

  • “Review this Laravel controller and move orchestration into a service or action.”
  • “Set up nested accounts and projects routes with scoped bindings.”
  • “Inspect these Eloquent queries for an N+1 problem.”

How to install laravel-patterns

1Skills CLI (Vercel)

npx skills add affaan-m/ECC --skill laravel-patterns -a claude-code

2Manually: put the files in place

~/.claude/skills/laravel-patterns/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/laravel-patterns/

From the same set146

The whole set →
video-editing affaan-m AI-assisted workflow for editing, trimming, and enriching real footage. Connects raw recordings with FFmpeg cuts, Remotion overlays, and ElevenLabs voiceovers. Claude Code CX Codex Cursor +1 6,500installs docker-patterns affaan-m A skill with Dockerfile and Docker Compose patterns: local dev stacks, networking, volumes, container hardening. Claude Code CX Codex Cursor +1 6,300installs frontend-patterns affaan-m A skill for React and Next.js work: component patterns, state, data fetching, and render performance. Use it when building or reviewing UI. Claude Code CX Codex Cursor +1 5,900installs backend-patterns affaan-m A skill with backend patterns for Node.js, Express and Next.js API routes: layering, caching, transactions, error handling. Claude Code CX Codex Cursor +1 5,900installs coding-standards affaan-m Baseline cross-project coding conventions for naming, immutability, and clean code principles. Useful for code reviews and refactoring. Claude Code CX Codex Cursor +1 5,600installs api-design affaan-m Provides REST API design standards covering resource naming, status codes, pagination, and error payloads. Use it when creating or auditing API contracts. Claude Code CX Codex Cursor +1 5,500installs rust-testing affaan-m A skill for writing Rust tests: unit, integration, async, property-based, mocks and coverage. Useful when the project runs on TDD. Claude Code CX Codex Cursor +1 5,500installs laravel-security affaan-m This skill reviews Laravel authentication, Eloquent safety, CSRF, XSS, APIs, and production settings. Use it for security reviews or deployment checks. Claude Code CX Codex Cursor +1 5,400installs

Similar

mcp-integration official Anthropic This skill shows how to connect MCP servers to Claude Code plugins and expose their tools. Use it when a plugin needs access to an API, database, or file system. Claude Code 6,994installs turnstile-spin cloudflare Sets up Cloudflare Turnstile bot verification across existing frontend and backend code, including widget embedding and server-side siteverify. Claude Code CX Codex Cursor +1 92,200installs vercel-cli vercel This skill guides an agent through Vercel CLI deployments, inspection, and troubleshooting. Use it for logs, environment variables, domains, and project setup. Claude Code CX Codex Cursor +1 6,700installs build-mcp-server official Anthropic This skill helps design an MCP server for Claude Code by clarifying the use case, choosing deployment, and shaping the tool interface. Claude Code 6,711installs mcp-builder official anthropics This skill walks through building an MCP server in TypeScript or Python, from reading the protocol spec to writing tests and evaluation questions. Claude Code Claude.ai 130,949installs lark-im larksuite This skill lets an agent work with Lark IM messages, chats, files, and interactive cards. Use it when coding tasks need access to team conversations. Claude Code CX Codex Cursor +1 472,300installs lark-event larksuite Listens to Lark/Feishu events and streams them as NDJSON. Use it for bots, message processing, and long-running subscribers. Claude Code CX Codex Cursor +1 466,200installs caveman-discover JuliusBrussee Finds and labels every LLM workflow in your repository so Caveman Cloud breaks down spend by actual job rather than grouping everything into one bucket. Claude Code CX Codex Cursor +1 103,500installs

Questions about laravel-patterns

How do I install laravel-patterns in Claude Code?

Run npx skills add affaan-m/ECC --skill laravel-patterns -a claude-code. The skill file is placed at ~/.claude/skills/laravel-patterns/SKILL.md.

What does laravel-patterns do?

It provides Laravel architecture guidance for routing, controllers, Eloquent, services, actions, API resources, queues, events, and caching.

Which platforms support laravel-patterns?

The skill is available for Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, and OpenCode.

Source text

SKILL.md · MIT
---
name: laravel-patterns
description: Laravel architecture patterns, routing/controllers, Eloquent ORM, service layers, queues, events, caching, and API resources for production apps. Use when building or reviewing Laravel apps — controllers, Eloquent, service layers, queues, or API resources.
metadata:
  origin: ECC
---

# Laravel Development Patterns

Production-grade Laravel architecture patterns for scalable, maintainable applications.

## When to Use

- Building Laravel web applications or APIs
- Structuring controllers, services, and domain logic
- Working with Eloquent models and relationships
- Designing APIs with resources and pagination
- Adding queues, events, caching, and background jobs

## How It Works

- Structure the app around clear boundaries (controllers -> services/actions -> models).
- Use explicit bindings and scoped bindings to keep routing predictable; still enforce authorization for access control.
- Favor typed models, casts, and scopes to keep domain logic consistent.
- Keep IO-heavy work in queues and cache expensive reads.
- Centralize config in `config/*` and keep environments explicit.

## Examples

### Project Structure

Use a conventional Laravel layout with clear layer boundaries (HTTP, services/actions, models).

### Recommended Layout

```
app/
├── Actions/            # Single-purpose use cases
├── Console/
├── Events/
├── Exceptions/
├── Http/
│   ├── Controllers/
│   ├── Middleware/
│   ├── Requests/       # Form request validation
│   └── Resources/      # API resources
├── Jobs/
├── Models/
├── Policies/
├── Providers/
├── Services/           # Coordinating domain services
└── Support/
config/
database/
├── factories/
├── migrations/
└── seeders/
resources/
├── views/
└── lang/
routes/
├── api.php
├── web.php
└── console.php
```

### Controllers -> Services -> Actions

Keep controllers thin. Put orchestration in services and single-purpose logic in actions.

```php
final class CreateOrderAction
{
    public function __construct(private OrderRepository $orders) {}

    public function handle(CreateOrderData $data): Order
    {
        return $this->orders->create($data);
    }
}

final class OrdersController extends Controller
{
    public function __construct(private CreateOrderAction $createOrder) {}

    public function store(StoreOrderRequest $request): JsonResponse
    {
        $order = $this->createOrder->handle($request->toDto());

        return response()->json([
            'success' => true,
            'data' => OrderResource::make($order),
            'error' => null,
            'meta' => null,
        ], 201);
    }
}
```

### Routing and Controllers

Prefer route-model binding and resource controllers for clarity.

```php
use Illuminate\Support\Facades\Route;

Route::middleware('auth:sanctum')->group(function () {
    Route::apiResource('projects', ProjectController::class);
});
```

### Route Model Binding (Scoped)

Use scoped bindings to prevent cross-tenant access.

```php
Route::scopeBindings()->group(function () {
    Route::get('/accounts/{account}/projects/{project}', [ProjectController::class, 'show']);
});
```

### Nested Routes and Binding Names

- Keep prefixes and paths consistent to avoid double nesting (e.g., `conversation` vs `conversations`).
- Use a single parameter name that matches the bound model (e.g., `{conversation}` for `Conversation`).
- Prefer scoped bindings when nesting to enforce parent-child relationships.

```php
use App\Http\Controllers\Api\ConversationController;
use App\Http\Controllers\Api\MessageController;
use Illuminate\Support\Facades\Route;

Route::middleware('auth:sanctum')->prefix('conversations')->group(function () {
    Route::post('/', [ConversationController::class, 'store'])->name('conversations.store');

    Route::scopeBindings()->group(function () {
        Route::get('/{conversation}', [ConversationController::class, 'show'])
            ->name('conversations.show');

        Route::post('/{conversation}/messages', [MessageController::class, 'store'])
            ->name('conversation-messages.store');

        Route::get('/{conversation}/messages/{message}', [MessageController::class, 'show'])
            ->name('conversation-messages.show');
    });
});
```

If you want a parameter to resolve to a different model class, define explicit binding. For custom binding logic, use `Route::bind()` or implement `resolveRouteBinding()` on the model.

```php
use App\Models\AiConversation;
use Illuminate\Support\Facades\Route;

Route::model('conversation', AiConversation::class);
```

### Service Container Bindings

Bind interfaces to implementations in a service provider for clear dependency wiring.

```php
use App\Repositories\EloquentOrderRepository;
use App\Repositories\OrderRepository;
use Illuminate\Support\ServiceProvider;

final class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->bind(OrderRepository::class, EloquentOrderRepository::class);
    }
}
```

### Eloquent Model Patterns

### Model Configuration

```php
final class Project extends Model
{
    use HasFactory;

    protected $fillable = ['name', 'owner_id', 'status'];

    protected $casts = [
        'status' => ProjectStatus::class,
        'archived_at' => 'datetime',
    ];

    public function owner(): BelongsTo
    {
        return $this->belongsTo(User::class, 'owner_id');
    }

    public function scopeActive(Builder $query): Builder
    {
        return $query->whereNull('archived_at');
    }
}
```

### Custom Casts and Value Objects

Use enums or value objects for strict typing.

```php
use Illuminate\Database\Eloquent\Casts\Attribute;

protected $casts = [
    'status' => ProjectStatus::class,
];
```

```php
protected function budgetCents(): Attribute
{
    return Attribute::make(
        get: fn (int $value) => Money::fromCents($value),
        set: fn (Money $money) => $money->toCents(),
    );
}
```

### Eager Loading to Avoid N+1

```php
$orders = Order::query()
    ->with(['customer', 'items.product'])
    ->latest()
    ->paginate(25);
```

### Query Objects for Complex Filters

```php
final class ProjectQuery
{
    public function __construct(private Builder $query) {}

    public function ownedBy(int $userId): self
    {
        $query = clone $this->query;

        return new self($query->where('owner_id', $userId));
    }

    public function active(): self
    {
        $query = clone $this->query;

        return new self($query->whereNull('archived_at'));
    }

    public function builder(): Builder
    {
        return $this->query;
    }
}
```

### Global Scopes and Soft Deletes

Use global scopes for default filtering and `SoftDeletes` for recoverable records.
Use either a global scope or a named scope for the same filter, not both, unless you intend layered behavior.

```php
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Builder;

final class Project extends Model
{
    use SoftDeletes;

    protected static function booted(): void
    {
        static::addGlobalScope('active', function (Builder $builder): void {
            $builder->whereNull('archived_at');
        });
    }
}
```

### Query Scopes for Reusable Filters

```php
use Illuminate\Database\Eloquent\Builder;

final class Project extends Model
{
    public function scopeOwnedBy(Builder $query, int $userId): Builder
    {
        return $query->where('owner_id', $userId);
    }
}

// In service, repository etc.
$projects = Project::ownedBy($user->id)->get();
```

### Transactions for Multi-Step Updates

```php
use Illuminate\Support\Facades\DB;

DB::transaction(function (): void {
    $order->update(['status' => 'paid']);
    $order->items()->update(['paid_at' => now()]);
});
```

### Migrations

### Naming Convention

- File names use timestamps: `YYYY_MM_DD_HHMMSS_create_users_table.php`
- Migrations use anonymous classes (no named class); the filename communicates intent
- Table names are `snake_case` and plural by default

### Example Migration

```php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('orders', function (Blueprint $table): void {
            $table->id();
            $table->foreignId('customer_id')->constrained()->cascadeOnDelete();
            $table->string('status', 32)->index();
            $table->unsignedInteger('total_cents');
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('orders');
    }
};
```

### Form Requests and Validation

Keep validation in form requests and transform inputs to DTOs.

```php
use App\Models\Order;

final class StoreOrderRequest extends FormRequest
{
    public function authorize(): bool
    {
        return $this->user()?->can('create', Order::class) ?? false;
    }

    public function rules(): array
    {
        return [
            'customer_id' => ['required', 'integer', 'exists:customers,id'],
            'items' => ['required', 'array', 'min:1'],
            'items.*.sku' => ['required', 'string'],
            'items.*.quantity' => ['required', 'integer', 'min:1'],
        ];
    }

    public function toDto(): CreateOrderData
    {
        return new CreateOrderData(
            customerId: (int) $this->validated('customer_id'),
            items: $this->validated('items'),
        );
    }
}
```

### API Resources

Keep API responses consistent with resources and pagination.

```php
$projects = Project::query()->active()->paginate(25);

return response()->json([
    'success' => true,
    'data' => ProjectResource::collection($projects->items()),
    'error' => null,
    'meta' => [
        'page' => $projects->currentPage(),
        'per_page' => $projects->perPage(),
        'total' => $projects->total(),
    ],
]);
```

### Events, Jobs, and Queues

- Emit domain events for side effects (emails, analytics)
- Use queued jobs for slow work (reports, exports, webhooks)
- Prefer idempotent handlers with retries and backoff

### Caching

- Cache read-heavy endpoints and expensive queries
- Invalidate caches on model events (created/updated/deleted)
- Use tags when caching related data for easy invalidation

### Configuration and Environments

- Keep secrets in `.env` and config in `config/*.php`
- Use per-environment config overrides and `config:cache` in production

© affaan-m · MIT · affaan-m/ECC

See also

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor