Laravel’s Eloquent ORM ( Object Relational mapping ) is a powerful tool for working with databases in our Laravel applications. It provides an intuitive interface for defining relationships between models and querying related data. When working with Laravel and complex database queries, it’s crucial to optimize our code for speed and efficiency.
In this blog post, we will explore how to transform a raw SQL subquery into an optimized Laravel query, and we will look at some additional examples as well for our solid understanding.
Let’s Start with one of the following raw SQL query and then we will see how we can transform that using Laravel Eloquent,
SELECT `p`.`id`, `p`.`name`, `p`.`img`, `p`.`safe_name`, `p`.`sku`, `p`.`status_id` FROM `products` p WHERE `p`.`id` IN ( SELECT `product_id` FROM `product_category` WHERE `category_id` IN ('223', '15') ) AND `p`.`active`=1
This above query retrieves specific product details for active products that belong to certain categories. While it works, converting this to Laravel’s query builder can offer better readability and potential performance improvements.
The Above Raw SQL Query, can be written in laravel using Two Approaches,
Approach 1, ( using JOINS )
$products = DB::table('products as p') ->select('p.id', 'p.name', 'p.img', 'p.safe_name', 'p.sku', 'p.status_id') ->join('product_category as pc', 'p.id', '=', 'pc.product_id') ->whereIn('pc.category_id', [223, 15]) ->where('p.active', 1) ->distinct() ->get();
Let’s Break down the above query
1. Indexing: Ensure that product_id and category_id in the product_category table, and id and active in the products table are properly indexed.
2. Chunking: If dealing with large datasets, consider using chunking:
DB::table('products as p') ->select(/* ... */) ->join('product_category as pc', 'p.id', '=', 'pc.product_id') ->whereIn('pc.category_id', [223, 15]) ->where('p.active', 1) ->distinct() ->chunk(1000, function ($products) { foreach ($products as $product) { // Process each product } });
3. Eager Loading: If you’re working with Eloquent models and need to access related data, use eager loading to prevent N+1 query problems.
Approach 2, ( Without using JOIN)
$products = DB::table('products as p') ->select('p.id', 'p.name', 'p.img', 'p.safe_name', 'p.sku', 'p.status_id) ->whereIn('p.id', function($query) { $query->select('product_id') ->from('product_category') ->whereIn('category_id', [223, 15]); }) ->where('p.active', 1) ->get();
Let’s Break down the above query.
Let’s explore some more couple of examples to solidify the concept.
Example 1: Filtering Orders by Customer and Date Range
Raw SQL:
SELECT `o`.`id`, `o`.`order_date`, `o`.`total_amount` FROM `orders` o WHERE `o`.`customer_id` IN ( SELECT `id` FROM `customers` WHERE `country` = 'USA' ) AND `o`.`order_date` BETWEEN '2023-01-01' AND '2023-12-31'
Optimized Laravel Eloquent Query.
$orders = DB::table('orders as o') ->select('o.id', 'o.order_date', 'o.total_amount') ->join('customers as c', 'o.customer_id', '=', 'c.id') ->where('c.country', 'USA') ->whereBetween('o.order_date', ['2023-01-01', '2023-12-31']) ->get();
Optimizing database queries in Laravel involves more than just translating raw SQL. By leveraging Laravel’s query builder, using joins instead of subqueries, and applying performance techniques like indexing and chunking, we can significantly improve our application’s database performance.