# Developing With Laravel

> 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. Use when this capability is needed.

- Skill: `tomevault-io/developing-with-laravel` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/developing-with-laravel`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/developing-with-laravel/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/developing-with-laravel

---


# 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](./REFERENCE.md).

---

## Table of Contents

1. [Project Structure](#project-structure)
2. [Routing & Controllers](#routing--controllers)
3. [Eloquent ORM](#eloquent-orm)
4. [Validation](#validation)
5. [Middleware](#middleware)
6. [Authentication](#authentication)
7. [Artisan CLI](#artisan-cli)
8. [Queues & Jobs](#queues--jobs)
9. [Events & Listeners](#events--listeners)
10. [Testing](#testing)
11. [Service Providers](#service-providers)
12. [Task Scheduling](#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

```php
// 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

```php
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

```php
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](./REFERENCE.md#2-eloquent-advanced-patterns)

### Query Builder

```php
// 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

```php
// 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

```php
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](./REFERENCE.md#4-validation-advanced)

---

## Middleware

```php
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](./REFERENCE.md#7-multi-tenancy-patterns)

---

## Authentication

### Sanctum (API Tokens)

```php
// 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](./REFERENCE.md#8-authentication-advanced)

---

## Artisan CLI

### Custom Command

```php
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](./REFERENCE.md#6-artisan-cli-advanced)

---

## Queues & Jobs

### Job Definition

```php
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

```php
// 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](./REFERENCE.md#4-queue-system-advanced)

---

## Events & Listeners

```php
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

```php
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

```php
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](./REFERENCE.md#11-testing-advanced)

---

## Service Providers

```php
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](./REFERENCE.md#1-service-container--dependency-injection)

---

## Task Scheduling

```php
// 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](./REFERENCE.md)**

---
> Converted and distributed by [TomeVault](https://tomevault.io/claim/fortiumpartners) — claim your Tome and manage your conversions.
<!-- tomevault:4.0:skill_md:2026-04-13 -->

