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-form-requests.md

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

Form Requests in Controllers

Impact: HIGH (Separates validation logic from controllers)

Move validation and authorization logic from controllers to dedicated Form Request classes.

Bad Example

// Validation logic cluttering the controller
class ArticleController extends Controller
{
    public function store(Request $request)
    {
        // Validation in controller
        $validated = $request->validate([
            'title' => 'required|string|max:255|unique:articles,title',
            'slug' => 'required|string|max:255|unique:articles,slug',
            'body' => 'required|string|min:100',
            'category_id' => 'required|exists:categories,id',
            'tags' => 'array',
            'tags.*' => 'exists:tags,id',
            'published_at' => 'nullable|date|after:now',
            'featured_image' => 'nullable|image|max:2048',
        ]);

        // Authorization mixed in
        if (!auth()->user()->can('create', Article::class)) {
            abort(403);
        }

        $article = Article::create($validated);

        return redirect()->route('articles.show', $article);
    }

    public function update(Request $request, Article $article)
    {
        // Same validation duplicated
        $validated = $request->validate([
            'title' => 'required|string|max:255|unique:articles,title,' . $article->id,
            'slug' => 'required|string|max:255|unique:articles,slug,' . $article->id,
            'body' => 'required|string|min:100',
            'category_id' => 'required|exists:categories,id',
            'tags' => 'array',
            'tags.*' => 'exists:tags,id',
            'published_at' => 'nullable|date',
            'featured_image' => 'nullable|image|max:2048',
        ]);

        $article->update($validated);

        return redirect()->route('articles.show', $article);
    }
}

Good Example

// Store request
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Str;

class StoreArticleRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     */
    public function authorize(): bool
    {
        return $this->user()->can('create', Article::class);
    }

    /**
     * Get the validation rules that apply to the request.
     */
    public function rules(): array
    {
        return [
            'title' => ['required', 'string', 'max:255', 'unique:articles,title'],
            'slug' => ['required', 'string', 'max:255', 'unique:articles,slug'],
            'body' => ['required', 'string', 'min:100'],
            'category_id' => ['required', 'exists:categories,id'],
            'tags' => ['array'],
            'tags.*' => ['exists:tags,id'],
            'published_at' => ['nullable', 'date', 'after:now'],
            'featured_image' => ['nullable', 'image', 'max:2048'],
        ];
    }

    /**
     * Get custom attributes for validator errors.
     */
    public function attributes(): array
    {
        return [
            'category_id' => 'category',
            'published_at' => 'publication date',
        ];
    }

    /**
     * Get custom messages for validator errors.
     */
    public function messages(): array
    {
        return [
            'title.unique' => 'An article with this title already exists.',
            'body.min' => 'The article body must be at least :min characters.',
        ];
    }

    /**
     * Prepare the data for validation.
     */
    protected function prepareForValidation(): void
    {
        $this->merge([
            'slug' => $this->slug ?? Str::slug($this->title),
        ]);
    }

    /**
     * Handle a passed validation attempt.
     */
    protected function passedValidation(): void
    {
        // Called after validation passes — use for side effects, not data modification
        // To add extra fields like user_id, do so in the controller:
        // Article::create([...$request->validated(), 'user_id' => $request->user()->id])
    }
}
// Update request extending store
namespace App\Http\Requests;

class UpdateArticleRequest extends StoreArticleRequest
{
    public function authorize(): bool
    {
        return $this->user()->can('update', $this->route('article'));
    }

    public function rules(): array
    {
        $article = $this->route('article');

        return [
            ...parent::rules(),
            'title' => ['required', 'string', 'max:255', "unique:articles,title,{$article->id}"],
            'slug' => ['required', 'string', 'max:255', "unique:articles,slug,{$article->id}"],
            'published_at' => ['nullable', 'date'], // Remove 'after:now' for updates
        ];
    }
}
// Clean controller
class ArticleController extends Controller
{
    public function store(StoreArticleRequest $request)
    {
        $article = Article::create($request->validated());

        return redirect()->route('articles.show', $article)
            ->with('success', 'Article created successfully');
    }

    public function update(UpdateArticleRequest $request, Article $article)
    {
        $article->update($request->validated());

        return redirect()->route('articles.show', $article)
            ->with('success', 'Article updated successfully');
    }
}
# Generate form request
php artisan make:request StoreArticleRequest

Why

  • Separation of concerns: Controllers handle HTTP, requests handle validation
  • Reusability: Same validation rules in multiple places
  • Testability: Validation logic tested independently
  • Authorization: Access control in one place
  • Custom messages: User-friendly error messages
  • Data preparation: Transform input before validation
  • Thin controllers: Controllers stay focused on their job

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