Laravel Best Practices

Laravel Best Practices

Laravel Best Practices

1. Database & Eloquent Best Practices

1.1 Optimize Database Queries

// ❌ Bad: N+1 Query Problem
@foreach (Post::all() as $post)
    {{ $post->category->name }}
@endforeach

// ✅ Good: Eager Loading
$posts = Post::with('category')->get();
@foreach ($posts as $post)
    {{ $post->category->name }}
@endforeach

// ✅ Even Better: Use Laravel's Lazy Loading when needed
Model::withoutLazy()->get(); // Disable lazy loading in production

1.2 Use Query Builder for Complex Queries

// ✅ Good: Use Query Builder for complex queries
User::query()
    ->select(['name', 'email'])
    ->whereHas('posts', function ($query) {
        $query->where('published', true);
    })
    ->paginate();

2. Modern Controller Practices

2.1 Use Invokable Controllers for Single-Action Controllers

// ✅ Good: Single Action Controller
class ShowDashboardController extends Controller
{
    public function __invoke()
    {
        return view('dashboard', [
            'metrics' => $this->getMetrics(),
        ]);
    }
}

2.2 Use Form Requests for Validation

// ❌ Bad: Validation in Controller
public function store(Request $request)
{
    $validated = $request->validate([
        'title' => 'required|max:255',
        'body' => 'required',
    ]);
}

// ✅ Good: Dedicated Form Request
class StoreArticleRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'title' => ['required', 'string', 'max:255'],
            'body' => ['required', 'string'],
            'category_id' => ['required', 'exists:categories,id'],
        ];
    }
}

public function store(StoreArticleRequest $request)
{
    $article = Article::create($request->validated());
}

3. Modern Architecture Patterns

3.1 Use Actions Classes for Business Logic

// ✅ Good: Single-Purpose Action Class
class PublishArticleAction
{
    public function execute(Article $article): void
    {
        $article->published_at = now();
        $article->status = ArticleStatus::Published;
        $article->save();

        event(new ArticlePublished($article));
    }
}

3.2 Use Data Transfer Objects (DTOs)

// ✅ Good: Use DTOs for complex data structures
class ArticleData
{
    public function __construct(
        public readonly string $title,
        public readonly string $content,
        public readonly ?Carbon $publishDate = null,
    ) {}

    public static function fromRequest(StoreArticleRequest $request): self
    {
        return new self(
            title: $request->validated('title'),
            content: $request->validated('content'),
            publishDate: $request->validated('publish_date'),
        );
    }
}

4. Modern View & Frontend Practices

4.1 Use Blade Components

// ✅ Good: Reusable Blade Components
// alert.blade.php
<x-alert type="{{ $type }}" :dismissible="$dismissible">
    {{ $slot }}
</x-alert>

// Usage
<x-alert type="success" :dismissible="true">
    Profile updated successfully!
</x-alert>

4.2 Use Laravel Vite for Asset Management

// ✅ Good: Modern Asset Management
// vite.config.js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

export default defineConfig({
    plugins: [
        laravel({
            input: ['resources/css/app.css', 'resources/js/app.js'],
            refresh: true,
        }),
    ],
});

// In Blade
@vite(['resources/css/app.css', 'resources/js/app.js'])

5. Modern Testing Practices

5.1 Use Pest for Testing

// ✅ Good: Modern Testing with Pest
it('creates a new article', function () {
    $response = post('/articles', [
        'title' => 'My Article',
        'content' => 'Content here',
    ]);

    $response->assertCreated();
    expect(Article::count())->toBe(1);
});

5.2 Use Factories Effectively

// ✅ Good: Modern Factory Usage
class ArticleFactory extends Factory
{
    public function published(): self
    {
        return $this->state(fn (array $attributes) => [
            'published_at' => now(),
            'status' => ArticleStatus::Published,
        ]);
    }
}

// Usage
Article::factory()->published()->create();

6. API Development

6.1 Use API Resources

// ✅ Good: Use API Resources for Response Transformation
class ArticleResource extends JsonResource
{
    public function toArray($request): array
    {
        return [
            'id' => $this->id,
            'title' => $this->title,
            'content' => $this->when($request->user()?->isAdmin(), $this->content),
            'created_at' => $this->created_at->toISOString(),
        ];
    }
}

6.2 Use Sanctum for API Authentication

// ✅ Good: Modern API Authentication
Route::middleware(['auth:sanctum'])->group(function () {
    Route::apiResource('articles', ArticleController::class);
});

7. Performance Best Practices

7.1 Use Cache Effectively

// ✅ Good: Smart Caching
public function show(Article $article)
{
    return Cache::remember("articles.{$article->id}", now()->addHour(), fn () => 
        ArticleResource::make($article)
    );
}

7.2 Use Queue for Heavy Tasks

// ✅ Good: Queue Heavy Operations
class ProcessPodcast implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function handle(): void
    {
        // Heavy processing here
    }
}

8. Security Best Practices

8.1 Use Policy Classes

// ✅ Good: Use Policies for Authorization
class ArticlePolicy
{
    public function update(User $user, Article $article): bool
    {
        return $user->id === $article->user_id || $user->isAdmin();
    }
}

8.2 Secure Configuration

// ❌ Bad: Direct Environment Variable Usage
$apiKey = env('API_KEY');

// ✅ Good: Use Configuration Files
// config/services.php
'api' => [
    'key' => env('API_KEY'),
],

// Usage
$apiKey = config('services.api.key');

9. Code Organization

9.1 Use Laravel's Directory Structure

app/
├── Actions/         # Single-purpose action classes
├── Data/            # DTOs and other data objects
├── Events/          # Event classes
├── Listeners/       # Event listeners
├── Services/        # Complex business logic
├── Http/
   ├── Controllers/
   ├── Middleware/
   └── Requests/    # Form requests
└── Models/          # Eloquent models

9.2 Use Service Providers for Bootstrap Code

// ✅ Good: Register Bindings in Service Provider
class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->singleton(PaymentGateway::class, StripeGateway::class);
    }
}

10. Development Workflow

10.1 Use Laravel Sail for Development

# ✅ Good: Use Laravel Sail for consistent development environment
sail up
sail artisan migrate
sail test

10.2 Use GitHub Actions for CI/CD

# ✅ Good: Automated Testing and Deployment
name: Laravel

on:
  push:
    branches: [ main ]

jobs:
  laravel-tests:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Setup PHP
      uses: shivammathur/setup-php@v2
    - name: Run Tests
      run: |
        composer install
        php artisan test

Laravel Naming Conventions

The following table outlines the standard naming conventions for Laravel applications in 2024:

ComponentConvention✅ Good Example❌ Bad ExampleNotes
ControllersingularPostControllerPostsControllerUse singular form for resource controllers
Routepluralposts/1post/1Use plural for resource routes
Named routesnake_case with dotsusers.show_activeshow-active-usersConsistent with Laravel's internal naming
ModelsingularUserUsersAlways use singular for model names
hasOne/belongsTo relationshipsingularpostCommentpostCommentsReflects one-to-one nature
Other relationshipspluralpostCommentspostCommentReflects one-to-many nature
Database tablepluralpost_commentspost_commentUse snake_case, plural form
Pivot tablealphabetical singular modelspost_userusers_postsJoin singular model names with underscore
Model propertysnake_case$model->created_at$model->createdAtFollow Laravel's convention
Foreign keysingular with _idpost_idPostId, posts_idAlways use snake_case
Primary key'id'idcustom_idStick to Laravel defaults
Migrationdatetime_action2024_01_16_000000_create_posts_table2024_01_16_postsInclude full timestamp
MethodcamelCasegetAll()get_all()Follow PSR-12
Resource controller methodtablestore()savePost()Use standard resource names
VariablecamelCase$postsWithAuthor$posts_with_creatorClear and descriptive
Collectionplural, descriptive$activeUsers$activeClearly indicate content
Objectsingular, descriptive$activeUser$usersReflect single instance
Config indexsnake_casearticles_enabledArticlesEnabledConsistent with Laravel
Viewkebab-caseshow-filtered.blade.phpshowFiltered.blade.phpUse hyphens for views
Config filesnake_casegoogle_calendar.phpgoogleCalendar.phpUse underscores
Interfaceadjective/nounAuthenticationInterfaceIAuthenticationFollow PSR standards

Important Notes on Naming Conventions:

  1. Consistency is Key

    • Stick to these conventions across your entire application

    • Use automated tools like PHP CS Fixer to enforce standards

  2. Documentation

     // ✅ Good: Clear, consistent naming
     class PostController
     {
         public function show(Post $post)
         {
             return view('posts.show', compact('post'));
         }
     }
    
  3. File Structure

     app/
     ├── Http/
     │   └── Controllers/
     │       └── PostController.php
     ├── Models/
     │   └── Post.php
     └── Views/
         └── posts/
             └── show-details.blade.php
    
  4. Database Conventions

     // ✅ Good: Migration naming
     class CreatePostCommentsTable extends Migration
     {
         public function up()
         {
             Schema::create('post_comments', function (Blueprint $table) {
                 $table->id();
                 $table->foreignId('post_id')->constrained();
                 $table->timestamps();
             });
         }
     }
    
  5. Relationship Naming

     // ✅ Good: Clear relationship naming
     class Post extends Model
     {
         public function comments()
         {
             return $this->hasMany(Comment::class);
         }
    
         public function author()
         {
             return $this->belongsTo(User::class, 'user_id');
         }
     }
    

Following these naming conventions helps maintain consistency across your Laravel application and makes it easier for other developers to understand and work with your code. It also aligns with Laravel's built-in conventions, ensuring better integration with the framework's features and functionality.

Remember to:

  • Use PHP 8.2+ features where appropriate

  • Keep dependencies updated

  • Follow PSR-12 coding standards

  • Use static analysis tools like PHPStan

  • Implement proper logging and monitoring

  • Use Laravel Horizon for queue monitoring

  • Implement proper error handling and logging

  • Use Laravel Telescope for debugging in development

These practices reflect modern Laravel development as of 2024, incorporating new features and best practices from recent Laravel versions.