Go back Laravel Optimization Playbook: Conquering the 7 Levels of Speed and Efficiency /* by Sachin Sanchania - December 12, 2024 */ Tech Update LaravelLaravel ApplicationsLaravel DevelopmentOptimizationPHP Laravel performance optimization is critical for creating fast and efficient applications. As a Laravel developer, starting with “just making it work” is common, but as your application grows, performance quickly becomes a priority. This blog will guide you through essential optimization techniques to reduce memory usage and improve processing time, moving from foundational fixes to advanced performance improvements. We’ll begin with the basics, like eager loading, and advance to sophisticated lazy loading techniques that can cut memory costs by up to 90%. Optimizing a Laravel application often feels like scaling a mountain step builds on the last, taking you from beginner fixes to expert-level enhancements that significantly boost speed and resource efficiency. By the end of this guide, you’ll have the tools to transform your application from sluggish to supercharged, making it faster and more responsive for your users. Measuring Your Performance Gains $posts_count = $posts->count(); // No. Of Posts$users_count = $users->count(); // No. Of Users $time_taken = round(microtime(2) - LARAVEL_START, 2) . ' Second'; // Time Taken $memory_used = round((memory_get_peak_usage() / 1024 / 1024), 2) . ' MB'; // Memory Used Level 1: Eager Loading — Crush the N+1 Problem from the Start When you first begin optimizing Laravel, the initial hurdle is often the N+1 query problem. In simple terms, N+1 problems occur when additional queries are generated for each item in a dataset, multiplying the database load. Eager loading resolves this by loading related models in a single query. $posts = Post::with('user')->get(); No. Of PostsNo. Of UsersMemory UsedTime Taken20,000100038.90 MB0.15 Second Level 2: Selective Loading — Data You Need, Nothing More At Level 2, you start questioning whether you really need to load all the data. Is every column essential for your task? Instead of loading everything, you select only the necessary columns. This simplifies your queries, reduces memory usage, and makes your app faster and more efficient. $posts = Post::with('user:id,name')->select('id', 'user_id', 'title')->get(); No. Of PostsNo. Of UsersMemory UsedTime Taken20000100033.20 MB0.13 Second Level 3: Split Query — Optimize with Smart Separation You’re stepping up your game now. “Eager loading isn’t always the answer,” you realize. Instead, you manually separate queries, fetching posts first and then retrieving users in a more controlled and efficient way. $posts = Post::select('id','user_id','title')->get(); $users = User::query()->whereIn('id', $posts->pluck('user_id')->unique())->pluck('name', 'id'); No. Of PostsNo. Of UsersMemory UsedTime Taken20000100024.71 MB0.12 Second Level 4: Database Filtering — Advanced Querying You’re exploring new techniques and letting the database do more work. Applying distinct filtering directly in your queries can speed up data processing and reduce the load on Laravel. This approach improves performance while keeping memory usage in check. $query = Post::query(); $posts = (clone $query)->select('id', 'user_id', 'title')->get(); $users = User::query() ->whereIn('id', (clone $query)->select('user_id')->distinct()) ->get(); No. Of PostsNo. Of UsersMemory UsedTime Taken20000100024.71 MB0.07 Second Level 5: Speed Mode On — Skip Eloquent with toBase() At Level 5, you take optimization further by using the toBase() method to bypass Laravel’s Eloquent overhead. This approach retrieves raw data directly, reducing processing time and memory usage and making it ideal for efficiently handling large datasets. toBase() allows you to bypass the Eloquent-specific methods and work with the more flexible, base query builder in Laravel when necessary. $query = Post::query(); $posts = (clone $query)->select('id', 'user_id', 'title') ->toBase() ->get(); $users = User::whereIn('id', (clone $query) ->select('user_id')->distinct()) ->toBase() ->get(); No. Of PostsNo. Of UsersMemory UsedTime Taken20000100012.90 MB0.04 Second Level 6: Load Data in Chunks — The Power Move Now you handle large datasets efficiently by using chunkById. This method breaks data into smaller, manageable chunks, preventing memory overload and ensuring smooth processing, even with massive amounts of data. $query = Post::query(); $posts = collect(); (clone $query)->select('id', 'user_id', 'title') ->toBase() ->orderBy('posts.id') ->chunkById(10000, function ($collection) use (&$posts) { $posts->push(...$collection); }, 'id'); $users = User::whereIn('id', (clone $query) ->select('user_id') ->distinct()) ->toBase() ->get(); No. Of PostsNo. Of UsersMemory UsedTime Taken20000100012.21 MB0.03 Second Level 7: Lazy Loading — Minimal Memory, Maximum Performance Level 7 focuses on mastering lazyById, which efficiently handles large datasets by combining chunking with Laravel’s Lazy Collections. It processes data in chunks of 5,000 records, minimizing memory usage while maintaining high performance. $query = Post::query(); $posts = collect(); (clone $query)->select('id', 'user_id', 'title') ->toBase() ->orderBy('posts.id') ->chunkById(5000, function ($collection) use (&$posts) { $posts->push(...$collection); }, 'id'); $users = User::whereIn('id', (clone $query) ->select('user_id') ->distinct()) ->toBase() ->get(); No. Of PostsNo. Of UsersMemory UsedTime Taken2000010006.70 MB0.02 Second Conclusion You’ve mastered the 7 levels of Laravel optimization, transforming your application into a high-performance powerhouse. By utilizing techniques such as eager loading, chunking, and lazy collections, you’ve significantly decreased memory usage and processing time—reducing memory from 120 MB to just 6.7 MB, achieving an impressive 95% reduction. Remember, enhancing your application is an ongoing journey, so continue refining your approach, experimenting with new strategies, and pushing the boundaries of efficiency.