<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged eloquent</title>
<link>https://coderlegion.com/tag/eloquent</link>
<description>Powered by Question2Answer</description>
<item>
<title>Introducing The Atomic Query Construction (AQC) Design Pattern</title>
<link>https://coderlegion.com/14221/introducing-the-atomic-query-construction-aqc-design-pattern</link>
<description>&lt;p&gt;When I started my career as a developer, one of my senior team members used to construct queries in core PHP using concatenation based on different parameters. Once the construction was complete, he would echo it for different use cases and run it in PHPMyAdmin or SQLyog. Once satisfied, he’d move on. I always liked the way he built those queries.&lt;/p&gt;
&lt;p&gt;Later, when I moved on to CodeIgniter and eventually Laravel, I noticed something: Developers often write SQL or ORM-based queries buried deep inside controllers, services, views, event listeners — even helpers. This scattered approach leads to redundancy and painful maintenance. We’ve all seen this.&lt;/p&gt;
&lt;p&gt;This always bugged me. Why repeat the same logic scattered across the application when it can be centralized? &lt;/p&gt;
&lt;p&gt;Being an OOP geek, I finally settled on a pattern that works cleanly and elegantly. To counter this problem, I am introducing a new design principle: Atomic Query Construction — or AQC. It’s a design approach where each query lives in its own class. You write it once and use it everywhere. It’s structured, predictable, and aligned with better software design principles.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-is-aqc-&quot;&gt;What is AQC?&lt;/h3&gt;
&lt;p&gt;AQC stands for Atomic Query Construction. At its core, it’s a principle of isolating each query into its own class. Each of these classes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Accepts Parameters&lt;/li&gt;
&lt;li&gt;Constructs Query based on those parameters for different use cases&lt;/li&gt;
&lt;li&gt;Returns Results&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You only call this class whenever that specific query is needed — nothing else. It becomes the single source of truth for that operation.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;rules-of-the-pattern&quot;&gt;Rules of the Pattern&lt;/h3&gt;
&lt;h5 id=&quot;1-one-class-one-responsibility&quot;&gt;1. One Class, One Responsibility&lt;/h5&gt;
&lt;p&gt;Every class should focus on a single, well-defined query. Stick to the Single Responsibility Principle (SRP).&lt;/p&gt;
&lt;h5 id=&quot;2-accept-parameters&quot;&gt;2. Accept Parameters&lt;/h5&gt;
&lt;p&gt;Each class should accept parameters — filters, identifiers, flags — whatever’s needed to shape the query dynamically.&lt;/p&gt;
&lt;h5 id=&quot;3-construct-the-query-internally&quot;&gt;3. Construct the Query Internally&lt;/h5&gt;
&lt;p&gt;Based on the parameters, the class should build and return a query.&lt;/p&gt;
&lt;h5 id=&quot;4-single-source-of-truth&quot;&gt;4. Single Source of Truth&lt;/h5&gt;
&lt;p&gt;Anywhere in the app where you need this data — whether in a controller, event, or service — call this class. Do NOT rewrite the query logic elsewhere.&lt;/p&gt;
&lt;h5 id=&quot;5-handle-method&quot;&gt;5. Handle Method&lt;/h5&gt;
&lt;p&gt;Each class should expose a single public method, handle(). It can have private helpers internally, but the interface stays clean.&lt;/p&gt;
&lt;h5 id=&quot;6-consistent-naming&quot;&gt;6. Consistent Naming&lt;/h5&gt;
&lt;p&gt;Class names must be clear and consistent. For a resource like &lt;code&gt;Product&lt;/code&gt;, your AQC classes might be:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GetAllProducts&lt;/li&gt;
&lt;li&gt;GetProduct&lt;/li&gt;
&lt;li&gt;StoreProduct&lt;/li&gt;
&lt;li&gt;UpdateProduct&lt;/li&gt;
&lt;li&gt;DeleteProduct&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;benefits&quot;&gt;Benefits&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Modular&lt;/strong&gt;: Every query is isolated and self-contained. You know exactly where to look when changes are needed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reusable&lt;/strong&gt;: Once written, the query class can be reused across the app with different params or use cases.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Flexible&lt;/strong&gt;: It supports multiple scenarios (admin view, frontend listing, etc.) using dynamic input.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Clean Separation&lt;/strong&gt;: Keeps your controllers, services, and views clean. No query logic there — just calls to your AQC classes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Testable&lt;/strong&gt;: Because logic is in isolated classes, it’s easy to write unit tests without involving controllers or database state.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;implementation-example&quot;&gt;Implementation Example&lt;/h3&gt;
&lt;p&gt;Let’s say we’re working with a &lt;code&gt;Product&lt;/code&gt; model in a typical eCommerce app. First, create a folder:  &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;app/AQC/Product/
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GetAllProducts.php&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GetProduct.php&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;StoreProduct.php&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UpdateProduct.php&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DeleteProduct.php&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;“While I’ll use Laravel in the code examples, this pattern is framework-agnostic and can be adapted to any OOP-based backend environment — Node.js, PHP, Python, etc.”&lt;/p&gt;
&lt;p&gt;GetAllProducts.php&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace App\AQC\Product;

use App\Models\Product;

class GetAllProducts
{
    public static function handle($params = [], $paginate = true, $scenario = 'default')
    {
        $productObj = Product::latest('id');

        if (isset($params['category_id']) &amp;amp;&amp;amp; $params['category_id'] &amp;gt; 0) {
            $productObj-&amp;gt;where('category_id', $params['category_id']);
        }

        if (isset($params['brand_id']) &amp;amp;&amp;amp; $params['brand_id'] &amp;gt; 0) {
            $productObj-&amp;gt;where('brand_id', $params['brand_id']);
        }

        // add more conditions for different use cases

        switch ($scenario) {
            case 'minimal':
                $productObj-&amp;gt;select(['id', 'name']);
                break;
            case 'compact':
                $productObj-&amp;gt;select(['id', 'name', 'price', 'image']);
                break;
            case 'admin':
                $productObj-&amp;gt;select(['id', 'name', 'price', 'sku', 'image', 'stock', 'cost']);
                break;
            default:
                $productObj-&amp;gt;select('*');
        }

        return $paginate
            ? $productObj-&amp;gt;paginate(Product::PAGINATE)
            : $productObj-&amp;gt;get();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Usage:  &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Admin listing
$products = GetAllProducts::handle(); 
// return paginated product order id latest 

// Frontend product listing
$products = GetAllProducts::handle($request-&amp;gt;all(), true, 'compact');
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;GetProduct.php&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace App\AQC\Product;
use App\Models\Product;

class GetProduct
{
    public static function handle($id, $params = [], $scenario = 'default')
    {
        $productObj = Product::where('id', $id);

        if (isset($params['active']) &amp;amp;&amp;amp; $params['active']) {
            $productObj-&amp;gt;where('active', $params['active']);
        }

        // add more conditions for different use cases

        switch ($scenario) {
            case 'minimal':
                $productObj-&amp;gt;select(['id', 'name']);
                break;
            case 'compact':
                $productObj-&amp;gt;select(['id', 'name', 'price', 'image']);
                break;
            case 'admin':
                $productObj-&amp;gt;select(['id', 'name', 'price', 'sku', 'image', 'stock', 'cost']);
                break;
            default:
                $productObj-&amp;gt;select('*');
        }

        return $productObj-&amp;gt;first();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Usage:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Admin edit screen
$product = GetProduct::handle($productID, [], 'admin');

// Frontend product detail
$product = GetProduct::handle($productID, [], 'compact');
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;StoreProduct.php&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace App\AQC\Product;
use App\Models\Product;

class StoreProduct
{
    public static function handle($params)
    {
        $product = new Product();
        $product-&amp;gt;fill($params);

        if (isset($params['type']) &amp;amp;&amp;amp; $params['type'] === 'combo') {
            $product-&amp;gt;is_combo = true;
        }

        if (!isset($params['sku'])) {
            $product-&amp;gt;sku = self::generateSku($params);
        }

        $product-&amp;gt;save();
        return $product;
    }

    private static function generateSku($params)
    {
        // SKU generation logic
        return 'SKU-' . rand(1000, 9999);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;UpdateProduct.php&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace App\AQC\Product;
use App\Models\Product;

class UpdateProduct
{
    public static function handle($id, $params)
    {
        $product = Product::find($id);

        if (!$product) return null;

        if (isset($params['cost'])) $product-&amp;gt;cost = $params['cost'];
        if (isset($params['price'])) $product-&amp;gt;price = $params['price'];
        if (isset($params['stock'])) $product-&amp;gt;stock = $params['stock'];

        // add more fields as needed

        $product-&amp;gt;save();
        return $product;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;DeleteProduct.php&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace App\AQC\Product;
use App\Models\Product;

class DeleteProduct
{
    public static function handle($params = [])
    {
        $productsObj = Product::query();

        if (!empty($params['category_id'])) {
            $productsObj-&amp;gt;where('category_id', $params['category_id']);
        }

        if (!empty($params['brand_id'])) {
            $productsObj-&amp;gt;where('brand_id', $params['brand_id']);
        }

        if (!empty($params['product_id'])) {
            $productsObj-&amp;gt;where('id', $params['product_id']);
        }

        if (!$product) return false;

        $productObj-&amp;gt;delete();

        return true;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h3&gt;
&lt;p&gt;Each class builds the query, covers various scenarios, and cleanly separates logic. You can now reuse queries across your entire app — controllers, API endpoints, and admin panels — without rewriting anything.&lt;/p&gt;
&lt;p&gt;When a new requirement comes in, you know exactly where to go. No more chasing query logic across 20 files.&lt;/p&gt;
&lt;p&gt;“With AQC, every query has a home. No more hunting across layers of your app to find and fix logic. Write it once. Use it everywhere. Welcome to clean, atomic thinking.”&lt;/p&gt;
&lt;p&gt;If you try AQC in your projects, I’d love to hear how it works for you — or what you’d improve.&lt;/p&gt;
&lt;p&gt;I’ll be sharing more Laravel architecture patterns soon. Follow along if you’re into clean, scalable code.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;If you’d like personal mentorship or 1-on-1 help with &lt;code&gt;AQC Design Pattern&lt;/code&gt;, I’m available on &lt;a rel=&quot;nofollow&quot; href=&quot;https://topmate.io&quot;&gt;Topmate.io&lt;/a&gt; here.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://topmate.io/raheelshan/&quot;&gt;Schedule a booking&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;If you found this post helpful, consider supporting my work — it means a lot.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://raheelshan.com/payment&quot;&gt;Support My Work&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/14221/introducing-the-atomic-query-construction-aqc-design-pattern</guid>
<pubDate>Wed, 08 Apr 2026 10:17:35 +0000</pubDate>
</item>
</channel>
</rss>