Laravel framework patterns, Eloquent ORM, authentication, queues, and testing. Use for: laravel, eloquent, artisan, blade, php, sanctum, livewire, inertia, pest, phpunit, forge, vapor, queue, middleware, migration, factory, seeder.
Scanned 9/5/2026
Install to Claude Code
npx -y skills add 0xDarkMatter/claude-mods --skill laravel-ops --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Laravel Ops?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/0xdarkmatter-laravel-ops)More formats (shields.io, HTML) on the badges page.
---
name: laravel-ops
description: "Laravel framework patterns, Eloquent ORM, authentication, queues, and testing. Use for: laravel, eloquent, artisan, blade, php, sanctum, livewire, inertia, pest, phpunit, forge, vapor, queue, middleware, migration, factory, seeder."
when_to_use: "Use when building Laravel 11+ apps — e.g. 'write an Eloquent query or migration', 'set up Sanctum auth', 'configure queues and jobs', 'test with Pest or PHPUnit'. Covers artisan, Blade, Livewire/Inertia, Forge/Vapor, factories, and seeders."
license: MIT
allowed-tools: "Read Write Bash"
metadata:
author: claude-mods
related-skills: sql-ops, postgres-ops, testing-ops, docker-ops
---
# Laravel Operations
> Facts verified as of 2026-07.
Authoritative reference for Laravel 11+ development: architecture decisions, Eloquent patterns, authentication strategies, queue configuration, and testing approaches.
---
## Architecture Decision Tree
```
What type of application?
│
├─ Full-stack web (HTML responses)
│ ├─ Simple CRUD, small team → Monolith (Blade + Eloquent directly)
│ │ └─ Use action classes for business logic over 20 lines
│ ├─ Rich interactivity needed → Livewire (server-driven reactivity)
│ │ └─ Add Alpine.js for client-side micro-interactions
│ └─ SPA-like feel, React/Vue team → Inertia.js
│ └─ Keep server-side routing, dump client-side routing overhead
│
├─ API backend (JSON responses)
│ ├─ Single consumer (mobile/SPA) → API-only with Sanctum SPA auth
│ ├─ Multiple consumers / public → RESTful API with token auth
│ └─ Complex graph queries → Consider GraphQL (lighthouse-php/lighthouse)
│
├─ Large team / complex domain
│ ├─ Domain-driven → Modular monolith (app/Modules/{Domain}/)
│ │ ├─ Each module: Models, Actions, Events, Jobs, Http/
│ │ └─ Shared: app/Shared/ for cross-cutting concerns
│ └─ Independent deployability needed → Microservices
│ └─ Use Laravel Octane for high-throughput services
│
└─ What business logic pattern?
├─ Simple CRUD, < 20 lines → Direct Eloquent in controller
├─ Reusable operation (create order, send invoice) → Action class
│ └─ Single public handle() or execute() method
├─ Complex queries, multiple data sources → Repository pattern
│ └─ Interface + Eloquent implementation (enables swapping)
└─ Cross-cutting operations (audit, caching) → Service class
└─ Inject via constructor, bind in ServiceProvider
```
### Action Class vs Repository vs Service
| Pattern | Use When | Example |
|---------|----------|---------|
| Action class | Single, reusable business operation | `CreateOrderAction`, `SendInvoiceAction` |
| Repository | Abstract data access, multiple sources | `OrderRepository` with `EloquentOrderRepository` |
| Service | Orchestrate multiple actions/repos | `OrderService` combining payment + inventory |
| Direct Eloquent | Simple CRUD, < 5 lines in controller | `User::create($data)` |
---
## Eloquent Quick Reference
### Relationships
| Relationship | Method | Foreign Key Convention |
|-------------|--------|----------------------|
| `hasOne` | `return $this->hasOne(Profile::class)` | `profiles.user_id` |
| `hasMany` | `return $this->hasMany(Post::class)` | `posts.user_id` |
| `belongsTo` | `return $this->belongsTo(User::class)` | `posts.user_id` |
| `belongsToMany` | `return $this->belongsToMany(Role::class)` | `role_user` pivot |
| `hasManyThrough` | `return $this->hasManyThrough(Post::class, User::class)` | Country → User → Post |
| `morphTo` | `return $this->morphTo()` | `{col}_type`, `{col}_id` |
| `morphMany` | `return $this->morphMany(Comment::class, 'commentable')` | Polymorphic |
| `morphToMany` | `return $this->morphToMany(Tag::class, 'taggable')` | Polymorphic pivot |
### Eager Loading
```php
// Prevent N+1: always eager load in controllers
$posts = Post::with(['author', 'comments.author', 'tags'])->paginate(15);
// Conditional eager loading (load after retrieval)
$user->load('posts.comments');
$user->loadMissing('posts'); // only if not already loaded
// Eager load counts (no SELECT *)
$posts = Post::withCount('comments')->get();
// Constrained eager loading
$posts = Post::with(['comments' => fn($q) => $q->approved()->latest()])->get();
```
### Query Scopes
```php
// Local scope (reusable query constraint)
public function scopeActive(Builder $query): void
{
$query->where('status', 'active');
}
// Usage: User::active()->get()
// Dynamic scope
public function scopeOfType(Builder $query, string $type): void
{
$query->where('type', $type);
}
// Usage: User::ofType('admin')->get()
```
### Mass Assignment
```php
// Fillable (allowlist - preferred)
protected $fillable = ['name', 'email', 'password'];
// Guarded (denylist - use [] only if you trust all input)
protected $guarded = ['id', 'is_admin'];
// Never set guarded = [] in production code
```
---
## Artisan Command Cheat Sheet
| Command | Purpose | Common Options |
|---------|---------|----------------|
| `make:model Post -mfs` | Model + migration + factory + seeder | `-c` controller, `-r` resource |
| `make:controller PostController -r` | Resource controller (7 methods) | `--api` skips create/edit |
| `make:request StorePostRequest` | Form request for validation | |
| `make:job ProcessPayment` | Queueable job class | `--sync` for sync job |
| `make:event OrderPlaced` | Event class | |
| `make:listener SendOrderConfirmation -e OrderPlaced` | Listener for event | `--queued` |
| `make:notification InvoicePaid` | Notification class | |
| `make:policy PostPolicy -m Post` | Policy with model | |
| `make:middleware EnsureUserIsAdmin` | HTTP middleware | |
| `make:command SendDailyReport` | Custom Artisan command | |
| `migrate` | Run pending migrations | `--step` for individual |
| `migrate:rollback` | Roll back last batch | `--step=5` |
| `migrate:fresh --seed` | Drop all + re-migrate + seed | |
| `db:seed` | Run all seeders | `--class=UserSeeder` |
| `tinker` | REPL with app context | |
| `route:list` | Show all routes | `--name=api` filter |
| `route:cache` | Cache routes for production | |
| `config:cache` | Cache config for production | |
| `view:cache` | Pre-compile Blade templates | |
| `optimize` | Run all cache commands | `optimize:clear` to reset |
| `queue:work` | Process queue jobs | `--queue=high,default` |
| `queue:listen` | Work + auto-reload on code change | |
| `queue:failed` | List failed jobs | |
| `queue:retry all` | Retry all failed jobs | |
| `schedule:run` | Run due scheduled tasks | |
| `schedule:work` | Run scheduler every minute (dev) | |
| `key:generate` | Generate APP_KEY | |
| `test` | Run PHPUnit/Pest tests | `--filter=UserTest` |
| `test --parallel` | Run tests in parallel | `--processes=4` |
| `vendor:publish` | Publish package assets/config | `--tag=config` |
---
## Authentication Decision Tree
```
What do you need?
│
├─ SPA (Vue/React) + Laravel API backend
│ └─ Sanctum SPA authentication
│ ├─ Cookie-based (same domain or subdomain)
│ ├─ Csrf-cookie endpoint: GET /sanctum/csrf-cookie
│ └─ No tokens in localStorage (XSS safe)
│
├─ Mobile app or third-party API consumers
│ └─ Sanctum API tokens (Bearer tokens)
│ ├─ createToken($name, $abilities)
│ ├─ Token abilities for fine-grained control
│ └─ Token expiration with token:prune schedule
│
├─ Traditional web app (server-rendered)
│ ├─ Just need auth pages quickly → Breeze
│ │ ├─ Minimal, educational, Blade or Inertia stack
│ │ └─ Install: composer require laravel/breeze --dev
│ ├─ Need teams, 2FA, profile management → Jetstream
│ │ ├─ Livewire or Inertia stack
│ │ └─ Install: composer require laravel/jetstream
│ └─ Need headless auth (API + custom UI) → Fortify
│ ├─ Actions in app/Actions/Fortify/
│ └─ Customize: CreateNewUser, UpdateUserPassword
│
└─ Custom / enterprise
├─ LDAP/SAML → socialiteproviders/saml2
├─ OAuth social login → laravel/socialite
└─ Custom guard → Implement Guard + UserProvider contracts
```
### Sanctum Quick Setup
```php
// config/sanctum.php - stateful domains for SPA
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost')),
// API token creation
$token = $user->createToken('mobile-app', ['orders:read', 'orders:write']);
return ['token' => $token->plainTextToken];
// Check token ability
Route::get('/orders', function (Request $request) {
$request->user()->tokenCan('orders:read'); // bool
});
// Protect routes
Route::middleware('auth:sanctum')->group(function () {
// authenticated routes
});
```
---
## Queue Decision Tree
```
Queue driver selection:
│
├─ Development / testing
│ └─ sync driver (executes immediately, no worker needed)
│ QUEUE_CONNECTION=sync
│
├─ Small app, no Redis available
│ └─ database driver
│ ├─ php artisan queue:table && migrate
│ ├─ Works fine for < 100 jobs/min
│ └─ QUEUE_CONNECTION=database
│
├─ Medium-high throughput, self-hosted
│ └─ Redis driver (via predis or phpredis)
│ ├─ QUEUE_CONNECTION=redis
│ ├─ Laravel Horizon for monitoring
│ └─ Supports priorities, pausing, metrics
│
└─ AWS infrastructure / massive scale
└─ SQS driver
├─ QUEUE_CONNECTION=sqs
├─ Managed, auto-scaling
└─ Use with Laravel Vapor for serverless
```
### Job Patterns
```php
// Basic job dispatch
ProcessPayment::dispatch($order);
ProcessPayment::dispatch($order)->onQueue('payments')->delay(now()->addMinutes(5));
// Chaining (sequential)
Bus::chain([
new ProcessPayment($order),
new SendInvoice($order),
new UpdateInventory($order),
])->dispatch();
// Batching (parallel + callback)
$batch = Bus::batch([
new ImportRow($row1),
new ImportRow($row2),
new ImportRow($row3),
])->then(fn(Batch $batch) => ImportComplete::dispatch())
->catch(fn(Batch $batch, Throwable $e) => Log::error($e))
->dispatch();
// Rate limiting (throttle to 5 per minute)
public function middleware(): array
{
return [new RateLimited('payments')];
}
// Unique jobs (prevent duplicate processing)
use Illuminate\Contracts\Queue\ShouldBeUnique;
class ProcessPayment implements ShouldQueue, ShouldBeUnique
{
public string $uniqueId => $this->order->id;
public int $uniqueFor = 3600; // seconds
}
// Retry configuration
public int $tries = 3;
public int $backoff = 60; // seconds between retries
public function retryUntil(): DateTime
{
return now()->addHours(24);
}
```
### Task Scheduling
```php
// routes/console.php (Laravel 11+)
Schedule::job(SendDailyReport::class)->dailyAt('08:00')->timezone('America/New_York');
Schedule::command('backup:run')->daily()->runInBackground()->emailOutputOnFailure('ops@app.com');
Schedule::call(fn() => Cache::flush())->weekly()->sundays()->at('00:00');
// Prevent overlap (long-running tasks)
Schedule::job(ProcessImport::class)->everyFiveMinutes()->withoutOverlapping();
// Run on one server only (requires Redis/database cache driver)
Schedule::job(SendNewsletters::class)->daily()->onOneServer();
```
---
## Testing Quick Reference
### Test Types
| Type | Class extends | Database | Purpose |
|------|--------------|----------|---------|
| Feature test | `Tests\TestCase` | Yes (with trait) | HTTP endpoints, full stack |
| Unit test | `PHPUnit\Framework\TestCase` | No | Pure logic, no app boot |
| Browser test | `Laravel\Dusk\TestCase` | Yes | Real browser via ChromeDriver |
### Database Traits
```php
use Illuminate\Foundation\Testing\RefreshDatabase; // migrate fresh each test (slower)
use Illuminate\Foundation\Testing\DatabaseTransactions; // rollback each test (faster)
```
### Pest Syntax (preferred in Laravel 11+)
```php
describe('User authentication', function () {
beforeEach(function () {
$this->user = User::factory()->create();
});
it('allows login with valid credentials', function () {
$response = $this->post('/login', [
'email' => $this->user->email,
'password' => 'password',
]);
$response->assertRedirect('/dashboard');
$this->assertAuthenticatedAs($this->user);
});
it('rejects invalid credentials')->todo();
});
```
### Common Assertions
```php
// HTTP response
$response->assertStatus(200);
$response->assertOk(); // 200
$response->assertCreated(); // 201
$response->assertNoContent(); // 204
$response->assertUnauthorized(); // 401
$response->assertForbidden(); // 403
$response->assertNotFound(); // 404
$response->assertRedirect('/home');
// JSON responses
$response->assertJson(['status' => 'ok']);
$response->assertJsonPath('data.email', 'user@example.com');
$response->assertJsonCount(3, 'data');
$response->assertJsonStructure(['data' => ['id', 'name', 'email']]);
$response->assertJsonMissing(['password']);
// Database
$this->assertDatabaseHas('users', ['email' => 'user@example.com']);
$this->assertDatabaseMissing('users', ['email' => 'deleted@example.com']);
$this->assertDatabaseCount('posts', 5);
$this->assertSoftDeleted('posts', ['id' => $post->id]);
```
---
## Common Gotchas
| Gotcha | Why | Fix |
|--------|-----|-----|
| N+1 queries on relationships | Eloquent lazy-loads by default | Use `with()` eager loading; enable `Model::preventLazyLoading()` in AppServiceProvider during development |
| Mass assignment vulnerability | `$fillable = []` accepts all | Always define `$fillable`; never use `$guarded = []` in production |
| `created_at` not updating on `update()` | Only `updated_at` auto-sets | Use `$model->touch()` or `timestamps = true` (default) |
| Queue job fails on model serialization | Model state may change between dispatch and processing | Use `SerializesModels` trait; re-fetch from DB in `handle()` if needed |
| Timezone mismatch in scheduled tasks | Server tz != app tz | Set `APP_TIMEZONE` in `.env`; use `->timezone()` on schedule entries |
| Middleware order matters | Auth middleware must run before policies | Global → route group → route. Auth before throttle check or vice versa changes 401 vs 429 |
| Route model binding skips soft-deleted records | `RouteServiceProvider` ignores `trashed()` | Extend binding: `Route::bind('post', fn($id) => Post::withTrashed()->findOrFail($id))` |
| Service container binding not auto-resolved | Interface not bound to implementation | Register in `AppServiceProvider::register()`: `$this->app->bind(Interface::class, Implementation::class)` |
| Migration foreign key order | Must create referenced table first | Run `migrate:fresh` to verify; use `Schema::disableForeignKeyConstraints()` in tests |
| CSRF protection blocks API routes | `VerifyCsrfToken` runs on all web routes | Register API routes in `routes/api.php` (uses `api` middleware group without CSRF) |
| `env()` returns null after caching | `config:cache` bakes env values | Always access env via `config()` helper in app code; only use `env()` in `config/` files |
| Blade `@stack` renders in wrong order | `@push` must appear after `@stack` in execution | Use `@prepend` for scripts that must appear first |
| Event listener not firing | Listener not registered or discovered | Check `EventServiceProvider::$listen`; or enable `Event::discover()` in Laravel 11 |
---
## Reference Files
| File | Contents |
|------|---------|
| `references/eloquent-queries.md` | Deep-dive: relationships, query builder, scopes, accessors, mutators, events, soft deletes, pagination, performance, collections, factories |
| `references/architecture.md` | Service container, providers, facades, middleware, events, notifications, jobs, scheduling, Blade components, Livewire, Inertia |
| `references/testing-auth.md` | PHPUnit/Pest setup, HTTP tests, database testing, fakes, Sanctum, Fortify, policies, form requests, Dusk |
---
## See Also
- `sql-ops` - Query optimization, indexing strategy, raw SQL patterns
- `postgres-ops` - PostgreSQL-specific features, JSON columns, full-text search
- `testing-ops` - General testing philosophy, TDD, CI integration
- `docker-ops` - Containerizing Laravel apps, Docker Compose, production setup
### Key External Resources
- [Laravel 11 Documentation](https://laravel.com/docs/11.x)
- [Pest PHP](https://pestphp.com/)
- [Laravel Horizon](https://laravel.com/docs/11.x/horizon) - Queue monitoring
- [Laravel Telescope](https://laravel.com/docs/11.x/telescope) - Local debugging and request/query monitoring
- [Laravel Octane](https://laravel.com/docs/11.x/octane) - High-performance serving
- [Laravel Forge](https://forge.laravel.com/) - Server management
- [Laravel Vapor](https://vapor.laravel.com/) - Serverless deployment
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!