Claude
Skills
Sign in
Back

developing-with-laravel

Included with Lifetime
$97 forever

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.

General

What this skill does


# 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 

Related in General