Laravel Tips and Tricks for Better Productivity

Laravel adalah framework PHP yang powerful dan elegan. Setelah bertahun-tahun menggunakannya, berikut adalah tips dan tricks yang saya temukan untuk meningkatkan produktivitas dan kualitas kode.

1. Route Model Binding

Gunakan route model binding untuk clean controller methods:

// Instead of
public function show($id)
{
    $user = User::findOrFail($id);
    return view('users.show', compact('user'));
}

// Use
public function show(User $user)
{
    return view('users.show', compact('user'));
}

Route:

Route::get('/users/{user}', [UserController::class, 'show']);

2. Eloquent Scopes

Scopes untuk reusable query constraints:

class Post extends Model
{
    // Local scope
    public function scopePublished($query)
    {
        return $query->where('status', 'published');
    }
    
    public function scopeRecent($query)
    {
        return $query->orderBy('created_at', 'desc');
    }
}

// Usage
$posts = Post::published()->recent()->get();

3. Accessor & Mutator

Format data secara otomatis:

class User extends Model
{
    // Accessor
    protected function firstName(): Attribute
    {
        return Attribute::make(
            get: fn (string $value) => ucfirst($value),
        );
    }
    
    // Mutator
    protected function password(): Attribute
    {
        return Attribute::make(
            set: fn (string $value) => bcrypt($value),
        );
    }
}

4. Form Request Validation

Pisahkan validation logic:

// app/Http/Requests/StoreUserRequest.php
class StoreUserRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users',
            'password' => 'required|min:8|confirmed',
        ];
    }
    
    public function messages(): array
    {
        return [
            'email.unique' => 'Email ini sudah terdaftar.',
        ];
    }
}

Controller:

public function store(StoreUserRequest $request)
{
    // Data sudah tervalidasi
    User::create($request->validated());
    
    return redirect()->route('users.index');
}

5. Service Container & Dependency Injection

Manfaatkan IoC container:

// Bind interface to implementation
app()->bind(PaymentGatewayInterface::class, StripePaymentGateway::class);

// Resolve via constructor injection
class OrderController extends Controller
{
    public function __construct(
        private PaymentGatewayInterface $paymentGateway
    ) {}
    
    public function store(Request $request)
    {
        $this->paymentGateway->charge($request->amount);
    }
}

6. Collections Power

Manfaatkan Collection methods:

$users = User::all();

// Group by
$grouped = $users->groupBy('role');

// Filter
$admins = $users->where('role', 'admin');

// Map
$names = $users->map(fn ($user) => $user->name);

// Reduce
$total = $orders->reduce(fn ($carry, $order) => $carry + $order->total, 0);

// Chaining
$stats = $users
    ->where('active', true)
    ->groupBy('department')
    ->map(fn ($dept) => $dept->count());

7. Database Optimization

Eager Loading

// N+1 problem
$users = User::all();
foreach ($users as $user) {
    echo $user->posts->count(); // Query berulang
}

// With eager loading
$users = User::with('posts')->get();
foreach ($users as $user) {
    echo $user->posts->count(); // Sudah diload
}

// Nested eager loading
$users = User::with(['posts.comments', 'profile'])->get();

Query Optimization

// Select specific columns
$users = User::select('id', 'name', 'email')->get();

// Chunk large datasets
User::chunk(100, function ($users) {
    foreach ($users as $user) {
        // Process user
    }
});

// Cursor for memory efficiency
foreach (User::cursor() as $user) {
    // Process user
}

8. Artisan Commands

Custom commands untuk automation:

// app/Console/Commands/SendWeeklyReport.php
class SendWeeklyReport extends Command
{
    protected $signature = 'report:weekly {--email=}';
    
    protected $description = 'Send weekly report to admin';
    
    public function handle()
    {
        $email = $this->option('email') ?? config('app.admin_email');
        
        $this->info('Sending report...');
        
        // Send report logic
        
        $this->info('Report sent successfully!');
    }
}

Schedule:

// routes/console.php
Schedule::command('report:weekly')->weekly()->mondays()->at('09:00');

9. Queues for Background Jobs

Offload heavy tasks:

// Dispatch job
ProcessPodcast::dispatch($podcast);

// Delayed dispatch
ProcessPodcast::dispatch($podcast)->delay(now()->addMinutes(10));

// On specific queue
ProcessPodcast::dispatch($podcast)->onQueue('processing');

// Job class
class ProcessPodcast implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    
    public function __construct(public Podcast $podcast) {}
    
    public function handle(): void
    {
        // Heavy processing
    }
    
    public function failed(Throwable $exception): void
    {
        // Handle failure
    }
}

10. API Resources

Transform data untuk API:

// app/Http/Resources/UserResource.php
class UserResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'email' => $this->email,
            'avatar' => $this->avatar_url,
            'member_since' => $this->created_at->format('M Y'),
            'posts_count' => $this->whenCounted('posts'),
            'roles' => RoleResource::collection($this->whenLoaded('roles')),
        ];
    }
}

// Usage
return new UserResource($user);
return UserResource::collection($users);

11. Caching Strategies

Cache untuk performance:

// Remember forever
$users = Cache::rememberForever('users', function () {
    return User::all();
});

// With expiration
$value = Cache::remember('key', 3600, function () {
    return expensiveOperation();
});

// Cache tags (Redis/Memcached)
Cache::tags(['users', 'admins'])->put('admin-list', $admins, 3600);
Cache::tags(['users', 'admins'])->flush();

// View caching
@cache(['key' => 'sidebar', 'seconds' => 3600])
    @include('partials.sidebar')
@endcache

12. Testing dengan PHPUnit

Testing yang efektif:

class UserTest extends TestCase
{
    use RefreshDatabase;
    
    public function test_user_can_register()
    {
        $response = $this->postJson('/api/register', [
            'name' => 'John Doe',
            'email' => 'john@example.com',
            'password' => 'password123',
        ]);
        
        $response->assertCreated()
            ->assertJsonPath('user.name', 'John Doe');
        
        $this->assertDatabaseHas('users', [
            'email' => 'john@example.com',
        ]);
    }
    
    public function test_user_can_login()
    {
        $user = User::factory()->create();
        
        $response = $this->postJson('/api/login', [
            'email' => $user->email,
            'password' => 'password',
        ]);
        
        $response->assertOk()
            ->assertJsonStructure(['token']);
    }
}

Kesimpulan

Laravel menyediakan banyak fitur untuk meningkatkan produktivitas. Gunakan:

  • Route Model Binding untuk clean code
  • Eloquent Scopes untuk reusable queries
  • Form Requests untuk organized validation
  • Collections untuk data manipulation
  • Queues untuk background processing
  • Caching untuk performance

Keep learning dan eksplorasi fitur-fitur Laravel yang baru!