### 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');
```