### Documenting Testing Framework Choice in README Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/testing/stick-to-one-testing-framework.md Markdown example for documenting the chosen testing framework in a project's README or CONTRIBUTING file. ```markdown # Testing This project uses **Pest** for all tests. Please refer to the [Pest documentation](https://pest-framework.com/) for more information. ``` -------------------------------- ### Example of Consistent PHPUnit Test Suite Structure Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/testing/stick-to-one-testing-framework.md Shows a project directory structure with only PHPUnit test files, representing a consistent testing approach. ```text tests/ ├── Feature/ │ ├── UserRegistrationTest.php │ ├── CheckoutTest.php │ └── ProfileTest.php └── Unit/ ├── DiscountCalculatorTest.php └── OrderTest.php ``` -------------------------------- ### Correct Internal Markdown Links Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/CLAUDE.md Examples of how to correctly link to other practices within the repository using relative paths. ```markdown See also [Code Standards](../code-standards/) for related practices. Check our [Authentication guide](./auth-best-practices.md) for more details. ``` -------------------------------- ### Correct External Markdown Links Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/CLAUDE.md Examples of how to correctly link to external resources with descriptive titles. ```markdown - [Laravel Documentation](https://laravel.com/docs) - [PHP Standards Recommendations](https://www.php-fig.org/psr/) ``` -------------------------------- ### Example of Mixed Test Suite Structure Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/testing/stick-to-one-testing-framework.md Illustrates a project directory structure with both PHPUnit and Pest test files, highlighting the complexity of a mixed testing approach. ```text tests/ ├── Feature/ │ ├── UserRegistrationTest.php # PHPUnit class │ ├── checkout_test.php # Pest functional test │ └── ProfileTest.php # PHPUnit class └── Unit/ ├── calculate_discount_test.php # Pest functional test └── OrderTest.php # PHPUnit class ``` -------------------------------- ### Scheduling Commands and Jobs in Laravel Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/project-structure-and-code-architecture/keep-commands-small-defer-to-jobs.md Examples of scheduling console commands and direct jobs within Laravel's scheduler. Demonstrates using `withoutOverlapping` for commands that dispatch jobs. ```php // In app/Console/Kernel.php protected function schedule(Schedule $schedule) { // Good: Command quickly dispatches job $schedule->command('data:process daily') ->daily() ->withoutOverlapping(); // Better: Direct job scheduling for simple cases $schedule->job(new ProcessDataJob('hourly')) ->hourly(); } ``` -------------------------------- ### Install Pest Drift Plugin Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/testing/stick-to-one-testing-framework.md Use the Pest Drift plugin to automatically convert PHPUnit tests to Pest. Install it via Composer and run the drift command. ```bash composer require pestphp/pest-plugin-drift --dev ./vendor/bin/pest --drift ``` -------------------------------- ### PHPUnit Feature Test Example Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/testing/use-phpunit-or-pest-for-testing.md Demonstrates a feature test using PHPUnit to verify user registration functionality, including database assertions. ```php post('/register', [ 'name' => 'John Doe', 'email' => 'john@example.com', 'password' => 'password', 'password_confirmation' => 'password', ]); $response->assertRedirect('/dashboard'); $this->assertDatabaseHas('users', [ 'email' => 'john@example.com', ]); } } ``` -------------------------------- ### Single-Purpose Action Class Example Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/project-structure-and-code-architecture/use-action-classes-for-business-logic.md An example of a single-purpose action class that encapsulates the business logic for creating an order and reserving inventory. ```php class CreateOrderAction { public function __construct(private InventoryService $inventory) {} public function execute(array $data): Order { $order = Order::create($data); $this->inventory->reserve($order); return $order; } } ``` -------------------------------- ### Example of a Good External Link Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/CLAUDE.md Illustrates the correct format for external links within best practice documentation, emphasizing the use of descriptive titles instead of plain URLs. ```markdown [Laravel Authorization Documentation](https://laravel.com/docs/authorization) ``` -------------------------------- ### Pest Feature Test Example Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/testing/use-phpunit-or-pest-for-testing.md Illustrates a feature test using Pest to verify user registration, employing Pest's functional syntax and expectations. ```php post('/register', [ 'name' => 'John Doe', 'email' => 'john@example.com', 'password' => 'password', 'password_confirmation' => 'password', ]); $response->assertRedirect('/dashboard'); expect(User::where('email', 'john@example.com')->exists())->toBeTrue(); }); ``` -------------------------------- ### Example of a Bad External Link Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/CLAUDE.md Shows an incorrect way to format external links, where only the URL is provided without a descriptive title. ```markdown https://laravel.com/docs/authorization ``` -------------------------------- ### Repository Structure for Best Practices Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/CLAUDE.md Outlines the standard directory structure for the Laravel best practices repository, including the placement of the main CLAUDE.md file, example files, and section-specific directories. ```directory structure best-practices/ ├── README.md ├── CLAUDE.md (this file) ├── example.md (reference for styling) └── [section-directories]/ ├── README.md (section overview) └── [practice-name].md (individual practices) ``` -------------------------------- ### Incorrect Markdown Links Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/CLAUDE.md Examples of incorrect markdown link formatting that should be avoided. ```markdown - https://laravel.com/docs (no title) - [guide](/absolute/path/guide.md) (absolute path) ``` -------------------------------- ### Rate Limit Authentication and API Routes Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/security-and-authentication/prevent-common-vulnerabilities.md Configure rate limiting for routes like login or API endpoints to prevent brute-force attacks and abuse. This example limits login attempts per minute per IP address. ```php RateLimiter::for('login', function (Request $request) { return Limit::perMinute(5)->by($request->ip()); }); Route::post('/login', LoginController::class)->middleware('throttle:login'); ``` -------------------------------- ### Correctly Order Fakes for Model Events Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/testing/follow-testing-best-practices.md Ensure model events are not silently broken by calling `Event::fake()` after factory setup, as factories depend on these events (e.g., for UUID generation). ```php // Bad: Event::fake() prevents factory model events Event::fake(); $user = User::factory()->create(); ``` ```php // Good: create models first, then fake events $user = User::factory()->create(); Event::fake(); ``` -------------------------------- ### Good Practice: Using Raw SQL in Migration Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/database-and-eloquent-orm/avoid-eloquent-models-in-migrations.md Illustrates how to reliably update data in a migration using raw SQL statements via the DB facade. This is a robust alternative to using Eloquent models. ```php use Illuminate\Support\Facades\DB; public function up() { Schema::table('posts', function (Blueprint $table) { $table->boolean('is_published')->default(false); }); // Raw SQL is also reliable DB::statement('UPDATE posts SET is_published = true'); } ``` -------------------------------- ### AI Prompt for Creating a Practice Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/CLAUDE.md A prompt template to use when asking an AI to help create a new Laravel best practice document. ```markdown Create a Laravel best practice about [TOPIC]. Context: I'm contributing to a Laravel best practices repository. Please: 1. Check if this topic already exists by reviewing the repository structure in CLAUDE.md 2. Determine the appropriate directory 3. Create a properly formatted markdown file following the template in CLAUDE.md 4. Ensure all links are formatted correctly (relative paths for internal, descriptive titles for external) 5. Include practical examples and clear explanations The practice should cover: [SPECIFIC DETAILS] ``` -------------------------------- ### Prompt to Create a New Laravel Best Practice Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/CLAUDE.md Use this prompt when asking an AI assistant to generate a new Laravel best practice markdown file. It specifies the need to follow repository structure and formatting guidelines, and lists key checks for the AI to perform. ```markdown I want to create a new Laravel best practice. Please help me create a markdown file that follows the repository structure and formatting guidelines found in CLAUDE.md. The best practice should cover: [DESCRIBE YOUR TOPIC] Please check: 1. If this is a duplicate of existing practices 2. Which directory it should go in 3. Ensure it follows the correct format 4. Use proper linking conventions ``` -------------------------------- ### Controller with Constructor Injection Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/project-structure-and-code-architecture/use-action-classes-for-business-logic.md Demonstrates the correct way to use constructor injection in a controller to access a service, contrasting it with the service locator pattern. ```php // Bad: service locator pattern class OrderController extends Controller { public function store(StoreOrderRequest $request) { $service = app(OrderService::class); return $service->create($request->validated()); } } // Good: constructor injection class OrderController extends Controller { public function __construct(private OrderService $service) {} public function store(StoreOrderRequest $request) { return $this->service->create($request->validated()); } } ``` -------------------------------- ### Prompt to Verify an Existing Laravel Best Practice Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/CLAUDE.md This prompt is designed to have an AI assistant review an existing Laravel best practice file against a quality checklist. It requests specific verification points related to format, duplication, placement, and content quality. ```markdown Please review this Laravel best practice file and check it against the quality checklist in CLAUDE.md. [PASTE YOUR MARKDOWN CONTENT] Specifically verify: - Format compliance - Duplicate detection - Directory placement - Link formatting - Content quality ``` -------------------------------- ### Log in an Existing User Instance Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/example.md Authenticate an existing user instance using the 'Auth::login' method. The user instance must implement the 'Authenticatable' contract. ```php use Illuminate\Support\Facades\Auth; Auth::login($user); ``` -------------------------------- ### Coding to Interfaces for External Dependencies Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/project-structure-and-code-architecture/use-action-classes-for-business-logic.md Illustrates how to depend on an interface (PaymentGateway) rather than a concrete implementation (StripeGateway) for external services. This allows for easier testing and swapping of implementations. ```php // Bad: concrete dependency class OrderService { public function __construct(private StripeGateway $gateway) {} } // Good: interface dependency interface PaymentGateway { public function charge(int $amount, string $customerId): PaymentResult; } class OrderService { public function __construct(private PaymentGateway $gateway) {} } ``` -------------------------------- ### AI Prompt for Repository Updates Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/CLAUDE.md A prompt template for requesting AI assistance with updating repository README files after adding a new practice. ```markdown I've added a new best practice file. Please help me: 1. Update the relevant section README.md to include a link to the new practice 2. Verify the main README.md sections list is complete 3. Check that all internal links in the repository are still valid New file: [PATH/FILENAME] ``` -------------------------------- ### Eloquent Local Scopes: Bad vs. Good Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/database-and-eloquent-orm/use-eloquent-scopes-and-casts.md Demonstrates duplicated query logic versus using a reusable local scope for cleaner code. ```php // Bad: duplicated query logic $active = User::where('verified', true)->whereNotNull('activated_at')->get(); $articles = Article::whereHas('user', function ($q) { $q->where('verified', true)->whereNotNull('activated_at'); })->get(); // Good: reusable local scope public function scopeActive(Builder $query): Builder { return $query->where('verified', true)->whereNotNull('activated_at'); } $active = User::active()->get(); $articles = Article::whereHas('user', fn ($q) => $q->active())->get(); ``` -------------------------------- ### Environment Checks in Laravel Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/project-structure-and-code-architecture/use-configuration-properly.md Shows the recommended method for checking the application's environment. Using `app()->isProduction()` or `App::environment()` is reliable even when configuration is cached, unlike directly using `env('APP_ENV')`. ```php // Bad: breaks with config caching if (env('APP_ENV') === 'production') { // Good: always reliable if (app()->isProduction()) { // or if (App::environment('production')) { ``` -------------------------------- ### Configure Failover Cache Stores Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/application-performance/use-caching-effectively.md Sets up a failover cache configuration in `config/cache.php`. This ensures application resilience by specifying a list of fallback cache stores to use if the primary store becomes unavailable. ```php // config/cache.php 'failover' => [ 'driver' => 'failover', 'stores' => ['redis', 'database'], ], ``` -------------------------------- ### Encrypting Production Secrets Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/project-structure-and-code-architecture/use-configuration-properly.md Demonstrates how to encrypt sensitive production secrets using Artisan commands. For cloud deployments, it's recommended to use the platform's native secret store. ```bash php artisan env:encrypt --env=production --readable php artisan env:decrypt --env=production ``` -------------------------------- ### Handle User Authentication Attempt Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/example.md This snippet demonstrates how to authenticate a user by validating their credentials and using the Auth facade's `attempt` method. It regenerates the session upon successful authentication and redirects the user, or provides an error message on failure. Ensure the `Auth` facade is imported. ```php validate([ 'email' => ['required', 'email'], 'password' => ['required'], ]); if (Auth::attempt($credentials)) { $request->session()->regenerate(); return redirect()->intended('dashboard'); } return back()->withErrors([ 'email' => 'The provided credentials do not match our records.', ])->onlyInput('email'); } } ``` -------------------------------- ### Using Language Files for User-Facing Strings Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/project-structure-and-code-architecture/use-configuration-properly.md When language files are already in use for localization, use the `__()` helper for user-facing strings. For English-only apps, simple string literals are acceptable. ```php // Only when lang files already exist in the project return back()->with('message', __('app.article_added')); ``` -------------------------------- ### AI Prompt for Verification Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/CLAUDE.md A prompt template for verifying a new best practice document against the repository's checklist. ```markdown Please verify this Laravel best practice against the CLAUDE.md checklist: [PASTE CONTENT] Check for: - Proper formatting and structure - Correct directory placement - Link formatting compliance - Duplicate detection - Content quality and clarity ``` -------------------------------- ### Write Reversible `down()` Methods for Migrations Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/database-and-eloquent-orm/write-effective-migrations.md Ensure your migrations are reversible by implementing a `down()` method that undoes the changes made in the `up()` method. For irreversible migrations, add a clear comment and plan for a forward-fix migration. ```php public function down(): void { Schema::table('posts', function (Blueprint $table) { $table->dropColumn('slug'); }); } ``` -------------------------------- ### Using Constants Instead of Magic Strings Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/project-structure-and-code-architecture/use-configuration-properly.md Illustrates how to improve code maintainability and refactorability by using class constants instead of magic strings for states or types. ```php // Bad: typo-prone magic string return $this->type === 'normal'; // Good: refactorable constant return $this->type === self::TYPE_NORMAL; ``` -------------------------------- ### Log in User Instance with 'Remember Me' Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/example.md Optionally enable 'remember me' functionality when logging in a user instance by passing true as the second argument to 'Auth::login'. ```php Auth::login($user, $remember = true); ``` -------------------------------- ### Configuring Custom Token Guard Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/example.md Configure your custom token guard as a driver within the 'guards' configuration of your auth.php file. ```php 'guards' => [ 'api' => [ 'driver' => 'custom-token', ], ], ``` -------------------------------- ### Log in User Instance with Specific Guard Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/example.md Specify an authentication guard before calling the 'login' method to authenticate a user instance with a particular guard. ```php Auth::guard('admin')->login($user); ``` -------------------------------- ### Registering Resource Controllers Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/routing/use-route-model-binding.md Shows how to register resource controllers for standard CRUD operations using `Route::resource` and `Route::apiResource`. These methods automatically define routes for common resource actions. ```php Route::resource('posts', PostController::class); Route::apiResource('api/posts', Api\PostController::class); ``` -------------------------------- ### Eager Load Relationships with `with()` Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/database-and-eloquent-orm/prevent-n-plus-one-queries.md Compares the N+1 query problem with the efficient solution using `with()`. Use `with()` to eager load relationships, reducing N+1 queries to just 2. ```php // Bad: N+1 — executes 1 + N queries $posts = Post::all(); foreach ($posts as $post) { echo $post->author->name; } // Good: 2 queries total $posts = Post::with('author')->get(); foreach ($posts as $post) { echo $post->author->name; } ``` -------------------------------- ### Markdown Content Template for a Practice Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/CLAUDE.md This is the standard markdown template to follow when creating a new best practice document for the repository. ```markdown # Practice Title ## Introduction Brief explanation of what this practice involves. ## Why - Benefit 1 - Benefit 2 - Benefit 3 ## Suitable For - Use case 1 - Use case 2 ## Less Suitable - When not to use this practice ## More Info - [Descriptive Link Title](https://example.com) - [Another Resource](https://example.com) ``` -------------------------------- ### File Naming Convention for Best Practices Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/CLAUDE.md Specifies the recommended file naming convention for individual best practice documents, advocating for descriptive, kebab-case names and avoiding redundant terms. ```markdown use-policies-for-authorization.md ``` -------------------------------- ### Configure Apache for HTTP Basic Authentication with FastCGI Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/example.md These Apache configuration lines may be necessary to resolve issues with HTTP Basic Authentication when using PHP FastCGI. ```apache RewriteCond %{HTTP:Authorization} ^(.+)$ RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] ``` -------------------------------- ### Configuring Custom Guard in auth.php Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/example.md Reference your custom guard in the 'guards' configuration of your auth.php configuration file, specifying the driver and provider. ```php 'guards' => [ 'api' => [ 'driver' => 'jwt', 'provider' => 'users', ], ], ``` -------------------------------- ### Use Cache::flexible() for Stale-While-Revalidate Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/application-performance/use-caching-effectively.md Improves user experience on high-traffic keys by serving slightly stale data while refreshing the cache in the background. This avoids users waiting for slow recomputations when the cache expires. ```php // Bad: one unlucky user waits for recomputation Cache::remember('users', 300, fn () => User::all()); // Good: fresh for 5 min, stale-but-served up to 10 min, refreshes via deferred function Cache::flexible('users', [300, 600], fn () => User::all()); ``` -------------------------------- ### Implement Retry with Exponential Backoff Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/apis/use-the-http-client-correctly.md Use the `retry()` method with an array of delays to handle transient API failures gracefully. This prevents overwhelming external services during temporary outages. ```php // Bad: no retry on transient failure $response = Http::post('https://api.stripe.com/v1/charges', $data); // Good: exponential backoff $response = Http::retry([100, 500, 1000]) ->timeout(10) ->post('https://api.stripe.com/v1/charges', $data); ``` -------------------------------- ### Good Practice: Using Query Builder in Migration Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/database-and-eloquent-orm/avoid-eloquent-models-in-migrations.md Shows the recommended approach for updating data in a migration using Laravel's Query Builder (DB facade). This method ensures reliability regardless of model changes. ```php use Illuminate\Support\Facades\DB; public function up() { Schema::table('posts', function (Blueprint $table) { $table->boolean('is_published')->default(false); }); // This will always work DB::table('posts')->update(['is_published' => true]); } ``` -------------------------------- ### Use Factory States for Self-Documenting Tests Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/testing/follow-testing-best-practices.md Utilize named factory states (e.g., `unverified()`) to improve test readability and communicate intent clearly, rather than manually overriding attributes. ```php // Bad: manual attribute override User::factory()->create(['email_verified_at' => null]); ``` ```php // Good: named state communicates intent User::factory()->unverified()->create(); ``` -------------------------------- ### Select Only Needed Columns for Eager Loading Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/database-and-eloquent-orm/prevent-n-plus-one-queries.md Compares fetching all columns (`SELECT *`) with selecting only necessary columns for both the main model and its eager-loaded relationship. Always include the foreign key when selecting columns on eager-loaded relationships. ```php // Bad: SELECT * on both tables $posts = Post::with('author')->get(); // Good: only fetches what you need $posts = Post::select('id', 'title', 'user_id', 'created_at') ->with(['author:id,name,avatar']) ->get(); ``` -------------------------------- ### Configuring a Custom User Provider in auth.php Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/example.md Configure your application to use the newly registered custom user provider by defining a 'users' provider with the 'mongo' driver in your `auth.php` configuration file. ```php 'providers' => [ 'users' => [ 'driver' => 'mongo', ], ], ``` -------------------------------- ### Scoped Bindings for Nested Resources Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/routing/use-route-model-binding.md Demonstrates how to use scoped route model bindings to automatically resolve nested resources, ensuring that a child resource belongs to its parent. The `scopeBindings()` method enforces this relationship. ```php Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) { // $post is automatically scoped to $user })->scopeBindings(); ``` -------------------------------- ### Recommended Git Branch Naming Convention Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/CLAUDE.md The recommended format for naming Git branches when creating a new best practice. ```text feature/add-[section]-[practice-name] Examples: feature/add-testing-unit-test-organization feature/add-security-csrf-protection feature/add-performance-database-optimization ``` -------------------------------- ### Centralized Exception Handling Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/maintenance/handle-exceptions-properly.md Manage all exception rendering logic in a single location, typically `bootstrap/app.php`. This approach is useful for maintaining a consistent overview of error handling across the application. ```php ->withExceptions(function (Exceptions $exceptions) { $exceptions->render(function (InvalidOrderException $e, Request $request) { return response()->view('errors.invalid-order', status: 422); }); }) ``` -------------------------------- ### Conditional Authentication with attemptWhen Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/example.md Employ 'attemptWhen' for thorough inspection of a potential user before authentication. The provided closure should return true or false to determine if authentication is permitted. ```php if (Auth::attemptWhen([ 'email' => $email, 'password' => $password, ], function (User $user) { return $user->isNotBanned(); })) { // Authentication was successful... } ``` -------------------------------- ### Stateless HTTP Basic Authentication Middleware Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/example.md Implement stateless HTTP Basic Authentication for API requests by defining a middleware that uses the `onceBasic` method. This avoids session cookies. ```php app->bind(PaymentGateway::class, StripeGateway::class); ``` -------------------------------- ### Configure Queue Driver for Asynchronous Processing Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/project-structure-and-code-architecture/keep-commands-small-defer-to-jobs.md Avoid the `sync` queue driver as it executes jobs synchronously, defeating the purpose of deferring tasks. Use drivers like `redis`, `database`, `sqs`, or `beanstalkd` for asynchronous job execution. ```dotenv // BAD: Job blocks the command with sync driver QUEUE_CONNECTION=sync ``` ```dotenv // GOOD: Job runs asynchronously QUEUE_CONNECTION=redis # or database, sqs, beanstalkd ``` -------------------------------- ### Batch Related Jobs with Callbacks Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/project-structure-and-code-architecture/configure-queued-jobs-properly.md Dispatch batch jobs using `Bus::batch` and define `then` and `catch` callbacks for success and failure scenarios. ```php Bus::batch([ new ImportCsvChunk($chunk1), new ImportCsvChunk($chunk2), ]) ->then(fn (Batch $batch) => Notification::send($user, new ImportComplete)) ->catch(fn (Batch $batch, Throwable $e) => Log::error('Batch failed')) ->dispatch(); ``` -------------------------------- ### Audit Project Dependencies Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/security-and-authentication/prevent-common-vulnerabilities.md Run `composer audit` periodically to check for known security vulnerabilities in your project's dependencies. Automate this check in your Continuous Integration (CI) pipeline. ```bash composer audit ``` -------------------------------- ### Add Indexes in Migrations Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/database-and-eloquent-orm/write-effective-migrations.md Include indexes on frequently queried columns directly within the migration to improve database performance from the outset. This avoids performance regressions and forgotten optimizations. ```php // Bad: no indexes on frequently queried columns Schema::create('orders', function (Blueprint $table) { $table->id(); $table->foreignId('user_id')->constrained(); $table->string('status'); $table->timestamps(); }); // Good: indexes added from the start Schema::create('orders', function (Blueprint $table) { $table->id(); $table->foreignId('user_id')->constrained()->index(); $table->string('status')->index(); $table->timestamp('shipped_at')->nullable()->index(); $table->timestamps(); }); ``` -------------------------------- ### Use Cache::add() for Atomic Conditional Writes Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/application-performance/use-caching-effectively.md Provides an atomic way to write a cache key only if it does not already exist, preventing race conditions that can occur with separate `has()` and `put()` calls. The key is set with an expiration time. ```php // Bad: race condition between check and write if (! Cache::has('lock')) { Cache::put('lock', true, 10); } // Good: atomic — only writes if key doesn't exist Cache::add('lock', true, 10); ``` -------------------------------- ### Basic Chunking with `chunk()` Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/database-and-eloquent-orm/use-chunking-for-large-datasets.md Use `chunk()` to process records in batches, preventing memory exhaustion. This is suitable for operations like sending notifications to subscribed users. ```php // Bad: loads everything into memory $users = User::all(); foreach ($users as $user) { $user->notify(new WeeklyDigest); } ``` ```php // Good: processes in batches of 200 User::where('subscribed', true)->chunk(200, function ($users) { foreach ($users as $user) { $user->notify(new WeeklyDigest); } }); ``` -------------------------------- ### Bad Practice: Using Eloquent Model in Migration Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/database-and-eloquent-orm/avoid-eloquent-models-in-migrations.md Demonstrates the incorrect way to update data within a migration by using an Eloquent model. This can lead to failures if the model definition changes. ```php use App\Models\Post; public function up() { Schema::table('posts', function (Blueprint $table) { $table->boolean('is_published')->default(false); }); // This will break if the Post model changes Post::query()->update(['is_published' => true]); } ``` -------------------------------- ### Implement `failed()` for Explicit Error Handling Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/project-structure-and-code-architecture/configure-queued-jobs-properly.md Implement the `failed()` method to explicitly handle job failures, logging errors and updating relevant model statuses. ```php public function failed(?Throwable $exception): void { $this->podcast->update(['status' => 'failed']); Log::error('Processing failed', [ 'id' => $this->podcast->id, 'error' => $exception->getMessage(), ]); } ``` -------------------------------- ### Define Timeouts in a Service-Specific Macro Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/apis/use-the-http-client-correctly.md For reusable service clients, define default timeouts and base URLs within an HTTP macro. This promotes consistency and reduces boilerplate. ```php Http::macro('github', function () { return Http::baseUrl('https://api.github.com') ->timeout(10) ->connectTimeout(3) ->withToken(config('services.github.token')); }); $response = Http::github()->get('/repos/laravel/framework'); ``` -------------------------------- ### Protect Route with HTTP Basic Authentication Middleware Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/example.md Attach the 'auth.basic' middleware to a route to enable HTTP Basic Authentication. Browsers will automatically prompt for credentials, using the 'email' column as the username by default. ```php Route::get('/profile', function () { // Only authenticated users may access this route... })->middleware('auth.basic'); ``` -------------------------------- ### Implement 'Remember Me' Functionality Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/example.md Enable 'remember me' functionality by passing a boolean true as the second argument to the 'attempt' method. Ensure your 'users' table has a 'remember_token' column. ```php use Illuminate\Support\Facades\Auth; if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) { // The user is being remembered... } ``` -------------------------------- ### Define Route for Password Confirmation Form Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/example.md This route displays the view that prompts the user to confirm their password. It requires the 'auth' middleware to ensure the user is logged in. ```php Route::get('/confirm-password', function () { return view('auth.confirm-password'); })->middleware('auth')->name('password.confirm'); ``` -------------------------------- ### Use Exponential Backoff for Retries Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/project-structure-and-code-architecture/configure-queued-jobs-properly.md Configure `tries` and `backoff` properties to implement exponential backoff, defining seconds between retries for jobs. ```php class SyncWithStripe implements ShouldQueue { public $tries = 3; public $backoff = [1, 5, 10]; // seconds between retries } ``` -------------------------------- ### Use `withCount()` for Counting Relations Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/database-and-eloquent-orm/prevent-n-plus-one-queries.md Compares inefficiently counting relations by loading entire collections versus using `withCount()`. `withCount()` adds a `relation_count` attribute via a single subquery. ```php // Bad: loads entire collections just to count them $posts = Post::all(); foreach ($posts as $post) { echo $post->comments->count(); } // Good: adds a comments_count attribute via a single subquery $posts = Post::withCount('comments')->get(); foreach ($posts as $post) { echo $post->comments_count; } ``` -------------------------------- ### Use Exceptions::fake() for Exception Assertions Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/testing/follow-testing-best-practices.md Replace `withoutExceptionHandling()` with `Exceptions::fake()` to assert that the correct exception was reported while allowing the request to complete normally. ```php use function Pest\Laravel\withoutExceptionHandling; // Instead of: // withoutExceptionHandling(); // Use: use function Pest\Laravel\expectException; use function Pest\Laravel\expectToHaveReportedException; expectException(SomeException::class); // ... code that throws SomeException ... expectToHaveReportedException(); ``` -------------------------------- ### Eloquent `whereBelongsTo()` for Relationship Queries Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/database-and-eloquent-orm/use-eloquent-scopes-and-casts.md Compares hardcoded foreign key usage with the cleaner, relationship-aware `whereBelongsTo()` method. ```php // Bad: hardcoded foreign key Post::where('user_id', $user->id)->get(); // Good: cleaner and relationship-aware Post::whereBelongsTo($user)->get(); Post::whereBelongsTo($user, 'author')->get(); ``` -------------------------------- ### Use `Rule::when()` for Conditional Validation Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/routing/use-form-request-classes.md Illustrates how to use `Rule::when()` for conditional validation rules that only apply based on certain criteria. ```php 'company_name' => [ Rule::when($this->account_type === 'business', ['required', 'string', 'max:255']), ], ``` -------------------------------- ### Eloquent Date Casting for Blade Templates Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/database-and-eloquent-orm/use-eloquent-scopes-and-casts.md Shows how to cast date columns in the model for direct use in Blade templates, avoiding manual parsing. ```php // Bad: manual date parsing in templates {{ Carbon::createFromFormat('Y-d-m H-i', $order->ordered_at)->toDateString() }} // Good: cast in the model protected function casts(): array { return [ 'ordered_at' => 'datetime', ]; } // Then use directly in Blade {{ $order->ordered_at->toDateString() }} {{ $order->ordered_at->format('m-d') }} ``` -------------------------------- ### Dispatch a Job from a Laravel Command Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/project-structure-and-code-architecture/keep-commands-small-defer-to-jobs.md A lightweight Laravel command that dispatches a job for asynchronous processing. The command handles CLI input and queues the job, keeping its own logic minimal. ```php namespace App\Console\Commands; use App\Jobs\ProcessDataJob; use Illuminate\Console\Command; class ProcessDataCommand extends Command { protected $signature = 'data:process {type}'; protected $description = 'Queue data processing job'; public function handle() { $type = $this->argument('type'); // Minimal logic - just dispatch the job ProcessDataJob::dispatch($type); $this->info("Data processing job queued for type: {$type}"); return 0; } } ``` -------------------------------- ### Apply Auth and Session Authentication Middleware to Routes Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/example.md This snippet shows how to apply both the 'auth' and 'auth.session' middleware to a group of routes. Ensure the 'Illuminate\Session\Middleware\AuthenticateSession' middleware is included on routes requiring session authentication. ```php Route::middleware(['auth', 'auth.session'])->group(function () { Route::get('/', function () { // ... }); }); ``` -------------------------------- ### Applying Custom Guard Middleware to Routes Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/example.md Reference the custom guard when assigning the authentication middleware to a route. ```php Route::middleware('auth:api')->group(function () { // ... }); ``` -------------------------------- ### Use Cache::remember() for Atomic Operations Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/application-performance/use-caching-effectively.md Replaces manual cache get/check/put patterns with an atomic operation, preventing race conditions. Use `Cache::remember()` with a key, expiration time, and a closure to compute the value if it's not found. ```php // Bad: race condition, boilerplate $val = Cache::get('stats'); if (! $val) { $val = $this->computeStats(); Cache::put('stats', $val, 60); } // Good: atomic pattern $val = Cache::remember('stats', 60, fn () => $this->computeStats()); ``` -------------------------------- ### Co-located Exception Handling Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/maintenance/handle-exceptions-properly.md Define custom reporting and rendering logic directly within your exception class. Use this when you want exception-specific behavior to be self-contained. ```php class InvalidOrderException extends Exception { public function report(): void { /* custom reporting */ } public function render(Request $request): Response { return response()->view('errors.invalid-order', status: 422); } } ``` -------------------------------- ### Accessing Environment Variables in Laravel Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/project-structure-and-code-architecture/use-configuration-properly.md Demonstrates the correct way to access environment variables in Laravel. Environment variables should only be accessed within configuration files, and application code should use the `config()` helper. ```php // Bad: returns null when config is cached $key = env('API_KEY'); // Good: define in config, use via config() // config/services.php 'key' => env('API_KEY'), // Application code $key = config('services.key'); ``` -------------------------------- ### Use once() for Per-Request In-Memory Memoization Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/application-performance/use-caching-effectively.md Memoizes a function's return value purely in memory for the lifetime of the object, avoiding any cache store hits. This is ideal for computations that should only run once per request. ```php public function roles(): Collection { return once(fn () => $this->loadRoles()); } ``` -------------------------------- ### Use Closure for Complex Authentication Conditions Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/example.md For intricate query conditions, provide a closure to the 'attempt' method. This allows dynamic customization of the authentication query based on application needs. ```php use Illuminate\Database\Eloquent\Builder; if (Auth::attempt([ 'email' => $email, 'password' => $password, fn (Builder $query) => $query->has('activeSubscription'), ])) { // Authentication was successful... } ``` -------------------------------- ### Implicit Route Model Binding Comparison Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/routing/use-route-model-binding.md Compares manual model resolution with Laravel's automatic route model binding using type-hinting. Automatic resolution eliminates the need for explicit `findOrFail()` calls. ```php // Bad: manual resolution public function show(int $id) { $post = Post::findOrFail($id); } // Good: automatic resolution with type-hinting public function show(Post $post) { return view('posts.show', ['post' => $post]); } ``` -------------------------------- ### Use recycle() to Share Relationship Instances Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/testing/follow-testing-best-practices.md Employ `recycle()` when using nested factories to ensure that relationship instances are shared, preventing the creation of separate, redundant instances of the same conceptual entity. ```php use App\Models\Airline; use App\Models\Ticket; Ticket::factory() ->recycle(Airline::factory()->create()) ->create(); // Without recycle(), nested factories create separate instances of the same conceptual entity. ``` -------------------------------- ### Prevent Lazy Loading in Development Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/database-and-eloquent-orm/prevent-n-plus-one-queries.md Enable `Model::preventLazyLoading()` in `AppServiceProvider::boot()` to catch N+1 issues during development. This throws a `LazyLoadingViolationException` when a relationship is accessed without being eager-loaded. ```php public function boot(): void { Model::preventLazyLoading(! app()->isProduction()); } ``` -------------------------------- ### Add Extra Conditions to Authentication Attempt Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/example.md Use this to add custom conditions to the authentication query, such as verifying a user's active status. Ensure the conditions match your database schema. ```php if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) { // Authentication was successful... } ``` -------------------------------- ### Implement Unique Jobs for Scheduled Commands Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/project-structure-and-code-architecture/keep-commands-small-defer-to-jobs.md Use `ShouldBeUnique` to prevent duplicate jobs from running simultaneously for scheduled commands. Configure `uniqueId` for identification and `uniqueFor` to set the uniqueness lock duration. ```php use Illuminate\Contracts\Queue\ShouldBeUnique; class ScheduledImportJob implements ShouldQueue, ShouldBeUnique { public function uniqueId(): string { return 'scheduled-import-' . $this->importType; } public function uniqueFor(): int { return 3600; // 1 hour } } ``` -------------------------------- ### Perform Concurrent HTTP Requests with Pooling Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/apis/use-the-http-client-correctly.md Use `Http::pool()` to run multiple independent HTTP requests concurrently, significantly improving performance by eliminating sequential wait times. ```php // Bad: sequential requests $users = Http::get('https://api.example.com/users')->json(); $posts = Http::get('https://api.example.com/posts')->json(); // Good: concurrent requests use Illuminate\Http\Client\Pool; $responses = Http::pool(fn (Pool $pool) => [ $pool->as('users')->get('https://api.example.com/users'), $pool->as('posts')->get('https://api.example.com/posts'), ]); $users = $responses['users']->json(); $posts = $responses['posts']->json(); ``` -------------------------------- ### Control Concurrency with `WithoutOverlapping` Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/project-structure-and-code-architecture/configure-queued-jobs-properly.md Use the `WithoutOverlapping` middleware to prevent concurrent processing of jobs. The `untilProcessing()` option releases the lock when processing begins. ```php public function middleware(): array { return [new WithoutOverlapping($this->product->id)->untilProcessing()]; } ``` -------------------------------- ### Mass Assignment Protection: Whitelist vs. Blacklist Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/security-and-authentication/prevent-common-vulnerabilities.md Define either a `$fillable` whitelist or a `$guarded` blacklist for your Eloquent models to control mass assignable attributes. Avoid using `$guarded = []` on models that handle user input. ```php class User extends Model { protected $guarded = []; } ``` ```php class User extends Model { protected $fillable = [ 'name', 'email', 'password', ]; } ``` -------------------------------- ### Use `after()` for Custom Validation Logic Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/routing/use-form-request-classes.md Explains how to use the `after()` method in Form Requests for custom validation logic that depends on multiple fields. ```php public function after(): array { return [ function (Validator $validator) { if ($this->quantity > Product::find($this->product_id)?->stock) { $validator->errors()->add('quantity', 'Not enough stock.'); } }, ]; } ``` -------------------------------- ### Add Structured Context to Exceptions Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/maintenance/handle-exceptions-properly.md Enhance exception log entries with custom, structured data by defining a `context()` method within your exception class. This provides valuable metadata for debugging. ```php class InvalidOrderException extends Exception { public function context(): array { return ['order_id' => $this->orderId]; } } ``` -------------------------------- ### Keep Migrations Focused: Separate Schema and Data Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/database-and-eloquent-orm/write-effective-migrations.md Avoid mixing Data Definition Language (DDL) for schema changes with Data Manipulation Language (DML) for data operations within a single migration. Use separate migrations for schema creation/alteration and for seeding initial data. ```php // Bad: partial failure creates unrecoverable state public function up(): void { Schema::create('settings', function (Blueprint $table) { /* ... */ }); DB::table('settings')->insert(['key' => 'version', 'value' => '1.0']); } // Good: separate migrations // Migration 1: create_settings_table Schema::create('settings', function (Blueprint $table) { /* ... */ }); // Migration 2: seed_default_settings DB::table('settings')->insert(['key' => 'version', 'value' => '1.0']); ``` -------------------------------- ### Protect Route with Auth Middleware Source: https://github.com/dutch-laravel-foundation/best-practices/blob/main/example.md Apply the 'auth' middleware to a route definition to restrict access to authenticated users only. ```php Route::get('/flights', function () { // Only authenticated users may access this route... })->middleware('auth'); ```