laravel-performance
Auto-invoked skill
Optimize with caching, Pulse monitoring, memory management
Trigger Keywords
This skill automatically activates when Claude detects these keywords:
slow
optimize
cache
pulse
performance
memory
Big O
O(n)
complexity
nested loop
Overview
The laravel-performance skill provides expertise for optimizing Laravel applications. It covers caching strategies, query optimization, memory management, and monitoring with Laravel Pulse.
What This Skill Provides
- Caching Strategies - Data, query, and full-page caching
- Query Optimization - N+1 fixes, indexing, eager loading
- Big O Fixes - Detect O(n²) patterns and fix with O(1) lookups
- Memory Management - Chunking, lazy collections, memory limits
- Laravel Pulse - Real-time performance monitoring
- Profiling - Identifying bottlenecks with tools
- Asset Optimization - Vite bundling, compression
Example Conversations
# Diagnosing slowness
"My dashboard page is loading slowly, how can I profile it?"
# Caching
"What's the best way to cache expensive database queries?"
# Memory issues
"My job runs out of memory processing large datasets"
# Monitoring
"Set up Laravel Pulse to monitor my application performance"
Caching Patterns
<?php
use Illuminate\Support\Facades\Cache;
// Basic cache with TTL
$users = Cache::remember('active-users', 3600, function () {
return User::where('active', true)->get();
});
// Cache tags for group invalidation
$products = Cache::tags(['products', 'catalog'])->remember(
"category-{$categoryId}-products",
3600,
fn () => Product::where('category_id', $categoryId)->get()
);
// Invalidate all products cache
Cache::tags('products')->flush();
// Atomic locks for expensive operations
$result = Cache::lock('report-generation', 120)->block(10, function () {
return $this->generateExpensiveReport();
});
// Cache model queries efficiently
class Product extends Model
{
public static function getCachedFeatured()
{
return Cache::remember('featured-products', 3600, function () {
return static::where('featured', true)
->with('category')
->limit(10)
->get();
});
}
protected static function booted()
{
static::saved(fn () => Cache::forget('featured-products'));
static::deleted(fn () => Cache::forget('featured-products'));
}
}
Memory-Efficient Processing
<?php
// BAD: Loads all records into memory
$users = User::all();
foreach ($users as $user) {
$user->sendNewsletter();
}
// GOOD: Process in chunks
User::chunk(1000, function ($users) {
foreach ($users as $user) {
$user->sendNewsletter();
}
});
// BETTER: Lazy collection (one record at a time)
User::lazy()->each(function ($user) {
$user->sendNewsletter();
});
// For cursor-based iteration (lowest memory)
foreach (User::cursor() as $user) {
$user->sendNewsletter();
}
// Chunk by ID for stability during updates
User::chunkById(1000, function ($users) {
foreach ($users as $user) {
$user->update(['processed_at' => now()]);
}
});
Query Optimization
<?php
// BAD: N+1 queries
$posts = Post::all();
foreach ($posts as $post) {
echo $post->author->name; // Query for each post!
}
// GOOD: Eager loading
$posts = Post::with('author')->get();
// Select only needed columns
$posts = Post::select('id', 'title', 'user_id')
->with('author:id,name')
->get();
// Use query scopes for reusability
class Post extends Model
{
public function scopeWithAuthorName($query)
{
return $query->select('id', 'title', 'user_id')
->with('author:id,name');
}
}
// Index frequently queried columns
// In migration:
$table->index(['status', 'created_at']); // Composite index
$table->index('user_id');
// Use explain to analyze queries
DB::enableQueryLog();
$posts = Post::where('status', 'published')->get();
dd(DB::getQueryLog());
Big O Complexity Fixes
Big O complexity issues cause exponential slowdowns as data grows.
<?php
// BAD: O(n²) - Nested loops
foreach ($users as $user) {
foreach ($orders as $order) {
if ($order->user_id === $user->id) {
// Process - runs n×m times!
}
}
}
// GOOD: O(n) - Use relationships or keyBy
$users = User::with('orders')->get();
// Or: $ordersByUser = $orders->groupBy('user_id');
// BAD: O(n²) - contains() in loop
foreach ($newUsers as $userData) {
if (!$existingEmails->contains($userData['email'])) {
User::create($userData);
}
}
// GOOD: O(n) - Use flip() for O(1) lookup
$existingEmails = User::pluck('email')->flip();
foreach ($newUsers as $userData) {
if (!$existingEmails->has($userData['email'])) {
User::create($userData);
}
}
Laravel Pulse Setup
# Install Pulse
composer require laravel/pulse
# Publish config and migrations
php artisan vendor:publish --provider="Laravel\Pulse\PulseServiceProvider"
php artisan migrate
# Access at /pulse (protected by gate)
// config/pulse.php - Configure recorders
'recorders' => [
\Laravel\Pulse\Recorders\CacheInteractions::class => [
'enabled' => true,
'sample_rate' => 1,
],
\Laravel\Pulse\Recorders\Exceptions::class => [
'enabled' => true,
],
\Laravel\Pulse\Recorders\Queues::class => [
'enabled' => true,
],
\Laravel\Pulse\Recorders\SlowQueries::class => [
'enabled' => true,
'threshold' => 100, // ms
],
\Laravel\Pulse\Recorders\SlowRequests::class => [
'enabled' => true,
'threshold' => 1000, // ms
],
],
// app/Providers/AppServiceProvider.php - Gate access
use Laravel\Pulse\Facades\Pulse;
public function boot()
{
Pulse::authorize(function ($request) {
return $request->user()?->isAdmin();
});
}
Performance Checklist
| Area | Optimization |
|---|---|
| Config | config:cache, route:cache, view:cache |
| Queries | Eager load, indexes, select specific columns |
| Big O | Avoid nested loops, use keyBy/groupBy for O(1) lookups |
| Caching | Redis for sessions, cache, and queues |
| Assets | Vite minification, CDN, compression |
| PHP | OPcache enabled, JIT compilation |
Common Pitfalls
- Caching forever - Always set TTL, stale data causes bugs
- Cache stampede - Use atomic locks for expensive operations
- Over-eager loading - Only load relationships you need
- Missing indexes - Add indexes for WHERE, ORDER BY columns
- O(n²) nested loops - Use groupBy() or keyBy() for O(1) lookups
- contains() in loops - Use flip()->has() instead for O(1) lookups
Profiling Tools
// Laravel Debugbar (development)
composer require barryvdh/laravel-debugbar --dev
// Clockwork (development)
composer require itsgoingd/clockwork --dev
// Custom timing
$start = microtime(true);
// ... operation
$duration = microtime(true) - $start;
Log::info("Operation took {$duration}s");
// Memory usage
$memory = memory_get_peak_usage(true) / 1024 / 1024;
Log::info("Peak memory: {$memory}MB");
Related Commands
- /laravel-agent:db:optimize - Database optimization
Related Skills
- laravel-database - Query patterns and N+1 fixes
- laravel-queue - Offload work to background jobs