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.

rulesvalidation-custom-rules.md

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

Custom Validation Rules

Impact: HIGH (Reusable domain-specific validation)

Create reusable custom validation rules for complex or domain-specific validation logic.

Bad Example

// Complex validation logic in controller
class UserController extends Controller
{
    public function store(Request $request)
    {
        $validated = $request->validate([
            'password' => [
                'required',
                'string',
                'min:8',
                function ($attribute, $value, $fail) {
                    // Complex password rules inline
                    if (!preg_match('/[A-Z]/', $value)) {
                        $fail('Password must contain uppercase letter.');
                    }
                    if (!preg_match('/[a-z]/', $value)) {
                        $fail('Password must contain lowercase letter.');
                    }
                    if (!preg_match('/[0-9]/', $value)) {
                        $fail('Password must contain a number.');
                    }
                    if (!preg_match('/[@$!%*#?&]/', $value)) {
                        $fail('Password must contain a special character.');
                    }
                    if (preg_match('/(.)\1{2,}/', $value)) {
                        $fail('Password cannot contain 3+ repeated characters.');
                    }
                },
            ],
            'phone' => [
                'required',
                function ($attribute, $value, $fail) {
                    // Phone validation duplicated everywhere
                    $cleaned = preg_replace('/[^0-9]/', '', $value);
                    if (strlen($cleaned) < 10 || strlen($cleaned) > 15) {
                        $fail('Invalid phone number.');
                    }
                },
            ],
        ]);
    }
}

Good Example

// Custom rule class for strong password
namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;

class StrongPassword implements ValidationRule
{
    public function __construct(
        private bool $requireUppercase = true,
        private bool $requireNumber = true,
        private bool $requireSpecialChar = true,
        private int $minLength = 8,
    ) {}

    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        if (strlen($value) < $this->minLength) {
            $fail("The :attribute must be at least {$this->minLength} characters.");
            return;
        }

        if ($this->requireUppercase && !preg_match('/[A-Z]/', $value)) {
            $fail('The :attribute must contain at least one uppercase letter.');
        }

        if (!preg_match('/[a-z]/', $value)) {
            $fail('The :attribute must contain at least one lowercase letter.');
        }

        if ($this->requireNumber && !preg_match('/[0-9]/', $value)) {
            $fail('The :attribute must contain at least one number.');
        }

        if ($this->requireSpecialChar && !preg_match('/[@$!%*#?&]/', $value)) {
            $fail('The :attribute must contain at least one special character.');
        }

        if (preg_match('/(.)\1{2,}/', $value)) {
            $fail('The :attribute cannot contain 3 or more repeated characters.');
        }
    }
}
// Phone number validation rule
namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;

class ValidPhoneNumber implements ValidationRule
{
    public function __construct(
        private ?string $countryCode = null
    ) {}

    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        $cleaned = preg_replace('/[^0-9+]/', '', $value);

        if (strlen($cleaned) < 10 || strlen($cleaned) > 15) {
            $fail('The :attribute must be a valid phone number.');
            return;
        }

        if ($this->countryCode === 'US' && !preg_match('/^(\+1)?[0-9]{10}$/', $cleaned)) {
            $fail('The :attribute must be a valid US phone number.');
        }
    }
}
// Rule with database check
namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Facades\DB;

class UniqueForUser implements ValidationRule
{
    public function __construct(
        private string $table,
        private string $column,
        private ?int $ignoreId = null,
    ) {}

    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        $query = DB::table($this->table)
            ->where($this->column, $value)
            ->where('user_id', auth()->id());

        if ($this->ignoreId) {
            $query->where('id', '!=', $this->ignoreId);
        }

        if ($query->exists()) {
            $fail("You already have a record with this :attribute.");
        }
    }
}
// Invokable rule for simple cases
namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;

class Uppercase implements ValidationRule
{
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        if (strtoupper($value) !== $value) {
            $fail('The :attribute must be uppercase.');
        }
    }
}
// Using custom rules in Form Request
class StoreUserRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'email', 'unique:users,email'],
            'password' => [
                'required',
                'confirmed',
                new StrongPassword(
                    requireSpecialChar: true,
                    minLength: 12
                ),
            ],
            'phone' => ['required', new ValidPhoneNumber('US')],
            'nickname' => [
                'nullable',
                'string',
                new UniqueForUser('profiles', 'nickname'),
            ],
        ];
    }
}
// Register as a fluent macro (optional)
// In AppServiceProvider
// Or use Rule::macro for fluent syntax
Rule::macro('strongPassword', function () {
    return new StrongPassword();
});

// Usage: 'password' => ['required', Rule::strongPassword()]

Why

  • Reusability: Same validation logic used across the application
  • Testability: Custom rules can be unit tested
  • Encapsulation: Complex logic hidden behind simple interface
  • Readability: Clean, descriptive rule names
  • Maintainability: Change validation logic in one place
  • Documentation: Rule classes document validation requirements

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