Performance Optimization
Best practices and techniques for optimizing Core PHP Framework applications.
Database Optimization
Eager Loading
Prevent N+1 queries with eager loading:
php
// ❌ Bad - N+1 queries
$posts = Post::all();
foreach ($posts as $post) {
echo $post->author->name; // Query per post
echo $post->category->name; // Another query per post
}
// ✅ Good - 3 queries total
$posts = Post::with(['author', 'category'])->get();
foreach ($posts as $post) {
echo $post->author->name;
echo $post->category->name;
}Query Optimization
php
// ❌ Bad - fetches all columns
$posts = Post::all();
// ✅ Good - only needed columns
$posts = Post::select(['id', 'title', 'created_at'])->get();
// ✅ Good - count instead of loading all
$count = Post::count();
// ❌ Bad
$count = Post::all()->count();
// ✅ Good - exists check
$exists = Post::where('status', 'published')->exists();
// ❌ Bad
$exists = Post::where('status', 'published')->count() > 0;Chunking Large Datasets
php
// ❌ Bad - loads everything into memory
$posts = Post::all();
foreach ($posts as $post) {
$this->process($post);
}
// ✅ Good - process in chunks
Post::chunk(1000, function ($posts) {
foreach ($posts as $post) {
$this->process($post);
}
});
// ✅ Better - lazy collection
Post::lazy()->each(function ($post) {
$this->process($post);
});Database Indexes
php
// Migration
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('slug')->unique(); // Index for lookups
$table->string('status')->index(); // Index for filtering
$table->foreignId('workspace_id')->constrained(); // Foreign key index
// Composite index for common query
$table->index(['workspace_id', 'status', 'created_at']);
});Caching Strategies
Model Caching
php
use Illuminate\Support\Facades\Cache;
class Post extends Model
{
public static function findCached(int $id): ?self
{
return Cache::remember(
"posts.{$id}",
now()->addHour(),
fn () => self::find($id)
);
}
protected static function booted(): void
{
// Invalidate cache on update
static::updated(fn ($post) => Cache::forget("posts.{$post->id}"));
static::deleted(fn ($post) => Cache::forget("posts.{$post->id}"));
}
}Query Result Caching
php
// ❌ Bad - no caching
public function getPopularPosts()
{
return Post::where('views', '>', 1000)
->orderByDesc('views')
->limit(10)
->get();
}
// ✅ Good - cached for 1 hour
public function getPopularPosts()
{
return Cache::remember('posts.popular', 3600, function () {
return Post::where('views', '>', 1000)
->orderByDesc('views')
->limit(10)
->get();
});
}Cache Tags
php
// Tag cache for easy invalidation
Cache::tags(['posts', 'popular'])->put('popular-posts', $posts, 3600);
// Clear all posts cache
Cache::tags('posts')->flush();Redis Caching
php
// config/cache.php
'default' => env('CACHE_DRIVER', 'redis'),
'stores' => [
'redis' => [
'driver' => 'redis',
'connection' => 'cache',
'lock_connection' => 'default',
],
],Asset Optimization
CDN Integration
php
// Use CDN helper
<img src="{{ cdn('images/hero.jpg') }}" alt="Hero">
// With transformations
<img src="{{ cdn('images/hero.jpg', ['width' => 800, 'quality' => 85]) }}">Image Optimization
php
use Core\Media\Image\ImageOptimizer;
$optimizer = app(ImageOptimizer::class);
// Automatic optimization
$optimizer->optimize($imagePath, [
'quality' => 85,
'max_width' => 1920,
'strip_exif' => true,
'convert_to_webp' => true,
]);Lazy Loading
blade
{{-- Lazy load images --}}
<img src="{{ cdn($image) }}" loading="lazy" alt="...">
{{-- Lazy load thumbnails --}}
<img src="{{ lazy_thumbnail($image, 'medium') }}" loading="lazy" alt="...">Code Optimization
Lazy Loading Modules
Modules only load when their events fire:
php
// Module Boot.php
public static array $listens = [
WebRoutesRegistering::class => 'onWebRoutes',
];
// Only loads when WebRoutesRegistering fires
// Saves memory and boot timeDeferred Service Providers
php
<?php
namespace Mod\Analytics;
use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Support\DeferrableProvider;
class AnalyticsServiceProvider extends ServiceProvider implements DeferrableProvider
{
public function register(): void
{
$this->app->singleton(AnalyticsService::class);
}
public function provides(): array
{
return [AnalyticsService::class];
}
}Configuration Caching
bash
# Cache configuration
php artisan config:cache
# Clear config cache
php artisan config:clearRoute Caching
bash
# Cache routes
php artisan route:cache
# Clear route cache
php artisan route:clearQueue Optimization
Queue Heavy Operations
php
// ❌ Bad - slow request
public function store(Request $request)
{
$post = Post::create($request->validated());
// Slow operations in request cycle
$this->generateThumbnails($post);
$this->generateOgImage($post);
$this->notifySubscribers($post);
return redirect()->route('posts.show', $post);
}
// ✅ Good - queued
public function store(Request $request)
{
$post = Post::create($request->validated());
// Queue heavy operations
GenerateThumbnails::dispatch($post);
GenerateOgImage::dispatch($post);
NotifySubscribers::dispatch($post);
return redirect()->route('posts.show', $post);
}Job Batching
php
use Illuminate\Bus\Batch;
use Illuminate\Support\Facades\Bus;
Bus::batch([
new ProcessPost($post1),
new ProcessPost($post2),
new ProcessPost($post3),
])->then(function (Batch $batch) {
// All jobs completed successfully
})->catch(function (Batch $batch, Throwable $e) {
// First batch job failure
})->finally(function (Batch $batch) {
// Batch finished
})->dispatch();Livewire Optimization
Lazy Loading Components
blade
{{-- Load component when visible --}}
<livewire:post-list lazy />
{{-- Load on interaction --}}
<livewire:comments lazy on="click" />Polling Optimization
php
// ❌ Bad - polls every 1s
<div wire:poll.1s>
{{ $count }} users online
</div>
// ✅ Good - polls every 30s
<div wire:poll.30s>
{{ $count }} users online
</div>
// ✅ Better - poll only when visible
<div wire:poll.visible.30s>
{{ $count }} users online
</div>Debouncing
blade
{{-- Debounce search input --}}
<input
type="search"
wire:model.live.debounce.500ms="search"
placeholder="Search..."
>Response Optimization
HTTP Caching
php
// Cache response for 1 hour
return response($content)
->header('Cache-Control', 'public, max-age=3600');
// ETag caching
$etag = md5($content);
if ($request->header('If-None-Match') === $etag) {
return response('', 304);
}
return response($content)
->header('ETag', $etag);Gzip Compression
php
// config/app.php (handled by middleware)
'middleware' => [
\Illuminate\Http\Middleware\HandleCors::class,
\Illuminate\Http\Middleware\ValidatePostSize::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
],Response Streaming
php
// Stream large files
return response()->streamDownload(function () {
$handle = fopen('large-file.csv', 'r');
while (!feof($handle)) {
echo fread($handle, 8192);
flush();
}
fclose($handle);
}, 'download.csv');Monitoring Performance
Query Logging
php
// Enable query log in development
if (app()->isLocal()) {
DB::enableQueryLog();
}
// View queries
dd(DB::getQueryLog());Telescope
bash
# Install Laravel Telescope
composer require laravel/telescope --dev
php artisan telescope:install
php artisan migrateClockwork
bash
# Install Clockwork
composer require itsgoingd/clockwork --devApplication Performance
php
// Measure execution time
$start = microtime(true);
// Your code here
$duration = (microtime(true) - $start) * 1000; // milliseconds
Log::info("Operation took {$duration}ms");Load Testing
Using Apache Bench
bash
# 1000 requests, 10 concurrent
ab -n 1000 -c 10 https://example.com/Using k6
javascript
// load-test.js
import http from 'k6/http';
export let options = {
vus: 10, // 10 virtual users
duration: '30s',
};
export default function () {
http.get('https://example.com/api/posts');
}bash
k6 run load-test.jsBest Practices Checklist
Database
- [ ] Use eager loading to prevent N+1 queries
- [ ] Add indexes to frequently queried columns
- [ ] Use
select()to limit columns - [ ] Chunk large datasets
- [ ] Use
exists()instead ofcount() > 0
Caching
- [ ] Cache expensive query results
- [ ] Use Redis for session/cache storage
- [ ] Implement cache tags for easy invalidation
- [ ] Set appropriate cache TTLs
Assets
- [ ] Optimize images before uploading
- [ ] Use CDN for static assets
- [ ] Enable lazy loading for images
- [ ] Generate responsive image sizes
Code
- [ ] Queue heavy operations
- [ ] Use lazy loading for modules
- [ ] Cache configuration and routes
- [ ] Implement deferred service providers
Frontend
- [ ] Minimize JavaScript bundle size
- [ ] Debounce user input
- [ ] Use lazy loading for Livewire components
- [ ] Optimize polling intervals
Monitoring
- [ ] Use Telescope/Clockwork in development
- [ ] Log slow queries
- [ ] Monitor cache hit rates
- [ ] Track job queue performance