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-array-validation.md

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

Array and Nested Validation

Impact: HIGH (Validates complex nested data structures)

Validate arrays and nested data structures with Laravel's powerful array validation syntax.

Bad Example

// Manual array validation
class OrderController extends Controller
{
    public function store(Request $request)
    {
        // No proper array validation
        $items = $request->items;

        if (!is_array($items) || empty($items)) {
            return back()->withErrors(['items' => 'Items required']);
        }

        foreach ($items as $index => $item) {
            if (empty($item['product_id'])) {
                return back()->withErrors(["items.{$index}.product_id" => 'Product required']);
            }
            if (!is_numeric($item['quantity']) || $item['quantity'] < 1) {
                return back()->withErrors(["items.{$index}.quantity" => 'Invalid quantity']);
            }
        }

        // Prone to errors, hard to maintain
    }
}

Good Example

// Proper array validation
class StoreOrderRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            // Array validation
            'items' => ['required', 'array', 'min:1', 'max:50'],

            // Validate each item in the array
            'items.*' => ['required', 'array'],
            'items.*.product_id' => ['required', 'integer', 'exists:products,id'],
            'items.*.quantity' => ['required', 'integer', 'min:1', 'max:100'],
            'items.*.notes' => ['nullable', 'string', 'max:500'],

            // Nested arrays
            'items.*.options' => ['nullable', 'array'],
            'items.*.options.*' => ['string', 'max:100'],

            // Shipping address as object
            'shipping' => ['required', 'array'],
            'shipping.street' => ['required', 'string', 'max:255'],
            'shipping.city' => ['required', 'string', 'max:100'],
            'shipping.state' => ['required', 'string', 'size:2'],
            'shipping.zip' => ['required', 'string', 'regex:/^\d{5}(-\d{4})?$/'],
            'shipping.country' => ['required', 'string', 'size:2'],

            // Optional billing address
            'billing' => ['nullable', 'array'],
            'billing.street' => ['required_with:billing', 'string', 'max:255'],
            'billing.city' => ['required_with:billing', 'string', 'max:100'],

            // Tags array
            'tags' => ['nullable', 'array', 'max:10'],
            'tags.*' => ['string', 'max:50', 'distinct'],

            // Key-value metadata
            'metadata' => ['nullable', 'array'],
            'metadata.*.key' => ['required', 'string', 'max:50'],
            'metadata.*.value' => ['required', 'string', 'max:255'],
        ];
    }

    public function messages(): array
    {
        return [
            'items.required' => 'Please add at least one item to your order.',
            'items.min' => 'Your order must contain at least one item.',
            'items.*.product_id.required' => 'Each item must have a product selected.',
            'items.*.product_id.exists' => 'Item #:position has an invalid product.',
            'items.*.quantity.min' => 'Item #:position quantity must be at least 1.',
            'tags.*.distinct' => 'Duplicate tags are not allowed.',
        ];
    }
}
// Validating array keys
class ConfigRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            // Only allow specific keys
            'settings' => ['required', 'array'],
            'settings.theme' => ['required', Rule::in(['light', 'dark', 'auto'])],
            'settings.language' => ['required', 'string', 'size:2'],
            'settings.notifications' => ['required', 'array'],
            'settings.notifications.email' => ['required', 'boolean'],
            'settings.notifications.push' => ['required', 'boolean'],
            'settings.notifications.sms' => ['required', 'boolean'],
        ];
    }
}
// Dynamic array keys validation
class DynamicFormRequest extends FormRequest
{
    public function rules(): array
    {
        $rules = [
            'fields' => ['required', 'array'],
        ];

        // Add rules for each field dynamically
        foreach ($this->input('fields', []) as $key => $value) {
            if (str_starts_with($key, 'email_')) {
                $rules["fields.{$key}"] = ['required', 'email'];
            } elseif (str_starts_with($key, 'phone_')) {
                $rules["fields.{$key}"] = ['required', 'string', 'min:10'];
            }
        }

        return $rules;
    }
}
// Validating file arrays
class GalleryUploadRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'images' => ['required', 'array', 'min:1', 'max:10'],
            'images.*' => ['required', 'image', 'mimes:jpg,png,webp', 'max:5120'],

            'documents' => ['nullable', 'array'],
            'documents.*' => ['file', 'mimes:pdf,doc,docx', 'max:10240'],

            // With metadata for each file
            'files' => ['required', 'array'],
            'files.*.file' => ['required', 'file', 'max:10240'],
            'files.*.title' => ['required', 'string', 'max:100'],
            'files.*.description' => ['nullable', 'string', 'max:500'],
        ];
    }
}
// Custom array validation rule
namespace App\Rules;

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

class UniqueArrayValues implements ValidationRule
{
    public function __construct(
        private string $key
    ) {}

    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        if (!is_array($value)) {
            return;
        }

        $values = collect($value)->pluck($this->key);

        if ($values->count() !== $values->unique()->count()) {
            $fail("The {$attribute} contains duplicate {$this->key} values.");
        }
    }
}
// Usage
'items' => ['required', 'array', new UniqueArrayValues('product_id')],

Why

  • Type safety: Ensures arrays contain expected data types
  • Nested validation: Deep validation of complex structures
  • Custom messages: Clear error messages with array positions
  • Consistency: Same validation syntax for all array types
  • Security: Prevents unexpected data from entering the system
  • Documentation: Rules document expected data structure

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