Agent skill
laravel
Laravel 11+ framework guardrails, patterns, and best practices for AI-assisted development. Use when working with Laravel projects, or when the user mentions Laravel. Provides Eloquent ORM, Blade templates, routing, middleware, and queue guidelines.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/laravel
Metadata
Additional technical details for this skill
- author
- samuel
- version
- 1.0
- category
- framework
- language
- php
- extensions
- .php
SKILL.md
Laravel Guide
Applies to: Laravel 11+, PHP 8.2+, Full-Stack Web Applications, APIs, SaaS
Core Principles
- Convention Over Configuration: Follow Laravel's conventions for file placement, naming, and structure
- Service Layer: Keep controllers thin, extract business logic into service classes
- Form Requests: Validate all input through dedicated Form Request classes
- Eloquent Relationships: Use eager loading to prevent N+1 queries
- Queue Everything Slow: Offload email, notifications, and heavy processing to queues
Guardrails
Version & Dependencies
- Use Laravel 11+ with PHP 8.2+
- Run
composer install --no-devfor production - Use Laravel Pint for code formatting (ships with Laravel)
- Use PHPStan or Larastan for static analysis
- Lock dependency versions in
composer.lock
Code Style
- Always use
declare(strict_types=1);at the top of every PHP file - Use constructor property promotion for dependency injection
- Use typed properties and return types on all methods
- Follow PSR-12 coding standard (enforced by Pint)
- Use
readonlyproperties where values should not change after construction
Security
- Never use
DB::raw()without parameter binding - Always validate input through Form Requests, never in controllers
- Use Sanctum for API authentication, not plain tokens
- Never commit
.envfiles; use.env.exampleas template - Use
$fillable(whitelist) on models, never$guarded = [] - Escape Blade output with
{{ }}(not{!! !!}unless explicitly safe) - Use CSRF protection on all web routes (enabled by default)
Performance
- Always eager load relationships:
->with('relation')or->load('relation') - Use pagination for list endpoints:
->paginate(15)or->cursorPaginate(15) - Cache expensive queries with
Cache::remember() - Use database indexes on columns used in
WHERE,ORDER BY,JOIN - Queue emails, notifications, and any operation >200ms
Project Structure
myapp/
├── app/
│ ├── Console/Commands/ # Artisan commands
│ ├── Enums/ # PHP enums (roles, statuses)
│ ├── Events/ # Domain events
│ ├── Http/
│ │ ├── Controllers/ # Thin controllers (delegate to services)
│ │ ├── Middleware/ # HTTP middleware
│ │ └── Requests/ # Form Request validation
│ ├── Jobs/ # Queued jobs
│ ├── Listeners/ # Event listeners
│ ├── Models/ # Eloquent models
│ ├── Policies/ # Authorization policies
│ ├── Providers/ # Service providers
│ └── Services/ # Business logic layer
├── config/ # Configuration files
├── database/
│ ├── factories/ # Model factories for testing
│ ├── migrations/ # Always include down() for rollback
│ └── seeders/ # Database seeders
├── resources/views/ # Blade templates
├── routes/
│ ├── api.php # API routes (versioned: /api/v1/)
│ └── web.php # Web routes
├── tests/
│ ├── Feature/ # Integration/HTTP tests
│ └── Unit/ # Isolated unit tests
├── .env.example
├── composer.json
└── phpunit.xml
Eloquent Models
Defining Models
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class Post extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = [
'title',
'slug',
'body',
'status',
'user_id',
];
protected $casts = [
'published_at' => 'datetime',
'is_featured' => 'boolean',
];
// -- Relationships --
public function author(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id');
}
public function comments(): HasMany
{
return $this->hasMany(Comment::class);
}
// -- Scopes --
public function scopePublished($query)
{
return $query->where('status', 'published')
->whereNotNull('published_at');
}
// -- Accessors (Laravel 11 attribute casting) --
public function getExcerptAttribute(): string
{
return str()->limit($this->body, 150);
}
}
Key Eloquent Rules
- Always declare
$fillable(whitelist) for mass assignment protection - Use
$castsfor type casting (datetime, boolean, enum, array, json) - Type-hint relationship return types (
HasMany,BelongsTo, etc.) - Use SoftDeletes when data should be recoverable
- Name scopes descriptively:
scopeActive,scopePublished,scopeByRole
Routing
Web Routes
<?php
// routes/web.php
use App\Http\Controllers\PostController;
use Illuminate\Support\Facades\Route;
Route::get('/', fn () => view('welcome'));
Route::middleware('auth')->group(function () {
Route::resource('posts', PostController::class);
});
API Routes
<?php
// routes/api.php
use App\Http\Controllers\Api\PostController;
use Illuminate\Support\Facades\Route;
Route::prefix('v1')->group(function () {
Route::middleware('auth:sanctum')->group(function () {
Route::apiResource('posts', PostController::class);
});
});
Routing Rules
- Group routes with shared middleware using
Route::middleware()->group() - Use
apiResourcefor API controllers (excludescreate/editviews) - Version API routes with prefix:
/api/v1/... - Use route model binding for automatic model resolution
- Name routes explicitly when needed:
->name('posts.publish')
Controllers
Controller Pattern (Thin Controller)
<?php
declare(strict_types=1);
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Requests\StorePostRequest;
use App\Http\Resources\PostResource;
use App\Models\Post;
use App\Services\PostService;
use Illuminate\Http\JsonResponse;
class PostController extends Controller
{
public function __construct(
private readonly PostService $postService,
) {}
public function index(): PostResource
{
return PostResource::collection(
Post::with('author')->published()->paginate(15)
);
}
public function store(StorePostRequest $request): JsonResponse
{
$post = $this->postService->create($request->validated());
return PostResource::make($post)->response()->setStatusCode(201);
}
public function show(Post $post): PostResource
{
return PostResource::make($post->load('author', 'comments'));
}
public function destroy(Post $post): JsonResponse
{
$this->authorize('delete', $post);
$this->postService->delete($post);
return response()->json(null, 204);
}
}
Controller Rules
- Delegate business logic to Service classes (thin controllers)
- Use constructor injection with
readonlyfor dependencies - Use Form Requests for validation, API Resources for responses
- Use
$this->authorize()or Policy middleware for authorization - One resource per controller; avoid custom methods beyond CRUD
Blade Templates
Component Pattern
{{-- resources/views/components/alert.blade.php --}}
@props(['type' => 'info', 'dismissible' => false])
<div {{ $attributes->merge(['class' => "alert alert-{$type}"]) }}>
{{ $slot }}
@if($dismissible)
<button type="button" class="close">×</button>
@endif
</div>
Blade Rules
- Use
{{ }}for escaped output (default);{!! !!}only for trusted HTML - Use
@propsin components for typed attributes with defaults - Use
@stack/@pushfor page-specific CSS/JS - Use
@includefor partials,<x-component>for reusable components - Use
@vite()for asset bundling (replaces Laravel Mix) - Never put business logic in Blade templates
Form Request Validation
<?php
declare(strict_types=1);
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class StorePostRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user()->can('create', Post::class);
}
/** @return array<string, mixed> */
public function rules(): array
{
return [
'title' => ['required', 'string', 'max:255'],
'slug' => ['required', 'string', 'max:255', Rule::unique('posts')],
'body' => ['required', 'string', 'min:10'],
'status' => ['required', Rule::in(['draft', 'published'])],
];
}
}
Middleware
<?php
declare(strict_types=1);
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class EnsureUserHasRole
{
public function handle(Request $request, Closure $next, string $role): Response
{
if ($request->user()?->role !== $role) {
abort(403, "Role '{$role}' is required.");
}
return $next($request);
}
}
Register in bootstrap/app.php (Laravel 11+):
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'role' => \App\Http\Middleware\EnsureUserHasRole::class,
]);
})
Artisan Commands
# Scaffolding
php artisan make:model Post -mfcs # Model + migration + factory + controller + seeder
php artisan make:controller Api/PostController --api
php artisan make:request StorePostRequest
php artisan make:resource PostResource
php artisan make:event PostPublished
php artisan make:job ProcessReport
php artisan make:policy PostPolicy --model=Post
# Database
php artisan migrate # Run pending migrations
php artisan migrate:fresh --seed # Reset DB and seed (dev only)
# Cache & Optimization
php artisan optimize # Cache config, routes, views
php artisan optimize:clear # Clear all caches (dev)
# Queue & Testing
php artisan queue:work # Start queue worker
php artisan test # Run PHPUnit
php artisan test --filter=PostTest # Run specific test
php artisan test --coverage # With coverage report
Testing
Feature Test Example
<?php
declare(strict_types=1);
namespace Tests\Feature;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class PostControllerTest extends TestCase
{
use RefreshDatabase;
public function test_authenticated_user_can_create_post(): void
{
$user = User::factory()->create();
$response = $this->actingAs($user)
->postJson('/api/v1/posts', [
'title' => 'My Post',
'slug' => 'my-post',
'body' => 'Post content here.',
'status' => 'draft',
]);
$response->assertCreated()
->assertJsonPath('data.title', 'My Post');
$this->assertDatabaseHas('posts', ['slug' => 'my-post']);
}
public function test_guest_cannot_create_post(): void
{
$this->postJson('/api/v1/posts', ['title' => 'My Post'])
->assertUnauthorized();
}
}
Testing Rules
- Use
RefreshDatabasetrait for database tests - Use model factories for test data (never insert raw SQL)
- Test happy paths, validation failures, and authorization
- Name tests descriptively:
test_guest_cannot_delete_post - Use
actingAs()for authenticated requests - Assert both HTTP status and database state
- See references/patterns.md for factory patterns and advanced testing
Migrations
<?php
declare(strict_types=1);
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('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('title');
$table->string('slug')->unique();
$table->text('body');
$table->string('status')->default('draft');
$table->timestamp('published_at')->nullable();
$table->timestamps();
$table->softDeletes();
$table->index(['status', 'published_at']);
});
}
public function down(): void
{
Schema::dropIfExists('posts');
}
};
- Always include
down()for rollback - Use
foreignId()->constrained()for foreign keys with cascading - Add indexes on columns used in
WHERE,ORDER BY - Never modify a deployed migration; create a new one instead
Advanced Topics
For detailed patterns and examples, see:
- references/patterns.md -- Eloquent patterns, jobs/queues, events, Livewire, API resources, deployment
External References
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?