laravel
Laravel 12
Opinionated PHP framework with expressive syntax and well-defined conventions.
Project Structure
app/
├── Http/
│ ├── Controllers/ # Request handlers
│ ├── Middleware/ # HTTP middleware
│ └── Requests/ # Form request validation
├── Models/ # Eloquent models
├── Policies/ # Authorization policies
├── Jobs/ # Queueable jobs
├── Events/ # Event classes
├── Listeners/ # Event listeners
├── Mail/ # Mailable classes
└── Providers/ # Service providers
config/ # Configuration files
database/
├── migrations/ # Database migrations
├── factories/ # Model factories
└── seeders/ # Database seeders
resources/
├── views/ # Blade templates
└── css/js # Frontend assets
routes/
├── web.php # Web routes
├── api.php # API routes
└── console.php # Artisan commands
Artisan Commands
# Models
php artisan make:model Post -mfsc # Model + migration, factory, seeder, controller
php artisan make:model Post --all # All resources
# Controllers
php artisan make:controller PostController --resource # CRUD controller
php artisan make:controller PostController --api # API controller
php artisan make:controller ShowPost --invokable # Single action
# Other
php artisan make:migration create_posts_table
php artisan make:request StorePostRequest
php artisan make:policy PostPolicy --model=Post
php artisan make:job ProcessPodcast
php artisan make:event OrderShipped
php artisan make:listener SendShipmentNotification
Controllers
Resource Controller
class PostController extends Controller
{
public function index(): View
{
return view('posts.index', ['posts' => Post::latest()->paginate()]);
}
public function store(StorePostRequest $request): RedirectResponse
{
Post::create($request->validated());
return redirect()->route('posts.index');
}
public function show(Post $post): View // Route model binding
{
return view('posts.show', compact('post'));
}
public function update(UpdatePostRequest $request, Post $post): RedirectResponse
{
$post->update($request->validated());
return redirect()->route('posts.show', $post);
}
public function destroy(Post $post): RedirectResponse
{
$post->delete();
return redirect()->route('posts.index');
}
}
Single Action Controller
class ShowDashboard extends Controller
{
public function __invoke(): View
{
return view('dashboard');
}
}
Routing
// Resource routes
Route::resource('posts', PostController::class);
Route::apiResource('posts', PostController::class); // API (no create/edit)
// Single routes
Route::get('/dashboard', ShowDashboard::class)->name('dashboard');
Route::get('/users/{user}', [UserController::class, 'show'])->name('users.show');
// Route groups
Route::middleware(['auth'])->group(function () {
Route::get('/profile', [ProfileController::class, 'edit']);
});
// Livewire pages
Route::livewire('/posts', 'pages::posts.index');
Eloquent Models
class Post extends Model
{
protected $fillable = ['title', 'content', 'user_id'];
protected $casts = [
'published_at' => 'datetime',
'is_featured' => 'boolean',
];
// Relationships
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function comments(): HasMany
{
return $this->hasMany(Comment::class);
}
public function tags(): BelongsToMany
{
return $this->belongsToMany(Tag::class);
}
// Scopes
public function scopePublished(Builder $query): Builder
{
return $query->whereNotNull('published_at');
}
}
Query Patterns
// Eager loading (prevent N+1)
$posts = Post::with(['user', 'comments'])->get();
// Chunking large datasets
Post::chunk(100, function ($posts) {
foreach ($posts as $post) { /* ... */ }
});
// Upserts
Post::upsert([
['id' => 1, 'title' => 'Updated'],
['id' => 2, 'title' => 'New'],
], ['id'], ['title']);
Form Requests
class StorePostRequest extends FormRequest
{
public function authorize(): bool
{
return true; // Or use policies
}
public function rules(): array
{
return [
'title' => ['required', 'string', 'max:255'],
'content' => ['required', 'string'],
'published_at' => ['nullable', 'date'],
];
}
}
Migrations
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->text('content');
$table->timestamp('published_at')->nullable();
$table->timestamps();
$table->index('published_at');
});
}
public function down(): void
{
Schema::dropIfExists('posts');
}
};
Authorization (Policies)
class PostPolicy
{
public function update(User $user, Post $post): bool
{
return $user->id === $post->user_id;
}
public function delete(User $user, Post $post): bool
{
return $user->id === $post->user_id || $user->is_admin;
}
}
// Usage in controller
$this->authorize('update', $post);
// Usage in Blade
@can('update', $post) ... @endcan
Best Practices
- Use Form Requests for validation, not controller logic
- Eager load relationships to prevent N+1 queries
- Use policies for authorization, not controller checks
- Type-hint dependencies for automatic injection
- Use route model binding instead of manual lookups
- Keep controllers thin — move business logic to services/actions
- Use queued jobs for slow operations (email, API calls)
Laravel Boost
For AI-powered development, install Laravel Boost:
composer require laravel/boost --dev
php artisan boost:install
Provides MCP tools for inspecting routes, database, config, and more.
References
For detailed docs, see references/:
controllers.md— Controller patternseloquent.md— Model conventionsrouting.md— Route definitions
More from freekmurze/dotfiles
context7-auto-research
Automatically fetches up-to-date documentation from Context7 when users ask about libraries, frameworks, APIs, or need code examples. Triggers proactively without explicit user request.
27copy-editing
When the user wants to edit, review, or improve existing marketing copy. Also use when the user mentions 'edit this copy,' 'review my copy,' 'copy feedback,' 'proofread,' 'polish this,' 'make this better,' or 'copy sweep.' This skill provides a systematic approach to editing marketing copy through multiple focused passes.
24web-design-guidelines
Review UI code for Web Interface Guidelines compliance. Use when asked to "review my UI", "check accessibility", "audit design", "review UX", or "check my site against best practices".
20pdf
Comprehensive PDF manipulation toolkit for extracting text and tables, creating new PDFs, merging/splitting documents, and handling forms. When Claude needs to fill in a PDF form or programmatically process, generate, or analyze PDF documents at scale.
20building-native-ui
Complete guide for building beautiful apps with Expo Router. Covers fundamentals, styling, components, navigation, animations, patterns, and native tabs.
20ray-skill
Use when user says "send to Ray," "show in Ray," "debug in Ray," "log to Ray," "display in Ray," or wants to visualize data, debug output, or show diagrams in the Ray desktop application.
18