All skills
asyrafhussin avatar

/laravel-best-practices

@ef59186

Laravel 13 conventions and best practices. Use when creating controllers, models, migrations, validation, services, or structuring Laravel applications. Triggers on tasks involving Laravel architecture, Eloquent, database, API development, or PHP patterns.

Use this Skill: https://skilld.dev/gh/asyrafhussin/agent-skills/laravel-best-practices

This session only. Nothing lands on disk.

rulescontroller-resource-controllers.md

≈1.5k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Use Resource Controllers

Impact: HIGH (RESTful conventions and consistent routing)

Why It Matters

Resource controllers provide a consistent, RESTful structure for CRUD operations. They follow Laravel conventions, making code predictable and easier for other developers to understand.

Bad Example

// Inconsistent naming and structure
Route::get('/posts', [PostController::class, 'getAllPosts']);
Route::get('/posts/{id}', [PostController::class, 'getPost']);
Route::post('/posts/new', [PostController::class, 'createPost']);
Route::put('/posts/{id}/edit', [PostController::class, 'updatePost']);
Route::delete('/posts/{id}/delete', [PostController::class, 'removePost']);

// Non-standard controller methods
class PostController extends Controller
{
    public function getAllPosts() { }
    public function getPost($id) { }
    public function createPost() { }
    public function updatePost($id) { }
    public function removePost($id) { }
}

Good Example

Resource Route

// Single line defines all 7 RESTful routes
Route::resource('posts', PostController::class);

// Generated routes:
// GET    /posts              index    posts.index
// GET    /posts/create       create   posts.create
// POST   /posts              store    posts.store
// GET    /posts/{post}       show     posts.show
// GET    /posts/{post}/edit  edit     posts.edit
// PUT    /posts/{post}       update   posts.update
// DELETE /posts/{post}       destroy  posts.destroy

Resource Controller

<?php

namespace App\Http\Controllers;

use App\Http\Requests\StorePostRequest;
use App\Http\Requests\UpdatePostRequest;
use App\Models\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;

class PostController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index(): View
    {
        $posts = Post::with('author')
            ->latest()
            ->paginate(15);

        return view('posts.index', compact('posts'));
    }

    /**
     * Show the form for creating a new resource.
     */
    public function create(): View
    {
        $categories = Category::all();

        return view('posts.create', compact('categories'));
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(StorePostRequest $request): RedirectResponse
    {
        $post = Post::create([
            ...$request->validated(),
            'user_id' => auth()->id(),
        ]);

        return redirect()
            ->route('posts.show', $post)
            ->with('success', 'Post created successfully.');
    }

    /**
     * Display the specified resource.
     */
    public function show(Post $post): View
    {
        $post->load(['author', 'comments.user', 'tags']);

        return view('posts.show', compact('post'));
    }

    /**
     * Show the form for editing the specified resource.
     */
    public function edit(Post $post): View
    {
        $this->authorize('update', $post);

        $categories = Category::all();

        return view('posts.edit', compact('post', 'categories'));
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(UpdatePostRequest $request, Post $post): RedirectResponse
    {
        $post->update($request->validated());

        return redirect()
            ->route('posts.show', $post)
            ->with('success', 'Post updated successfully.');
    }

    /**
     * Remove the specified resource from storage.
     */
    public function destroy(Post $post): RedirectResponse
    {
        $this->authorize('delete', $post);

        $post->delete();

        return redirect()
            ->route('posts.index')
            ->with('success', 'Post deleted successfully.');
    }
}

Partial Resource Routes

// Only specific actions
Route::resource('posts', PostController::class)
    ->only(['index', 'show']);

// All except specific actions
Route::resource('posts', PostController::class)
    ->except(['destroy']);

API Resource Controller

// API routes (no create/edit - those are for forms)
Route::apiResource('posts', Api\PostController::class);

// Generated routes:
// GET    /posts          index
// POST   /posts          store
// GET    /posts/{post}   show
// PUT    /posts/{post}   update
// DELETE /posts/{post}   destroy

Nested Resources

// Nested resource routes
Route::resource('posts.comments', CommentController::class);

// Generated: /posts/{post}/comments/{comment}

// Shallow nesting (recommended)
Route::resource('posts.comments', CommentController::class)->shallow();

// Generated:
// /posts/{post}/comments       (index, store)
// /comments/{comment}          (show, update, destroy)

Generate Resource Controller

# Generate with all methods
php artisan make:controller PostController --resource

# Generate with model binding
php artisan make:controller PostController --resource --model=Post

# Generate API controller
php artisan make:controller Api/PostController --api --model=Post

Route Model Binding

// Automatic model binding - Laravel resolves Post from {post}
public function show(Post $post): View
{
    // $post is automatically fetched or 404
    return view('posts.show', compact('post'));
}

// Custom binding key
// Route: /posts/{post:slug}
public function show(Post $post): View
{
    // Resolved by slug instead of id
}

// In model
class Post extends Model
{
    public function getRouteKeyName(): string
    {
        return 'slug';
    }
}

Benefits

  • Consistent URL structure
  • Predictable controller methods
  • Automatic route model binding
  • Easy to generate views (posts.index, posts.show, etc.)
  • Clear conventions for team collaboration

Source: SKILL.md on GitHub

No alerts16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The skill is a comprehensive guide to Laravel 13 best practices, providing safe and standard architectural, database, and security patterns for PHP development. No malicious behavior or security risks were detected.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer6mo

    1/37 files flagged

  • ZeroLeaks5mo

    2 findings · Score: 80/100

Signed by skilld at ef59186. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub last month.

Steadyupdated 7 months ago
Other metadata
metadata
{
  "author": "Laravel Community",
  "version": "2.1.0",
  "laravelVersion": "13.x",
  "phpVersion": "8.3+"
}

README badge

README badge for asyrafhussin/agent-skills/laravel-best-practices