<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Raheel Shan</title>
    <description>The latest articles on Forem by Raheel Shan (@raheelshan).</description>
    <link>https://forem.com/raheelshan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F605559%2F6c4cc957-0c22-48fe-a1de-aa790e2cd394.jpg</url>
      <title>Forem: Raheel Shan</title>
      <link>https://forem.com/raheelshan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/raheelshan"/>
    <language>en</language>
    <item>
      <title>Your Laravel Queries Are Lying to You</title>
      <dc:creator>Raheel Shan</dc:creator>
      <pubDate>Fri, 27 Mar 2026 14:14:38 +0000</pubDate>
      <link>https://forem.com/raheelshan/your-laravel-queries-are-lying-to-you-4mp7</link>
      <guid>https://forem.com/raheelshan/your-laravel-queries-are-lying-to-you-4mp7</guid>
      <description>&lt;p&gt;How many times does &lt;code&gt;Product::latest()&lt;/code&gt; appear in your codebase?&lt;/p&gt;

&lt;p&gt;If the answer is more than once, you're not DRY — you're just organized duplication. Every repeated query entry point is another interpretation of how that query should behave. Inconsistencies creep in silently, and future changes become a scavenger hunt.&lt;/p&gt;

&lt;p&gt;Atomic Query Construction (AQC) fixes this with one rule: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every model gets exactly one query entry point, inside a dedicated execution class.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Every. Single. Time.&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GetProducts&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Filters, sorting, pagination — all controlled through params. One place to change, one place to debug, zero drift.&lt;/p&gt;

&lt;p&gt;📖 Read the complete article &lt;a href="https://raheelshan.com/posts/atomic-query-construction-design-pattern-eliminating-repeated-query-snippets-for-good" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>cleancode</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Stop Writing Queries Everywhere: The Atomic Habit Your Laravel Codebase Needs</title>
      <dc:creator>Raheel Shan</dc:creator>
      <pubDate>Mon, 23 Mar 2026 07:19:24 +0000</pubDate>
      <link>https://forem.com/raheelshan/stop-writing-queries-everywhere-the-atomic-habit-your-laravel-codebase-needs-4g58</link>
      <guid>https://forem.com/raheelshan/stop-writing-queries-everywhere-the-atomic-habit-your-laravel-codebase-needs-4g58</guid>
      <description>&lt;h2&gt;
  
  
  You Have a Query Problem. You Just Haven’t Admitted It Yet.
&lt;/h2&gt;

&lt;p&gt;Let’s describe a normal Laravel codebase.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  A controller fetches active users&lt;/li&gt;
&lt;li&gt;  A job fetches almost the same users, with one extra condition&lt;/li&gt;
&lt;li&gt;  A service fetches them again, missing a condition&lt;/li&gt;
&lt;li&gt;  A test rebuilds the query from scratch&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Same intent. Different queries. Different results.&lt;/p&gt;

&lt;p&gt;Nobody notices until something breaks. Then everyone starts guessing which version is “correct”.&lt;/p&gt;

&lt;p&gt;That’s the problem.&lt;/p&gt;

&lt;p&gt;Not complexity. Not scale.&lt;/p&gt;

&lt;p&gt;Inconsistency.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem Isn’t Duplication. It’s Drift.
&lt;/h2&gt;

&lt;p&gt;People love saying “don’t repeat yourself”.&lt;/p&gt;

&lt;p&gt;They still write this everywhere:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'active'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then somewhere else:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'active'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;whereNotNull&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'email_verified_at'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then somewhere else:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'active'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'role'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'admin'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you don’t have duplication.&lt;/p&gt;

&lt;p&gt;You have different definitions of the same thing.&lt;/p&gt;

&lt;p&gt;That’s worse.&lt;/p&gt;

&lt;p&gt;Because now your system behaves differently depending on which file you opened.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Rule: One Layer Owns All Queries
&lt;/h2&gt;

&lt;p&gt;There is only one way to stop this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every database query must live in one layer. That layer is AQC.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And once it exists:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Controllers do not write queries&lt;/li&gt;
&lt;li&gt;  Services do not write queries&lt;/li&gt;
&lt;li&gt;  Jobs do not write queries&lt;/li&gt;
&lt;li&gt;  Tests do not write queries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a query exists outside AQC, it’s wrong. No debate. Its violation of AQC.&lt;/p&gt;

&lt;p&gt;Instead of this in a controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'active'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;whereNotNull&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'email_verified_at'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'users.index'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;compact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'users'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GetUsers&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'active'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'users.index'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;compact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'users'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the query has one home.&lt;/p&gt;

&lt;h2&gt;
  
  
  Same Rule Everywhere
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Controller
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GetUsers&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'role'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Service
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GetUsers&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'digest_enabled'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Job
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GetUsers&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'active'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GetUsers&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'active'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nobody writes queries anymore.&lt;/p&gt;

&lt;p&gt;They just use them.&lt;/p&gt;

&lt;p&gt;And guess what? You have composition.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;With AQC, you can make a single query class handle multiple contexts, without repeating yourself or risking divergence.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Look at this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\AQC\User&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Models\User&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GetUsers&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// apply filters conditionally&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'active'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'active'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'active'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'digest_enabled'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'digest_enabled'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'digest_enabled'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'role'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'role'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'role'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Apply sorting when requested otherwise do it on id by default&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'sortBy'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'type'&lt;/span&gt;&lt;span class="p"&gt;])){&lt;/span&gt;
            &lt;span class="nv"&gt;$sortBy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'sortBy'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="nv"&gt;$type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'type'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;    
            &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$sortBy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$type&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'paginate'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PAGINATE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;   
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s what’s happening:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Conditional filters – Active users, digest-enabled users, role-based filtering – all controlled by the caller. Nothing is hard-coded in multiple places.&lt;/li&gt;
&lt;li&gt; Flexible sorting – The query supports dynamic ordering, but defaults to a predictable behavior if nothing is specified.&lt;/li&gt;
&lt;li&gt; Pagination or full collection – One class handles both paginated lists for the UI and full collections for jobs or exports.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is composition in action. One query class serves multiple consumers, each passing their parameters. The base conditions and logic live in one place. No controller, service, or job reimplements a filter or accidentally forgets a condition.&lt;/p&gt;

&lt;p&gt;The beauty? Every consumer gets exactly what it needs without duplicating or diverging query logic. You maintain a single source of truth for fetching users.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Gain (And What You Stop Losing)
&lt;/h2&gt;

&lt;p&gt;When queries live in one place:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  You stop redefining business rules&lt;/li&gt;
&lt;li&gt;  You stop forgetting conditions&lt;/li&gt;
&lt;li&gt;  You stop chasing bugs across files&lt;/li&gt;
&lt;li&gt;  You stop guessing which query is correct&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Change happens once. Behavior updates everywhere.&lt;/p&gt;

&lt;p&gt;That’s it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Counts as a Violation
&lt;/h2&gt;

&lt;p&gt;Let’s make this uncomfortable:&lt;/p&gt;

&lt;p&gt;If you write this anywhere outside AQC:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You just broke the architecture. You just violated the AQC Design pattern rule.&lt;/p&gt;

&lt;p&gt;Doesn’t matter if it’s “just one condition” Doesn’t matter if it’s “temporary” Doesn’t matter if it’s “faster this way”&lt;/p&gt;

&lt;p&gt;It’s wrong.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AQC only works if it’s enforced. Not suggested. But enforced.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Habit
&lt;/h2&gt;

&lt;p&gt;Before writing any query, there are only two options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  It already exists → use it&lt;/li&gt;
&lt;li&gt;  It doesn’t exist → create it in AQC&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is no third option.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Atomic Query Construction&lt;/strong&gt; removes the repeatitive queries by giving every query a single home. One layer. One class per operation. One public method. One parameter signature. Every consumer uses the same query, every time.&lt;/p&gt;

&lt;p&gt;Controllers call AQC classes. Services call AQC classes. Jobs call AQC classes. Tests call AQC classes. The database answers to one layer — and one layer only.&lt;/p&gt;

&lt;p&gt;Stop guessing. Stop duplicating. Stop chasing subtle divergences across files. Build the layer, enforce the rules, and make query discipline a habit.&lt;/p&gt;

&lt;p&gt;This is not optional. This is the foundation of a maintainable, predictable Laravel codebase.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>laravel</category>
      <category>aqcdesignpattern</category>
    </item>
    <item>
      <title>The Reasoning Behind AQC Class Design Choices</title>
      <dc:creator>Raheel Shan</dc:creator>
      <pubDate>Tue, 17 Mar 2026 18:51:43 +0000</pubDate>
      <link>https://forem.com/raheelshan/the-reasoning-behind-aqc-class-design-choices-1o8k</link>
      <guid>https://forem.com/raheelshan/the-reasoning-behind-aqc-class-design-choices-1o8k</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9arnbdx4a882g8265ggd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9arnbdx4a882g8265ggd.jpg" alt="The Reasoning Behind AQC Class Design Choices"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Atomic Query Construction (AQC) design pattern is all about precision, clarity, and predictability. Over time, I’ve developed a consistent structure for AQC classes that may seem restrictive at first glance, but each restriction serves a practical purpose. Let’s break down why AQC classes are defined the way they are.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. No Constructor: Avoiding Dependency Injection
&lt;/h2&gt;

&lt;p&gt;In many design patterns, constructors are used to inject dependencies—services, repositories, or other objects. AQC classes intentionally avoid constructors. Why? Because the AQC philosophy assumes that the underlying service or query builder never changes internally.&lt;/p&gt;

&lt;p&gt;Injecting a dependency through a constructor implies that the class can be swapped or configured differently for each instance. For AQC classes, this is unnecessary overhead. The logic is atomic, deterministic, and isolated. By not requiring a constructor, you eliminate boilerplate, reduce configuration errors, and keep the class focused purely on its query logic.&lt;/p&gt;

&lt;p&gt;It also simplifies instantiation. You just create an instance whenever needed, without worrying about passing services or setup parameters that will never vary.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$getUsers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GetUsers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// simple and predictable&lt;/span&gt;
&lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$getUsers&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'status'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'active'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Non-Static Methods: Encouraging Instance-Based Clarity
&lt;/h3&gt;

&lt;p&gt;Some developers might wonder: why not make &lt;code&gt;handle()&lt;/code&gt; static? A static method would avoid instantiation and look simpler. But static methods carry hidden costs: they obscure state, make testing harder, and encourage global coupling.&lt;/p&gt;

&lt;p&gt;Using non-static methods ensures each instance is independent, even if it doesn’t hold state now. It’s a design discipline. By always creating an object before calling &lt;code&gt;handle()&lt;/code&gt;, the code communicates intent: “this is a self-contained, reusable atomic query unit.”&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Single Public Method (handle) with params Array
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;handle()&lt;/code&gt; method is the only public interface of an AQC class. This makes the API extremely predictable: no guessing which method to call or which method affects the query.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;params&lt;/code&gt; array standardizes input. Whether the query needs a filter, a sort order, or pagination, everything is passed through a single array. This avoids proliferating method signatures and keeps the class atomic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'status'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'active'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'role'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'admin'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'sort'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'created_at'&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GetUsers&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Private methods handle the internal steps, such as building joins, filtering conditions, or selecting columns. They are invisible externally, which keeps the class contract simple and ensures maintainers only focus on the &lt;code&gt;handle&lt;/code&gt; method.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Why This Pattern Works
&lt;/h3&gt;

&lt;p&gt;This design achieves three key goals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;Predictability&lt;/code&gt;: Every AQC class looks and behaves the same way.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;Simplicity&lt;/code&gt;: No unnecessary constructors, no dependency injection, no multiple public methods.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;Flexibility&lt;/code&gt;: While the class is simple externally, private methods allow complex query construction internally.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By following these rules, teams can read, use, and maintain AQC classes without worrying about hidden state, dependencies, or inconsistent public APIs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;The structure of AQC classes—no constructor, non-static methods, single &lt;code&gt;handle()&lt;/code&gt; public method accepting a &lt;code&gt;**params**&lt;/code&gt; array—is deliberate. It enforces atomicity, simplicity, and predictability, which are the core principles of the Atomic Query Construction design pattern. Each class becomes a self-contained unit that can be easily reused, tested, and maintained without overengineering or unnecessary complexity.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>laravel</category>
      <category>aqcdesignpattern</category>
    </item>
    <item>
      <title>Why Atomic Query Construction (AQC) Intentionally Uses Arrays Instead of DTOs</title>
      <dc:creator>Raheel Shan</dc:creator>
      <pubDate>Mon, 16 Mar 2026 21:30:13 +0000</pubDate>
      <link>https://forem.com/raheelshan/why-atomic-query-construction-aqc-intentionally-uses-arrays-instead-of-dtos-3jfa</link>
      <guid>https://forem.com/raheelshan/why-atomic-query-construction-aqc-intentionally-uses-arrays-instead-of-dtos-3jfa</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn8g5sb81ugcsk2nvfo4k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn8g5sb81ugcsk2nvfo4k.png" alt="Why Atomic Query Construction (AQC) Intentionally Uses Arrays Instead of DTOs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One question keeps appearing whenever someone looks at the Atomic Query Construction (AQC) pattern for the first time:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why are parameters passed as arrays instead of DTOs?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At first glance, DTOs sound like the “cleaner” solution. They provide type safety, structure, and explicit contracts. In many architectures, DTOs make perfect sense.&lt;/p&gt;

&lt;p&gt;But AQC is intentionally designed around arrays, and replacing them with DTOs would undermine one of the core ideas of the pattern.&lt;/p&gt;

&lt;p&gt;To understand why, we need to look at how AQC actually works.&lt;/p&gt;

&lt;h2&gt;
  
  
  AQC Is Parameter-Driven
&lt;/h2&gt;

&lt;p&gt;The central idea behind AQC is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Parameters shape the query.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Each parameter activates a specific atomic piece of logic.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'category_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'min_price'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'max_price'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'with'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'brand'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the query class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'category_id'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'category_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'category_id'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'min_price'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'price'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;gt;='&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'min_price'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'max_price'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'price'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;='&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'max_price'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'with'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'with'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each parameter independently contributes to the final query.&lt;/p&gt;

&lt;p&gt;This means query composition is dynamic.&lt;/p&gt;

&lt;p&gt;And this is where arrays become important.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cartesian Flexibility of Parameters
&lt;/h2&gt;

&lt;p&gt;AQC relies on the idea that parameters can combine freely.&lt;/p&gt;

&lt;p&gt;If you have five optional parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;category_id
brand_id
price_min
price_max
with
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You don't just have five scenarios.&lt;/p&gt;

&lt;p&gt;You have dozens of possible combinations.&lt;/p&gt;

&lt;p&gt;Examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;category_id
category_id + brand_id
category_id + price_min
brand_id + price_max
price_min + price_max
category_id + brand_id + with
brand_id + price_min + price_max
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The number of combinations grows quickly.&lt;/p&gt;

&lt;p&gt;Arrays allow this naturally because parameters are simply present or absent.&lt;/p&gt;

&lt;p&gt;Each parameter acts like a switch that activates a piece of query logic.&lt;/p&gt;

&lt;p&gt;DTOs don't work well in this environment because they encourage rigid structures, where every field is predefined and expected.&lt;/p&gt;

&lt;p&gt;AQC is not designed for rigid structures.&lt;/p&gt;

&lt;p&gt;It is designed for combinatorial flexibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  DTOs Introduce Unnecessary Rigidity
&lt;/h2&gt;

&lt;p&gt;A DTO typically looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductQueryDTO&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;?int&lt;/span&gt; &lt;span class="nv"&gt;$categoryId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;?int&lt;/span&gt; &lt;span class="nv"&gt;$brandId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;?int&lt;/span&gt; &lt;span class="nv"&gt;$priceMin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;?int&lt;/span&gt; &lt;span class="nv"&gt;$priceMax&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This immediately creates a few problems.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. DTOs Define Structure Too Early
&lt;/h3&gt;

&lt;p&gt;DTOs force you to define all possible parameters upfront.&lt;/p&gt;

&lt;p&gt;But queries evolve.&lt;/p&gt;

&lt;p&gt;Tomorrow you may add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;min_stock
max_stock
rating
visibility
published_at
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your DTO grows. Then another developer creates another DTO for a slightly different query.&lt;/p&gt;

&lt;p&gt;Soon you have DTOs multiplying.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. DTOs Add an Extra Layer
&lt;/h3&gt;

&lt;p&gt;AQC classes already serve a single purpose: build and execute queries.&lt;/p&gt;

&lt;p&gt;Introducing DTOs means adding a translation layer:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Request → DTO → AQC → Query&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;With arrays the flow stays simple:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Request → AQC → Query&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;AQC intentionally avoids unnecessary layers.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. DTOs Reduce Dynamic Composition
&lt;/h3&gt;

&lt;p&gt;DTOs imply a fixed contract.&lt;/p&gt;

&lt;p&gt;But AQC is built on conditional query pieces.&lt;/p&gt;

&lt;p&gt;Each parameter activates a piece of logic.&lt;/p&gt;

&lt;p&gt;Arrays support that naturally because they allow parameters to appear or disappear without forcing structural constraints.&lt;/p&gt;

&lt;h2&gt;
  
  
  Arrays Match the Philosophy of AQC
&lt;/h2&gt;

&lt;p&gt;AQC is built on a few clear ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  One class = one intention&lt;/li&gt;
&lt;li&gt;  Parameters control query behavior&lt;/li&gt;
&lt;li&gt;  Queries are composed conditionally&lt;/li&gt;
&lt;li&gt;  Controllers remain thin&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Arrays align perfectly with this philosophy.&lt;/p&gt;

&lt;p&gt;Each parameter acts as a trigger for an atomic query segment.&lt;/p&gt;

&lt;p&gt;That makes the query builder inside the AQC class extremely flexible without introducing complexity elsewhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;DTOs are useful in many architectural patterns.&lt;/p&gt;

&lt;p&gt;But AQC is intentionally parameter-driven, and arrays preserve the dynamic nature of query composition.&lt;/p&gt;

&lt;p&gt;Each parameter activates an atomic query piece, and arrays allow those pieces to combine freely into a Cartesian number of possible query scenarios.&lt;/p&gt;

&lt;p&gt;Trying to force DTOs into this pattern doesn't improve it.&lt;/p&gt;

&lt;p&gt;It just makes the system heavier while removing the flexibility AQC was designed to provide.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>aqcdesignpattern</category>
      <category>laravel</category>
    </item>
    <item>
      <title>Atomic Query Construction (AQC) Design Pattern: A Practical CRUD Implementation Guide</title>
      <dc:creator>Raheel Shan</dc:creator>
      <pubDate>Mon, 16 Mar 2026 20:32:16 +0000</pubDate>
      <link>https://forem.com/raheelshan/atomic-query-construction-aqc-design-pattern-a-practical-crud-implementation-guide-38hd</link>
      <guid>https://forem.com/raheelshan/atomic-query-construction-aqc-design-pattern-a-practical-crud-implementation-guide-38hd</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgo5bkmjlimh083lz8g51.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgo5bkmjlimh083lz8g51.png" alt="Atomic Query Construction (AQC) Design Pattern: A Practical CRUD Implementation Guide"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When I first introduced the &lt;strong&gt;Atomic Query Construction (AQC)&lt;/strong&gt; design pattern, the focus was on breaking down monolithic repositories into small, single-purpose query classes. These classes are driven entirely by parameters, not hard-coded logic. That philosophy allows a single class to handle multiple query intentions without method explosion, while keeping code readable and maintainable.&lt;/p&gt;

&lt;p&gt;In this article, I want to show you how to implement AQC for full CRUD operations, following the same parameter-first approach. This is the practical way to apply the pattern in a Laravel application but you are free to adopt this pattern in any language of your preference.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Philosophy Recap
&lt;/h2&gt;

&lt;p&gt;AQC is based on one principle&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One class = one intention. Parameters define the variations.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Folder Structure
&lt;/h2&gt;

&lt;p&gt;A simple organization for CRUD operations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app/
 └─ AQC/
     ├─ Product/
     │   ├─ GetProducts.php
     │   ├─ GetProduct.php
     │   ├─ CreateProduct.php
     │   ├─ UpdateProduct.php
     │   └─ DeleteProduct.php
     │
     └─ User/
         ├─ GetUsers.php
         ├─ GetUser.php
         ├─ CreateUser.php
         ├─ UpdateUser.php
         └─ DeleteUser.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  1. Fetch Multiple Records (GetProducts)  
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\AQC\Product&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Models\Product&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GetProducts&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$productObj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;latest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// apply filters&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'category_id'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'category_id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$productObj&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'category_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'category_id'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'brand_id'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'brand_id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$productObj&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'brand_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'brand_id'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// add more filters according to your requirements&lt;/span&gt;

        &lt;span class="c1"&gt;// select columns&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'columns'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'columns'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
            &lt;span class="nv"&gt;$productObj&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'columns'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$productObj&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'*'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;        

        &lt;span class="c1"&gt;// Apply sorting when requested otherwise do it on id by default&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'sortBy'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'type'&lt;/span&gt;&lt;span class="p"&gt;])){&lt;/span&gt;
            &lt;span class="nv"&gt;$sortBy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'sortBy'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="nv"&gt;$type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'type'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;    
            &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$sortBy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$type&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'paginate'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nv"&gt;$productObj&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PAGINATE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$productObj&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;    
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;if handle method grows create internal helper methods.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\AQC\Product&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Models\Product&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GetProducts&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;latest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;applyFilters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;selectColumns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;applySorting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'paginate'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;handlePagination&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;    
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;applyFilters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;selectColumns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;applySorting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handlePagination&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Fetch a Single Record (GetProduct)  
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\AQC\Product&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Models\Product&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GetProduct&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'slug'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'slug'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'slug'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'sku'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'sku'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'sku'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;firstOrFail&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Product can be fetched based on id, slug or sku.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Beware: Passing 2 parameters togather can result in not found.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  3. Create a Record (CreateProduct)  
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\AQC\Product&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Models\Product&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateProduct&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The parameters are the source of truth. No mapping or rigid arguments required. Create is straightforward.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Update a Record Conditionally (UpdateProduct)  
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\AQC\Product&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Models\Product&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UpdateProduct&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$productObj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Conditional WHERE clauses&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$productObj&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'category_id'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'category_id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$productObj&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'category_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'category_id'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// update provided columns only&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$productObj&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'columns'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;One class handles updating multiple fields, conditionally, depending on what parameters exist. No need for separate methods like &lt;code&gt;updatePrice()&lt;/code&gt; or &lt;code&gt;updateStock()&lt;/code&gt;. &lt;br&gt;
 &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  5. Delete Records Conditionally (DeleteProduct)  
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\AQC\Product&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Models\Product&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DeleteProduct&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'category_id'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'category_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'category_id'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'brand_id'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'brand_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'brand_id'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Conditional deletion using the same class for multiple delete intentions. Flexible, atomic, and predictable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Controller Example
&lt;/h2&gt;

&lt;p&gt;Using AQC inside a controller becomes very clean.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductController&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Controller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GetProducts&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'products.index'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;compact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'products'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GetProduct&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'products.index'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;compact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'product'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;      
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StoreProduct&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="nf"&gt;session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;flash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'message'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Product has been saved successfully.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'products.index'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UpdateProduct&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;flash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'message'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Product has been updated successfully.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'products.index'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DeleteProduct&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="nf"&gt;session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;flash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'message'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Product has been deleted successfully.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'products.index'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why This Works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; Parameter-driven: No rigid methods. Flexible to multiple scenarios.&lt;/li&gt;
&lt;li&gt; Single class = single intention: Each class is atomic.&lt;/li&gt;
&lt;li&gt; Controllers stay thin: All query logic is inside AQC.&lt;/li&gt;
&lt;li&gt; No dependency injection required: Eloquent is enough, because these queries have fixed intentions.&lt;/li&gt;
&lt;li&gt; Reusable atomic filters: Common filters (e.g., active, status, role) can be moved into static helpers to be applied across multiple query classes.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;AQC isn’t about complexity. It’s about clarity, atomicity, and flexibility.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Fetch multiple records? Use &lt;code&gt;GetProducts()&lt;/code&gt; with parameters.&lt;/li&gt;
&lt;li&gt;  Fetch a single record? Use &lt;code&gt;GetProduct()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;  Create, update, delete? Each has its own atomic class with conditional behavior.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When applied correctly, AQC keeps controllers thin, and queries flexible. It scales with your application without creating method explosion or unnecessary abstraction layers.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>aqcdesignpattern</category>
      <category>laravel</category>
    </item>
    <item>
      <title>Laravel Blade Partial Api Pattern</title>
      <dc:creator>Raheel Shan</dc:creator>
      <pubDate>Sat, 14 Mar 2026 17:40:57 +0000</pubDate>
      <link>https://forem.com/raheelshan/laravel-blade-partial-api-pattern-el</link>
      <guid>https://forem.com/raheelshan/laravel-blade-partial-api-pattern-el</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/raheelshan" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F605559%2F6c4cc957-0c22-48fe-a1de-aa790e2cd394.jpg" alt="raheelshan"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/raheelshan/laravel-blade-partial-api-pattern-fetching-data-the-missing-part-4i24" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Laravel Blade Partial API Pattern: Fetching Data — The Missing Part&lt;/h2&gt;
      &lt;h3&gt;Raheel Shan ・ Nov 1 '25&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#programming&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#laravel&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#designpatterns&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>webdev</category>
      <category>programming</category>
      <category>laravel</category>
      <category>designpatterns</category>
    </item>
    <item>
      <title>Write One Query Class Instead of Many: The AQC Design Pattern for Laravel</title>
      <dc:creator>Raheel Shan</dc:creator>
      <pubDate>Thu, 12 Mar 2026 20:04:52 +0000</pubDate>
      <link>https://forem.com/raheelshan/write-one-query-class-instead-of-many-the-aqc-design-pattern-5f6l</link>
      <guid>https://forem.com/raheelshan/write-one-query-class-instead-of-many-the-aqc-design-pattern-5f6l</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3kyt3nfyqqg91jj9b289.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3kyt3nfyqqg91jj9b289.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;When I introduced the &lt;strong&gt;Atomic Query Construction (AQC)&lt;/strong&gt; design pattern, a few people asked: "Is this something new? Or is it just a renamed version of something that already exists?"&lt;/p&gt;

&lt;p&gt;Fair question. And the honest answer is: it's both.&lt;/p&gt;

&lt;p&gt;AQC isn't pulling ideas out of thin air. It's a deliberate combination of three well-established software design principles, packaged into a focused, practical approach to managing query logic. Once you see those foundations clearly, you'll understand not just what AQC does — but why it works.&lt;/p&gt;

&lt;p&gt;The three pillars are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Query Object Pattern&lt;/li&gt;
&lt;li&gt; Single Responsibility Principle (SRP)&lt;/li&gt;
&lt;li&gt; Pipeline / Composition Pattern&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let me walk through each one.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Query Object Pattern
&lt;/h2&gt;

&lt;p&gt;The Query Object pattern is a lesser-known but well-documented pattern from Martin Fowler's Patterns of Enterprise Application Architecture. The idea is straightforward: represent a database query as an object. Instead of scattering query logic across controllers, services, and helpers you encapsulate it in a dedicated class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ActiveProducts&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
          &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'active'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In AQC, the same idea is implemented. A dedicated class is created for specific type for operation. Fetch products for example. See, the class accepts parameters and returns results. The caller doesn't care how the query is built. It just calls the class and gets what it needs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\AQC\Product&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Models\Product&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GetProducts&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nv"&gt;$columns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'*'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;latest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// other code goes here&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$query&lt;/span&gt;
            &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PAGINATE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a query object. It has one job. It accepts inputs. It builds the query internally. It returns results.&lt;/p&gt;

&lt;p&gt;Instead of putting query logic inside controllers, repositories, or helpers, the query lives in its own class. Not a method buried in a repository. Not a static helper in some utility class. The query is the object. It has a name, a namespace, and a clear contract.&lt;/p&gt;

&lt;p&gt;What AQC adds on top of the raw Query Object pattern is the use of dynamic parameters and scenarios to handle multiple use cases within a single class. Instead of creating a separate class for &lt;code&gt;GetActiveProducts()&lt;/code&gt; and &lt;code&gt;GetProductsByCategory()&lt;/code&gt;, you have one &lt;code&gt;GetProducts()&lt;/code&gt; class that handles both and every combination through parameters.&lt;/p&gt;

&lt;p&gt;You call it from a controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Admin panel&lt;/span&gt;
&lt;span class="nv"&gt;$columns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'*'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nv"&gt;$products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GetProducts&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;([],&lt;/span&gt; &lt;span class="nv"&gt;$columns&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Frontend filtered by category&lt;/span&gt;
&lt;span class="nv"&gt;$params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'category_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'active'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nv"&gt;$products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GetProducts&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$columns&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Mobile API - out of stock report&lt;/span&gt;
&lt;span class="nv"&gt;$params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'out_of_stock'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nv"&gt;$products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GetProducts&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$columns&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same class. Same method. Different parameters, different results. That's the &lt;strong&gt;Query Object pattern&lt;/strong&gt; doing its job.&lt;/p&gt;

&lt;p&gt;This is framework agnostic. The pattern doesn't care whether you're using Eloquent, Doctrine, SQLAlchemy, or raw PDO. The concept is the same: encapsulate the query in an object.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Single Responsibility Principle (SRP)
&lt;/h2&gt;

&lt;p&gt;SRP is one of the five SOLID principles. Robert C. Martin's definition is simple: a class should have one, and only one, reason to change.&lt;/p&gt;

&lt;p&gt;Most developers have heard this. But in practice, it gets violated constantly — especially with query logic. A service class ends up with &lt;code&gt;getActiveProducts()&lt;/code&gt;, &lt;code&gt;getProductsByCategory()&lt;/code&gt;, &lt;code&gt;getExpiredProducts()&lt;/code&gt;, and fifteen other methods. The class has dozens of reasons to change. That's the opposite of SRP.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductRepository&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// base methods&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Product&lt;/span&gt; &lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Product&lt;/span&gt; &lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;?Product&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;Collection&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// new fetch methods&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getActiveProducts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'active'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getProductsByCategory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$categoryId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'category_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$categoryId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getActiveProductsByCategory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$categoryId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'active'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                      &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'category_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$categoryId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                      &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getProductsByStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$storeId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'store_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$storeId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getActiveProductsByStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$storeId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'active'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                      &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'store_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$storeId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                      &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// and so on...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AQC enforces SRP at a structural level. Every query gets its own class. One class, one job.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app/
└── AQC/
    └── Product/
        └── CreateProduct.php
        └── UpdateProduct.php
        └── GetAllProducts.php
        └── GetProduct.php           
        └── DeleteProduct.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;GetProducts()&lt;/code&gt; only knows how to fetch a list of products. &lt;code&gt;StoreProduct()&lt;/code&gt; only knows how to save a new product. They don't bleed into each other. When a requirement changes — say, the listing query needs a new filter — you go to exactly one class and change exactly one thing.&lt;br&gt;
&lt;br&gt;
  &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\AQC\Product&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Models\Product&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GetProducts&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nv"&gt;$columns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;latest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// other code goes here&lt;/span&gt;
        &lt;span class="c1"&gt;// always return products (more than one)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;That's SRP in practice.&lt;/p&gt;

&lt;p&gt;This might sound trivial, but it's the reason AQC scales. As your application grows, you add new classes, you don't bloat existing ones. The structure stays flat, discoverable, and predictable regardless of the size of your codebase.&lt;/p&gt;

&lt;p&gt;And this isn't Laravel-specific. Whether you're working in Node.js, Python, or .Net, the principle holds. One class, one query, one reason to change.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Pipeline / Composition Pattern
&lt;/h2&gt;

&lt;p&gt;This is the one people notice last, but it's what makes AQC genuinely powerful.&lt;/p&gt;

&lt;p&gt;Look at the &lt;code&gt;GetProducts()&lt;/code&gt; class below. The &lt;code&gt;handle()&lt;/code&gt; method doesn't do everything in one monolithic block. Internally, it's building the query in stages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Apply filters&lt;/li&gt;
&lt;li&gt;  Select columns based on scenario&lt;/li&gt;
&lt;li&gt;  Apply sorting&lt;/li&gt;
&lt;li&gt;  Handle pagination&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each step is a distinct concern. In a more explicit implementation, these stages become private methods that each receive the query, add their piece, and pass it along.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\AQC\Product&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Models\Product&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GetProducts&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nv"&gt;$columns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;latest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;applyFilters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;selectColumns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;applySorting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'paginate'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;handlePagination&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;    
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;applyFilters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;selectColumns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;applySorting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handlePagination&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a pipeline. The query object passes through a sequence of stages. Each stage does one thing. Each stage is independently readable, testable, and modifiable.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Composition pattern&lt;/strong&gt; takes this further — you build complex behavior by composing smaller, focused pieces rather than writing one massive function. In AQC, you're composing query conditions. In more advanced implementations, you could compose multiple AQC classes together when building complex reporting queries.&lt;/p&gt;

&lt;p&gt;A class that does ten things in one method is a class that will break in ten different ways. Split the stages. Compose the result.&lt;/p&gt;

&lt;p&gt;This is also why AQC classes are naturally easy to unit test. You're not testing a sprawling 200-line method. You're testing a pipeline where each stage has a clear input and output. You can test the filter stage independently. You can test the pagination stage independently. That's composition paying dividends.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Underlying Power of Composition Pattern
&lt;/h2&gt;

&lt;p&gt;One of the most powerful aspects of composition in AQC is how filters combine dynamically based on the parameters passed to the query. Each filter is applied conditionally, which means the query does not follow a fixed path. Instead, every parameter acts like a switch that may or may not add a constraint to the query. When multiple parameters are provided, the resulting query becomes a combination of all applicable filters. In practical terms, this creates a flexible query system where a single query class can support many possible filter combinations without needing dozens of specialized query methods. The behavior is similar to a Cartesian combination of conditions: any subset of parameters can be used together, and the query naturally adapts by composing only the relevant constraints. This keeps the code simple while still allowing a wide range of filtering scenarios.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;applyFilters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// apply requested filters&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'category_id'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'category_id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'category_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'category_id'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'brand_id'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'brand_id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'brand_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'brand_id'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'keyword'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'like'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'%'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'keyword'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'%'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;orWhere&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'sku'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'like'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'%'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'keyword'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'%'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;orWhere&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'description'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'like'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'%'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'keyword'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'%'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'low_stock'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'stock'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;='&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'low_stock_point'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'out_of_stock'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'stock'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'expired'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;whereDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'expired_date'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;today&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'featured'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'is_featured'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'trending'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'is_trending'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'product_id'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'related_product_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'product_id'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a typical Repository or naive Query Object setup, every meaningful filter combination often turns into a separate method or class. Instead of one flexible query that reacts to parameters, developers end up writing many rigid query variants to cover different filtering needs. &lt;br&gt;&lt;br&gt;
Consider the above &lt;strong&gt;applyFilters(),&lt;/strong&gt; it would have been turned into the classes or repository methods listed below.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;getProductsByCategory()&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;getProductsByBrand()&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;getProductsByCategoryAndBrand()&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;searchProductsByKeyword()&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;searchProductsByKeywordAndCategory()&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;searchProductsByKeywordAndBrand()&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;searchProductsByKeywordCategoryAndBrand()&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;getLowStockProducts()&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;getOutOfStockProducts()&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;getExpiredProducts()&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;getFeaturedProducts()&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;getTrendingProducts()&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;getProductsByCategoryAndLowStock()&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;getProductsByBrandAndLowStock()&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;getProductsByCategoryBrandAndLowStock()&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;getFeaturedProductsByCategory()&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;getTrendingProductsByBrand()&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;getExpiredProductsByCategory()&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;getLowStockProductsByBrand()&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;getProductsRelatedToProduct()&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;getFeaturedProductsByCategoryAndBrand()&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;searchFeaturedProductsByKeyword()&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;searchTrendingProductsByKeyword()&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why These Three, Together
&lt;/h2&gt;

&lt;p&gt;Individually, none of these patterns is a complete solution.&lt;/p&gt;

&lt;p&gt;SRP tells you to split things apart — but doesn't tell you how to organize query logic specifically.&lt;/p&gt;

&lt;p&gt;The Query Object pattern gives you the structural template — but doesn't enforce clean internal organization.&lt;/p&gt;

&lt;p&gt;The Pipeline / Composition pattern gives you internal clarity — but doesn't tell you how to structure the class's public contract.&lt;/p&gt;

&lt;p&gt;Together, they give you AQC:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;SRP defines the boundary&lt;/strong&gt;: one class, one query.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;The Query Object pattern defines the structure&lt;/strong&gt;: a named class that encapsulates query construction.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;The Pipeline pattern defines the internals&lt;/strong&gt;: stages that compose to produce the final result.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is a pattern that's modular, reusable, testable, and predictable. Every query has a home. Every home has a consistent contract. Every contract is built in composable stages.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Note on Class Naming and Static Methods
&lt;/h2&gt;

&lt;p&gt;One thing that may look slightly unconventional in this pattern is the class naming. In many object-oriented conventions, class names typically represent entities such as &lt;code&gt;Product&lt;/code&gt;, &lt;code&gt;Order&lt;/code&gt;, or &lt;code&gt;User&lt;/code&gt;. In AQC, however, the class name often represents an action or intent, such as &lt;code&gt;GetProducts()&lt;/code&gt;, &lt;code&gt;GetOrders()&lt;/code&gt;, or &lt;code&gt;GetUsers()&lt;/code&gt;. While this may feel unusual at first, it is not entirely new. Many established patterns, including Query Objects, Actions, and Command patterns, follow the same idea where a class represents a specific operation rather than a domain entity.&lt;/p&gt;

&lt;p&gt;Another stylistic choice in AQC is the use of static methods. Some developers prefer static entry points for simplicity, while others prefer instance methods for better dependency handling or testability. The pattern itself does not require one approach over the other. Teams are free to implement AQC using static or non-static methods depending on their coding standards and architectural preferences.  &lt;/p&gt;

&lt;h2&gt;
  
  
  The Framework Agonist Pattern
&lt;/h2&gt;

&lt;p&gt;I use Laravel, so the examples above use Eloquent. But AQC is not a Laravel pattern. It's a software design pattern that happens to be demonstrated with Laravel.&lt;/p&gt;

&lt;p&gt;You can apply the same thinking in Node.js, Python, .Net Framework or any language you use.&lt;/p&gt;

&lt;p&gt;The three underlying principles — SRP, Query Object, Pipeline/Composition are language-agnostic. AQC is simply an opinionated application of those three principles to the specific problem of managing query logic across a growing application.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;When people see AQC for the first time, it looks deceptively simple. Just a class with a &lt;code&gt;handle()&lt;/code&gt; method. But the simplicity is intentional — it's what you get when three well-understood patterns come together cleanly.&lt;/p&gt;

&lt;p&gt;SRP says: give every query a dedicated home. Query Object says: make the query a first-class object with a clear contract. Pipeline/Composition says: build that query in stages, each one doing exactly one thing.&lt;/p&gt;

&lt;p&gt;That's AQC. Not magic. Not reinvention. Just three good ideas in the right combination.&lt;/p&gt;

&lt;p&gt;If you've been using these principles in your work already, AQC will feel like familiar ground with a concrete name and structure. If you haven't, this is a good place to start.&lt;/p&gt;

&lt;p&gt;Hope this article gives you new way of thinking and writing queries. Let me know what you think of it.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>laravel</category>
      <category>designpatterns</category>
    </item>
    <item>
      <title>Four Ways to Trigger Delete Row from a Table</title>
      <dc:creator>Raheel Shan</dc:creator>
      <pubDate>Sun, 08 Mar 2026 18:29:45 +0000</pubDate>
      <link>https://forem.com/raheelshan/four-ways-to-trigger-delete-row-from-a-table-43nn</link>
      <guid>https://forem.com/raheelshan/four-ways-to-trigger-delete-row-from-a-table-43nn</guid>
      <description>&lt;p&gt;Assume we have this route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Route::delete('/pages/{page}',
    [PageController::class, 'destroy']
)-&amp;gt;name('site.pages.destroy');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every delete must:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Open confirmation modal&lt;/li&gt;
&lt;li&gt; Wait for user confirmation&lt;/li&gt;
&lt;li&gt; Then trigger DELETE request&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We are only discussing how the front end triggers the route.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. One Form Per Row
&lt;/h2&gt;

&lt;p&gt;Each row contains its own form.&lt;/p&gt;

&lt;h3&gt;
  
  
  Blade
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@foreach ($pages as $page)
&amp;lt;tr&amp;gt;
    &amp;lt;td&amp;gt;{{ $page-&amp;gt;title }}&amp;lt;/td&amp;gt;
    &amp;lt;td&amp;gt;
        &amp;lt;form id="delete-form-{{ $page-&amp;gt;id }}"
              action="{{ route('site.pages.destroy', $page-&amp;gt;id) }}"
              method="POST"&amp;gt;
            @csrf
            @method('DELETE')

            &amp;lt;button type="button"
                    onclick="openModal({{ $page-&amp;gt;id }})"&amp;gt;
                Delete
            &amp;lt;/button&amp;gt;
        &amp;lt;/form&amp;gt;
    &amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
@endforeach
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  JavaScript
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script&amp;gt;
let selectedId = null;

function openModal(id) {
    selectedId = id;
    document.getElementById('delete-user-modal').classList.remove('hidden');
}

function submitDelete() {
    document
        .getElementById('delete-form-' + selectedId)
        .submit();
}
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What’s Happening
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  Each row has its own form&lt;/li&gt;
&lt;li&gt;  Modal opens&lt;/li&gt;
&lt;li&gt;  On confirmation → that specific form submits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Works fine.&lt;/p&gt;

&lt;p&gt;If you render 300 rows, you now have 300 forms sitting in your DOM.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Ajax / Fetch DELETE Request
&lt;/h2&gt;

&lt;p&gt;Here, rows do not contain forms.&lt;/p&gt;

&lt;h3&gt;
  
  
  Blade
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@foreach ($pages as $page)
&amp;lt;tr id="row-{{ $page-&amp;gt;id }}"&amp;gt;
    &amp;lt;td&amp;gt;{{ $page-&amp;gt;title }}&amp;lt;/td&amp;gt;
    &amp;lt;td&amp;gt;
        &amp;lt;button type="button"
                onclick="openModal({{ $page-&amp;gt;id }})"&amp;gt;
            Delete
        &amp;lt;/button&amp;gt;
    &amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
@endforeach
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Layout (CSRF Meta)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;meta name="csrf-token" content="{{ csrf_token() }}"&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  JavaScript
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script&amp;gt;
let selectedId = null;

function openModal(id) {
    selectedId = id;
    document.getElementById('delete-user-modal').classList.remove('hidden');
}

function submitDelete() {

    fetch(`/pages/${selectedId}`, {
        method: 'DELETE',
        headers: {
            'X-CSRF-TOKEN': document
                .querySelector('meta[name="csrf-token"]')
                .getAttribute('content'),
            'Content-Type': 'application/json'
        }
    }).then(response =&amp;gt; {
        if (response.ok) {
            document.getElementById('row-' + selectedId).remove();
        }
    });
}
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What’s Happening
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  Modal confirms&lt;/li&gt;
&lt;li&gt;  JavaScript sends DELETE request&lt;/li&gt;
&lt;li&gt;  On success → remove row from DOM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No page reload. Clean UI.&lt;/p&gt;

&lt;p&gt;But you are now responsible for handling errors properly.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. One Form Wrapping the Table
&lt;/h2&gt;

&lt;p&gt;Instead of many forms, use one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Blade
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;form id="delete-form" method="POST"&amp;gt;
    @csrf
    @method('DELETE')

    &amp;lt;input type="hidden" name="page" id="delete-id"&amp;gt;

    &amp;lt;table&amp;gt;
        @foreach ($pages as $page)
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;{{ $page-&amp;gt;title }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;
                &amp;lt;button type="button"
                        onclick="openModal({{ $page-&amp;gt;id }})"&amp;gt;
                    Delete
                &amp;lt;/button&amp;gt;
            &amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        @endforeach
    &amp;lt;/table&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  JavaScript
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script&amp;gt;
let selectedId = null;

function openModal(id) {
    selectedId = id;
    document.getElementById('delete-user-modal').classList.remove('hidden');
}

function submitDelete() {

    const form = document.getElementById('delete-form');

    form.action = `/pages/${selectedId}`;
    form.submit();
}
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What’s Happening
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  One form&lt;/li&gt;
&lt;li&gt;  Hidden input stores ID&lt;/li&gt;
&lt;li&gt;  Action set dynamically&lt;/li&gt;
&lt;li&gt;  Full page reload&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cleaner than method one. Still traditional submission.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Hidden Global Form + Route Placeholder (Preferred)
&lt;/h2&gt;

&lt;p&gt;This is my pattern.&lt;/p&gt;

&lt;p&gt;One hidden form. Outside the table. No ID in the initial action.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hidden Form
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;form method="POST"
      action="{{ route('site.pages.destroy', ['page' =&amp;gt; 0]) }}"
      id="delete-form"
      class="hidden"&amp;gt;
    @csrf
    @method('DELETE')
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice:&lt;/p&gt;

&lt;p&gt;We deliberately pass &lt;code&gt;0&lt;/code&gt; as a placeholder.&lt;/p&gt;

&lt;p&gt;Laravel generates a valid route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/pages/0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will replace &lt;code&gt;0&lt;/code&gt; dynamically.&lt;/p&gt;




&lt;h3&gt;
  
  
  Delete Button
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;button type="button"
        data-modal-target="delete-user-modal"
        data-modal-toggle="delete-user-modal"
        onclick="setID({{ $page-&amp;gt;id }})"&amp;gt;
    Delete
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  JavaScript
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script&amp;gt;
let selectedId = null;

function setID(id) {

    selectedId = id;

    let path = "{{ route('site.pages.destroy', ['page' =&amp;gt; 0]) }}";

    path = path.replace('/0', '/' + id);

    document.getElementById('delete-form').action = path;
}

function submitForm() {
    document.getElementById('delete-form').submit();
}
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Modal Confirmation Button
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;a href="#" onclick="submitForm()"&amp;gt;Yes&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  What’s Happening
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; Route is generated once with placeholder &lt;code&gt;0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; When delete button is clicked → ID is captured&lt;/li&gt;
&lt;li&gt; JavaScript replaces &lt;code&gt;/0&lt;/code&gt; with real ID&lt;/li&gt;
&lt;li&gt; Confirmation button submits the hidden form&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  One form in DOM&lt;/li&gt;
&lt;li&gt;  Clean table markup&lt;/li&gt;
&lt;li&gt;  Proper Laravel route usage&lt;/li&gt;
&lt;li&gt;  No route string hardcoded&lt;/li&gt;
&lt;li&gt;  Centralized delete logic&lt;/li&gt;
&lt;li&gt;  Modal confirmation built-in&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Structurally, this is clean and scalable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bonus: The HTMX Way
&lt;/h2&gt;

&lt;p&gt;If you’re using HTMX, you don’t need custom fetch calls, and you don’t need to manually submit hidden forms.&lt;/p&gt;

&lt;p&gt;HTMX lets you trigger HTTP requests directly from HTML attributes.&lt;/p&gt;

&lt;p&gt;Yes. Just attributes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Basic HTMX Delete Example
&lt;/h2&gt;

&lt;p&gt;First, include HTMX in your layout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script src="https://unpkg.com/htmx.org@1.9.10"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;meta name="csrf-token" content="{{ csrf_token() }}"&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@foreach ($pages as $page)
&amp;lt;tr id="row-{{ $page-&amp;gt;id }}"&amp;gt;
    &amp;lt;td&amp;gt;{{ $page-&amp;gt;title }}&amp;lt;/td&amp;gt;
    &amp;lt;td&amp;gt;
        &amp;lt;button 
            hx-delete="{{ route('site.pages.destroy', $page-&amp;gt;id) }}"
            hx-target="#row-{{ $page-&amp;gt;id }}"
            hx-swap="outerHTML"
            hx-confirm="Are you sure you want to delete this page?"
            hx-headers='{"X-CSRF-TOKEN": "{{ csrf_token() }}"}'
        &amp;gt;
            Delete
        &amp;lt;/button&amp;gt;
    &amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
@endforeach
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What’s Happening
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;hx-delete&lt;/code&gt; sends a DELETE request&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;hx-confirm&lt;/code&gt; shows confirmation popup automatically&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;hx-target&lt;/code&gt; tells HTMX which element to update&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;hx-swap="outerHTML"&lt;/code&gt; removes the row after successful response&lt;/li&gt;
&lt;li&gt;  CSRF token is passed via header&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No custom JavaScript required.&lt;/p&gt;

&lt;p&gt;No hidden forms.&lt;/p&gt;

&lt;p&gt;No manual fetch logic.&lt;/p&gt;

&lt;p&gt;Just attributes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which Method Is Better?
&lt;/h2&gt;

&lt;p&gt;With confirmation required, method one becomes noisy because you must track many forms.&lt;/p&gt;

&lt;p&gt;Method three is acceptable but still tied to page reload behavior.&lt;/p&gt;

&lt;p&gt;Ajax is powerful if you want instant UI updates.&lt;/p&gt;

&lt;p&gt;But from a structural Laravel perspective, the hidden global form with placeholder replacement is the most balanced:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Clean Blade&lt;/li&gt;
&lt;li&gt;  Clean routes&lt;/li&gt;
&lt;li&gt;  Clean confirmation flow&lt;/li&gt;
&lt;li&gt;  Minimal DOM pollution&lt;/li&gt;
&lt;li&gt;  Easy to maintain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It separates concerns properly.&lt;/p&gt;

&lt;p&gt;Table renders data. Modal handles confirmation. Hidden form handles submission.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why HTMX Is Interesting
&lt;/h2&gt;

&lt;p&gt;It gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  AJAX behavior&lt;/li&gt;
&lt;li&gt;  Confirmation&lt;/li&gt;
&lt;li&gt;  DOM update&lt;/li&gt;
&lt;li&gt;  RESTful requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without writing JavaScript logic yourself.&lt;/p&gt;

&lt;p&gt;It keeps your Blade readable.&lt;/p&gt;

&lt;p&gt;It keeps your routes clean.&lt;/p&gt;

&lt;p&gt;It feels like traditional server-driven apps, but dynamic.  &lt;/p&gt;

&lt;p&gt;Hope this gives you clearer picture what you can adopt to delete a row.&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>laravel</category>
      <category>php</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Laravel Blade Partial API Pattern: Fetching Data — The Missing Part</title>
      <dc:creator>Raheel Shan</dc:creator>
      <pubDate>Sat, 01 Nov 2025 11:18:54 +0000</pubDate>
      <link>https://forem.com/raheelshan/laravel-blade-partial-api-pattern-fetching-data-the-missing-part-4i24</link>
      <guid>https://forem.com/raheelshan/laravel-blade-partial-api-pattern-fetching-data-the-missing-part-4i24</guid>
      <description>&lt;p&gt;In the &lt;a href="https://dev.to/raheelshan/laravel-blade-partial-api-pattern-with-htmx-3aj"&gt;previous article&lt;/a&gt;, we built the foundation for what I called &lt;strong&gt;Laravel Blade Partial API Pattern&lt;/strong&gt;. The idea was simple—to make every Blade partial directly accessible through an API-style URL using HTMX, without writing individual controller methods or route definitions.&lt;/p&gt;

&lt;p&gt;That worked fine until we hit one problem: &lt;strong&gt;data&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The route knew how to locate and render a partial, but it didn’t know what data that partial needed. Some partials required a single record (like a product card), others needed multiple records (like a product list), and a few needed data built from complex DTO classes. This article is about that missing piece—how to fetch and pass the correct data automatically.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Problem Recap
&lt;/h3&gt;

&lt;p&gt;The old global route could do this much:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/products/partials/product-card/2&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;→ find &lt;code&gt;`resources/views/products/partials/product-card.blade.php`&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;→ load view&lt;/p&gt;

&lt;p&gt;→ done&lt;/p&gt;

&lt;p&gt;That’s it.&lt;/p&gt;

&lt;p&gt;It didn’t care whether that partial needed one product, a list of products, or a composed object with multiple models. We’re now going to fix that.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Rules (The Contract Between View and Data)
&lt;/h3&gt;

&lt;p&gt;To make the system predictable and maintainable, there need to be rules. After experimenting, I came up with six clear steps that define how a partial route resolves data.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Find the correct partial to load Every request like &lt;code&gt;`/products/partials/product-card/2`&lt;/code&gt; should map directly to &lt;code&gt;`resources/views/products/partials/product-card.blade.php`&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; Find the resource from the first segment The first segment &lt;code&gt;`products`&lt;/code&gt; is always the model reference. It’s converted to singular form &lt;code&gt;`product`&lt;/code&gt; to locate the model: &lt;code&gt;`App\Models\Product`&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; Decide if one or more records are required This comes from the filename prefix. Starts with &lt;code&gt;`products-`&lt;/code&gt; → multiple records Starts with &lt;code&gt;`product-`&lt;/code&gt; → single record&lt;/li&gt;
&lt;li&gt; Extract the DTO or ViewModel annotation At the top of each partial, I can define what data structure it expects:&lt;code&gt;`/** @var \App\ViewModels\ProductCardDTO $model */.`&lt;/code&gt; This tells the route what class to instantiate and which columns the AQC should select.&lt;/li&gt;
&lt;li&gt; Fetch the data using AQC Once the DTO is known, the system calls the AQC Design Pattern class to select the appropriate columns and fetch records from the corresponding model.&lt;/li&gt;
&lt;li&gt; Return the rendered HTML Finally, the resolved data is passed to the partial and returned as raw HTML — ideal for HTMX swaps or inline updates.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Example in Action
&lt;/h3&gt;

&lt;p&gt;Let’s take these partials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/resources/views/products/partials/products-list.blade.php  // multiple
/resources/views/products/partials/product-card.blade.php   // single
/resources/views/products/partials/product-row.blade.php    // single
/resources/views/products/partials/product-quick-view.blade.php // single
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And these URLs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/products/partials/products-list 
/products/partials/product-card/2
/products/partials/product-row/2 
/products/partials/product-quick-view/2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s the global route refactored.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use App\Helpers\PartialApiResolver;

Route::match(['get', 'post'], '{path}', function(string $path, Request $request){
    return App\Helpers\PartialApiResolver::handle($path, $request);
})-&amp;gt;where('path', '.*');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here's the helper class with all 6 steps.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php
namespace App\Helpers;

use Illuminate\Http\Request;
use Illuminate\Support\Str;

class PartialApiResolver
{
    public static function handle(string $path, Request $request)
    {
        $path = trim($path, '/');
        if ($path === '') abort(404);

        $segments = explode('/', $path);
        $id = self::extractId($segments);
        $viewPath = self::viewPath($segments);
        $resourceName = self::detectResource($segments);
        $dtoClass = self::extractDto($segments);
        $data = self::fetchData($resourceName, $id, $dtoClass, $request);

        return view($viewPath, $data);
    }

    private static function extractId(array &amp;amp;$segments): ?int
    {
        $maybeLast = end($segments);
        if (is_numeric($maybeLast)) {
            array_pop($segments);
            return (int) $maybeLast;
        }
        return null;
    }

    private static function viewPath(array $segments): string
    {
        return implode('.', $segments);
    }

    private static function detectResource(array $segments): ?string
    {
        $resourceSegment = $segments[0] ?? null;
        return $resourceSegment ? Str::singular($resourceSegment) : null;
    }

    private static function extractDto(array $segments): ?string
    {
        $bladeFile = resource_path('views/' . implode('/', $segments) . '.blade.php');
        if (!file_exists($bladeFile)) return null;

        $contents = file_get_contents($bladeFile);
        if (preg_match('/@var\s+([A-Za-z0-9_\\\\&amp;lt;&amp;gt;]+)\s+\$[A-Za-z0-9_]+/m', $contents, $m)) {
            return trim($m[1]);
        }
        return null;
    }

    private static function fetchData(?string $resource, ?int $id, ?string $dto, Request $request): array
    {
        if (!$resource) return [];
        $aqcNamespace = "App\\AQC\\" . ucfirst($resource);
        $columns = $dto &amp;amp;&amp;amp; method_exists($dto, 'columns') ? $dto::columns() : '*';

        try {
            if ($id) {
                $aqcClass = "{$aqcNamespace}\\Get" . ucfirst($resource);
                if (!class_exists($aqcClass)) abort(404, "AQC class [$aqcClass] not found.");

                $item = $aqcClass::handle($id, $request-&amp;gt;all(), $columns);
                return [$resource =&amp;gt; $item ?: new ("App\\Models\\" . ucfirst($resource))];
            } else {
                $aqcClass = "{$aqcNamespace}\\Get" . Str::plural($resource);
                if (!class_exists($aqcClass)) abort(404, "AQC class [$aqcClass] not found.");

                $items = $aqcClass::handle($request-&amp;gt;all(), $columns);
                return [Str::plural($resource) =&amp;gt; $items];
            }
        } catch (\Throwable $e) {
            return $id
                ? [$resource =&amp;gt; new ("App\\Models\\" . ucfirst($resource))]
                : [Str::plural($resource) =&amp;gt; collect()];
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Now that you know how we load partials, let’s look at how we prepare their data at a structural level.&lt;/p&gt;

&lt;h2&gt;
  
  
  Note before you move on
&lt;/h2&gt;

&lt;p&gt;To fully understand why I’m using DTO classes and the Atomic Query Construction (AQC) pattern in this setup, don’t skip this part. Both are essential to how the Partial API pattern works under the hood. DTOs define the structure of data your Blade partial expects, while AQC defines how that data is fetched. The rest of this article will make a lot more sense once you grasp both pieces—so read carefully.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Using DTO Classes with Blade Partials
&lt;/h3&gt;

&lt;p&gt;In this pattern, every Blade partial should know exactly what data structure it’s getting — and that’s where DTO classes come in. Instead of passing a random collection of variables from your controller or resolver, define a dedicated DTO (or ViewModel) that represents the data contract for that specific partial. For example, a &lt;code&gt;ProductCardDTO&lt;/code&gt; might expose only &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;price&lt;/code&gt;, and &lt;code&gt;imageUrl&lt;/code&gt;. This makes your Blade file predictable, testable, and IDE-friendly. If you’ve never structured your Blade files around contracts before, you can read my detailed explanation here: 👉 &lt;a href="https://dev.to/raheelshan/stop-treating-your-blade-files-like-trash-bins-give-them-contracts-and-structure-43e9"&gt;Don’t Pass Array or Variables to Laravel Blade Views Instead Do This&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That article shows how to make each Blade file behave like a component with a well-defined interface, keeping logic where it belongs and leaving the template clean. Once you apply that mindset, the Partial API pattern becomes far more maintainable because every endpoint and partial communicates through a stable contract instead of loose variables.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Role of the AQC Design Pattern
&lt;/h3&gt;

&lt;p&gt;Behind the DTOs sits the Atomic Query Construction (AQC) pattern. AQC lets you build reusable, composable query classes that encapsulate all data-fetching logic. Instead of sprinkling &lt;code&gt;Model::query()&lt;/code&gt; all over your project, each AQC class handles one atomic concern — fetching users, filtering products, calculating stats — and can be reused across controllers, APIs, and partials.&lt;/p&gt;

&lt;p&gt;When you combine AQC with DTOs, the flow becomes clean and layered: AQC → DTO → Blade Partial. Your AQC class produces the data. The DTO shapes it. The partial displays it.&lt;/p&gt;

&lt;p&gt;You can explore the full pattern in this article: 👉 &lt;a href="https://dev.to/raheelshan/introducing-the-atomic-query-construction-aqc-design-pattern-25l9"&gt;Introducing the Atomic Query Construction (AQC) Design Pattern&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That post dives deeper into why atomic queries outperform ad-hoc query logic and how they integrate naturally with the Partial API pattern described here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation Notes
&lt;/h3&gt;

&lt;p&gt;Here's what we have done so far.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Detect plural or singular pattern to decide between collection or single model.&lt;/li&gt;
&lt;li&gt;  Check for numeric ID in the URL (if found, load a single record).&lt;/li&gt;
&lt;li&gt;  Parse the partial file to extract the DTO or ViewModel class name from annotations.&lt;/li&gt;
&lt;li&gt;  Pass model and DTO into your AQC pipeline to build queries dynamically.&lt;/li&gt;
&lt;li&gt;  Graceful fallback: if a record is missing, provide a new model instance instead of throwing an error.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This way, even if you hit a partial like &lt;code&gt;/product/partials/product-card/9999&lt;/code&gt;, it still renders without breaking the flow.&lt;/p&gt;




&lt;h3&gt;
  
  
  Why This Works So Well
&lt;/h3&gt;

&lt;p&gt;Because it combines the predictability of Laravel’s Blade conventions with the flexibility of APIs. HTMX requests behave like AJAX calls but stay fully server-driven. Your AQC pattern handles the data efficiently. And your Blade partials now serve as self-contained, declarative view components that know what data they need.&lt;/p&gt;

&lt;p&gt;You don’t have to touch controllers, define routes, or mix JSON and HTML responses. The system just knows what to do.&lt;/p&gt;




&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;This pattern now feels complete. The first part gave partials their own routes. This part gave them data.&lt;/p&gt;

&lt;p&gt;What you have now is a unified layer where every Blade partial can act like an API endpoint — with model discovery, DTO integration, and automatic data fetching.&lt;/p&gt;

&lt;p&gt;This is what I have experienced recently. What is your take on this approach? Let's hear your ideas and thoughts.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>laravel</category>
      <category>designpatterns</category>
    </item>
    <item>
      <title>Laravel Blade Partial API Pattern with HTMX</title>
      <dc:creator>Raheel Shan</dc:creator>
      <pubDate>Mon, 20 Oct 2025 11:48:56 +0000</pubDate>
      <link>https://forem.com/raheelshan/laravel-blade-partial-api-pattern-with-htmx-3aj</link>
      <guid>https://forem.com/raheelshan/laravel-blade-partial-api-pattern-with-htmx-3aj</guid>
      <description>&lt;p&gt;We have seen splendid UIs in this latest era. &lt;em&gt;All thanks to Javascript, which has snatched the rendering responsibility from the backend that was always good at doing it.&lt;/em&gt; But it has also come with some cost. On the way to achieving SPA, reactivity, and interactivity, we have faced the following issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  The SEO Compromised
&lt;/h2&gt;

&lt;p&gt;This is the most important issue. When users or crawlers come to visit our page, the page is empty with a div with an id may be &lt;code&gt;root&lt;/code&gt;. Users wait for the page to be loaded, but crawlers go back, marking this page empty without data. This heavily affects the ranking in search engines. To counter this issue, people started to use static site generation. Now that the pages are statically generated, they are not updated frequently, yet another problem to be solved.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Markup Drift
&lt;/h2&gt;

&lt;p&gt;The real issue isn’t JSX syntax — it’s that markup moved to the JavaScript side. Once that happened, the frontend had to take over everything the backend used to handle effortlessly.&lt;/p&gt;

&lt;p&gt;Now the UI stack needs a build pipeline before a single element can render. JSX won’t run on its own, so tools like Babel, Vite, and Webpack become mandatory. Then come TypeScript, linters, formatters, and dependency managers — a full ecosystem just to output HTML.&lt;/p&gt;

&lt;p&gt;This shift adds layers of fragility. Every change passes through a build; every runtime error risks a blank screen. The frontend must now fetch data, construct markup, manage reactivity, and coordinate state — all inside one sensitive language runtime.&lt;/p&gt;

&lt;p&gt;And because markup no longer comes from the server, everything must be serialized into JSON first. The browser becomes responsible for turning that JSON back into visible UI. Two systems — backend and frontend — must now agree on the same shape of data and structure of views.&lt;/p&gt;

&lt;p&gt;We didn’t simplify web development; we just moved the complexity closer to the user’s browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hidden Cost of JavaScript-Controlled Rendering
&lt;/h2&gt;

&lt;p&gt;When JavaScript took over rendering, the web quietly stopped being simple. Every framework—React, Vue, Svelte, even the old Knockout and Backbone—started rebuilding what the browser was already doing just fine: creating and updating the DOM. Now, instead of writing HTML, frontend engineers spend their time being part-time compiler engineers—setting up build tools, handling hydration, debugging mismatched markup, and wrestling with virtual DOMs. What used to be a plain document has turned into a fragile choreography between server and client, where one small issue can break rendering, kill performance, or confuse search engines. We ended up building complex systems mostly to fix problems introduced by the same complexity.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Avoid JavaScript Hell
&lt;/h2&gt;

&lt;p&gt;Since we have given JavaScript the responsibility to render/hydrate the frontend and have stuck with what we have experienced, what if we take back the responsibility from JavaScript and make it a little simple? Let Javascript only do what it is best at. We are simply going to use a few techniques.&lt;/p&gt;

&lt;p&gt;Let's start step by step. First we are going to load a full page with Laravel Blade.&lt;/p&gt;

&lt;p&gt;Let's say we need to display a list of products in a table. Each row will have a refresh button. A very basic example. We will do the below code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ProductController extends Controller
{
    public function index(Request $request)
    {
        $products = Product::paginate(10);
        return view('product.index', compact('products'));
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, let's load the blade view.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// resources/views/product/index.blade.php
@extends('layouts.app')

@section('content')
&amp;lt;div class="container"&amp;gt;
    &amp;lt;h2&amp;gt;Products&amp;lt;/h2&amp;gt;
    &amp;lt;table&amp;gt;
        &amp;lt;thead&amp;gt;
            &amp;lt;tr&amp;gt;
                &amp;lt;th scope="col"&amp;gt;Product&amp;lt;/th&amp;gt;
                &amp;lt;th scope="col"&amp;gt;Category&amp;lt;/th&amp;gt;
                &amp;lt;th scope="col"&amp;gt;Brand&amp;lt;/th&amp;gt;
                &amp;lt;th scope="col"&amp;gt;Actions&amp;lt;/th&amp;gt;
            &amp;lt;/tr&amp;gt;
        &amp;lt;/thead&amp;gt;
        &amp;lt;tbody&amp;gt;
            @if (isset($products) &amp;amp;&amp;amp; count($products) &amp;gt; 0)
                @foreach ($products as $product)
                    @include('product.partials.table.row',['product' =&amp;gt; $product])
                @endforeach
            @endif

        &amp;lt;/tbody&amp;gt;
    &amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
@endsection
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do pay attention to how we are adding a partial inside loop.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You must load HTMX in your page before using it. &lt;a href="https://htmx.org/docs/#installing" rel="noopener noreferrer"&gt;HTMX installation&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//resources/views/product/partials/table/row.blade.php
&amp;lt;tr id="product-{{ $product-&amp;gt;id }}"&amp;gt;
    &amp;lt;td&amp;gt;{{ $product-&amp;gt;name }}&amp;lt;/td&amp;gt;
    &amp;lt;td&amp;gt;{{ $product-&amp;gt;category }}&amp;lt;/td&amp;gt;
    &amp;lt;td&amp;gt;{{ $product-&amp;gt;brand }}&amp;lt;/td&amp;gt;
    &amp;lt;td&amp;gt;
        &amp;lt;button 
            type="button"
            hx-get="{{ url('product/partials/table/row/'. $product-&amp;gt;id) }}" 
            hx-target="#product-{{ $product-&amp;gt;id }}"
            hx-swap="outerHTML" 
            class="btn btn-sm btn-primary mb-3"&amp;gt;
            Refresh
        &amp;lt;/button&amp;gt;    
    &amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here we are telling HTMX to refresh the current product row. We have provided it with a unique ID &lt;code&gt;product-x&lt;/code&gt; and an API endpoint to fetch partial from.&lt;/p&gt;

&lt;p&gt;Now let's move to Laravel routes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Global Arrival Route Setup
&lt;/h2&gt;

&lt;p&gt;To make this pattern fully reusable across any part of the application, we define a single global route that will handle all incoming requests for Blade partials through HTMX.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use Illuminate\Support\Str;
use Illuminate\Support\Facades\View;
use Illuminate\Http\Request;

Route::match(['get', 'post'], '{path}', function ($path, Request $request) {
    // convert "product/partials/table/row" to "product.partials.table.row"
    $viewPath = str_replace('/', '.', $path);

    // ensure view exists
    if (!View::exists($viewPath)) {
        abort(404, "Partial [$viewPath] not found.");
    }

    // split segments for context
    $segments = explode('/', $path);

    // detect model name (singular or plural)
    $modelSegment = $segments[0] ?? null;
    $modelName = ucfirst(Str::singular($modelSegment));
    $modelClass = "App\\Models\\$modelName";

    $data = [];

    // only attempt model binding if model class exists
    if (class_exists($modelClass)) {
        // if last segment is numeric, assume it’s an ID → single item
        $last = end($segments);
        if (is_numeric($last)) {
            $data = [Str::lower($modelName) =&amp;gt; $modelClass::findOrFail($last)];
        }
        // if plural model segment detected → fetch collection
        elseif (Str::plural($modelSegment) === $modelSegment) {
            $data = [Str::lower(Str::plural($modelName)) =&amp;gt; $modelClass::all()];
        }
    }

    return view($viewPath, $data);
})-&amp;gt;where('path', '.*');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This route is responsible for:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Handling both GET and POST requests so partials can support form submissions or dynamic interactions.&lt;/li&gt;
&lt;li&gt; Parsing the requested URL to determine which Blade partial should be loaded.&lt;/li&gt;
&lt;li&gt; Loading any dynamic data required by that partial. (This is currently experimental.)&lt;/li&gt;
&lt;li&gt; Returning a fully Blade-rendered HTML partial back to HTMX.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This global route acts like a content dispatcher. Once it is configured, any partial inside your views directory becomes instantly accessible through HTMX calls, simply by referencing its path as a URL. It removes repetitive route creation and keeps your backend clean while enabling front-end interactivity powered by Blade and HTMX.&lt;/p&gt;

&lt;p&gt;HTMX request → Arrival Route → Resolve Partial → Fetch Data → Return Rendered HTML&lt;/p&gt;

&lt;p&gt;With this approach in place, you can focus on building partials and dynamic UI behavior without needing dedicated routes for every small UI update.&lt;/p&gt;




&lt;h3&gt;
  
  
  Another example.
&lt;/h3&gt;

&lt;p&gt;Let's say we need to refresh the whole table this time and we are defining a button to refresh the table data. In this case, here is the example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@extends('layouts.app')

@section('content')
&amp;lt;div class="container"&amp;gt;
    &amp;lt;h2&amp;gt;Products&amp;lt;/h2&amp;gt;
    &amp;lt;button 
        type="button"
        hx-get="{{ url('product/partials/table') }}" 
        hx-target="#product-rows"
        hx-swap="innerHTML" 
        class="btn btn-sm btn-primary mb-3"&amp;gt;
        Refresh Table
    &amp;lt;/button&amp;gt;       
    &amp;lt;table&amp;gt;
        &amp;lt;thead&amp;gt;
            &amp;lt;tr&amp;gt;
                &amp;lt;th scope="col"&amp;gt;Product&amp;lt;/th&amp;gt;
                &amp;lt;th scope="col"&amp;gt;Category&amp;lt;/th&amp;gt;
                &amp;lt;th scope="col"&amp;gt;Brand&amp;lt;/th&amp;gt;
                &amp;lt;th scope="col"&amp;gt;Actions&amp;lt;/th&amp;gt;
            &amp;lt;/tr&amp;gt;
        &amp;lt;/thead&amp;gt;
        &amp;lt;tbody id="product-rows"&amp;gt;
            @include('products.partials.table.list',['products' =&amp;gt; $products])
        &amp;lt;/tbody&amp;gt;
    &amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;
@endsection
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And again, extract the partial where needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//resources/views/product/partials/table/list.blade.php

@if (isset($products) &amp;amp;&amp;amp; count($products) &amp;gt; 0)
    @foreach ($products as $product)
        @include('products.partials.table.row',['product' =&amp;gt; $product])
    @endforeach
@endif
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this setup we need to respect some rules. Here we go.&lt;/p&gt;

&lt;h4&gt;
  
  
  Rule 1—Directory equals URL
&lt;/h4&gt;

&lt;p&gt;Every request path directly maps to a Blade file under &lt;code&gt;resources/views&lt;/code&gt;. &lt;code&gt;/product/partials/form&lt;/code&gt; → &lt;code&gt;resources/views/product/partials/form.blade.php&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Rule 2 — Dots replace slashes
&lt;/h4&gt;

&lt;p&gt;Internally, Laravel views use dot notation, so the route simply replaces &lt;code&gt;/&lt;/code&gt; with &lt;code&gt;.&lt;/code&gt; before rendering.&lt;/p&gt;

&lt;h4&gt;
  
  
  Rule 3 — Any depth allowed
&lt;/h4&gt;

&lt;p&gt;There’s no fixed folder depth. The resolver supports nested structures like &lt;code&gt;/product/partials/table/row&lt;/code&gt; → &lt;code&gt;product.partials.table.row&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Rule 4 — No model or data assumptions
&lt;/h4&gt;

&lt;p&gt;The resolver doesn’t inject or infer data. Every partial is responsible for its own context (passed from the parent view or controller).&lt;/p&gt;

&lt;h4&gt;
  
  
  Rule 5 — 404 for missing partials
&lt;/h4&gt;

&lt;p&gt;If the resolved view doesn’t exist, the route gracefully fails with a 404. No silent fallbacks, no guesswork.&lt;/p&gt;

&lt;h4&gt;
  
  
  Rule 6 — Both GET and POST supported
&lt;/h4&gt;

&lt;p&gt;The same route handles &lt;code&gt;GET&lt;/code&gt; and &lt;code&gt;POST&lt;/code&gt;, so it can respond to &lt;code&gt;hx-get&lt;/code&gt;, &lt;code&gt;hx-post&lt;/code&gt;, or form submissions without extra definitions.&lt;/p&gt;

&lt;h4&gt;
  
  
  Rule 7 — Everything is a partial endpoint
&lt;/h4&gt;

&lt;p&gt;Any Blade file—model-specific or global—can act as an endpoint. You’re free to mix singular (product) or plural (products) folders; the router doesn’t care.&lt;/p&gt;




&lt;h2&gt;
  
  
  How This Solves the SEO Problem
&lt;/h2&gt;

&lt;p&gt;When the page first loads, Laravel renders everything—a fully formed HTML document with all partials in place. Search engines see a complete, server-rendered page.&lt;/p&gt;

&lt;p&gt;After that, HTMX takes over. The same partials that were included during the initial load can now be reloaded individually via the same Blade templates and route pattern.&lt;/p&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  The page is crawlable and SEO-friendly.&lt;/li&gt;
&lt;li&gt;  The partials are reusable.&lt;/li&gt;
&lt;li&gt;  You’re not duplicating frontend logic or templates.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why This Pattern Works
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  No duplication. The same Blade partials power both full-page and partial loads.&lt;/li&gt;
&lt;li&gt;  SEO intact. The first render is full HTML, not JavaScript-rendered fluff.&lt;/li&gt;
&lt;li&gt;  Reusable. Any component can be loaded standalone or as part of a bigger page.&lt;/li&gt;
&lt;li&gt;  Simple routing. One global route pattern controls all partial endpoints.&lt;/li&gt;
&lt;li&gt;  No JSON. The browser never has to assemble UI from raw data again.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is Laravel doing what Laravel does best: serving clean HTML from the server, enhanced with just enough JavaScript to feel dynamic.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Simpler Future
&lt;/h2&gt;

&lt;p&gt;The Blade Partial API Pattern isn’t a framework or a library. It’s just &lt;strong&gt;Laravel used properly&lt;/strong&gt;—with &lt;strong&gt;HTMX&lt;/strong&gt; as the final piece that gives your pages dynamic behavior without losing their soul. It’s fast, indexable, and human to read. Server-driven UI, but done right. No build tools, no JSON, no magic—just Blade.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;The beauty of this pattern is that every request still flows through Laravel’s full stack—middleware, validation, gates, and permissions. You’re not giving up structure or safety for convenience. Each Blade partial can be protected, validated, or even transformed the same way as any API route. The difference is that the response isn’t JSON—it’s ready-to-render markup, straight from the server, exactly as Laravel intended.&lt;/p&gt;

&lt;p&gt;It’s not about going backward. It’s about taking what’s already powerful and using it as it was meant to be used—without pretending the browser needs to be a compiler.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ending Notes
&lt;/h2&gt;

&lt;p&gt;This article is experimental, and I am going to do a bit more research so we can complete all scenarios. if you are interested. Subscribe to my blog and stay tuned. &lt;/p&gt;

</description>
      <category>laravel</category>
      <category>api</category>
      <category>javascript</category>
      <category>architecture</category>
    </item>
    <item>
      <title>I’m SO Looking Forward to Not Needing a Build System Again</title>
      <dc:creator>Raheel Shan</dc:creator>
      <pubDate>Sat, 18 Oct 2025 06:17:37 +0000</pubDate>
      <link>https://forem.com/raheelshan/im-so-looking-forward-to-not-needing-a-build-system-again-a57</link>
      <guid>https://forem.com/raheelshan/im-so-looking-forward-to-not-needing-a-build-system-again-a57</guid>
      <description>&lt;p&gt;Have you been working on the frontend with Javascript? Have you ever noticed what used to be simple &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; has become a full build system? Frontend development has involved more tools than needed just to feel "modern," "reactive," and "interactive". Lets first talk about the problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Compromised SEO
&lt;/h2&gt;

&lt;p&gt;This is the most important issue. When users or crawlers come to visit our page, the page is empty with a div with an id. Users wait for the page to be loaded, but crawlers go back, marking this page empty without data. This heavily affects the ranking in search engines. To counter this issue, people started to use static site generation. Now that the pages are statically generated, they are not updated frequently, yet another problem to be solved.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Markup Drift
&lt;/h2&gt;

&lt;p&gt;The real problem isn’t JSX syntax—it’s that markup moved to the JavaScript end. When that happened, the backend stopped sending HTML and started sending JSON instead.&lt;/p&gt;

&lt;p&gt;Now the frontend has to do many things.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Build Tool Spiral
&lt;/h3&gt;

&lt;p&gt;Once your markup lives in JavaScript, raw files can’t run directly in the browser anymore. You need a build pipeline—Babel, TypeScript, Vite, Webpack, or something else—to transpile JSX, resolve imports, bundle dependencies, minify assets, and compile everything into a format the browser understands. What started as a simple template now requires a full compiler chain just to draw a div.  &lt;/p&gt;

&lt;h3&gt;
  
  
  2. The TypeScript Dependency
&lt;/h3&gt;

&lt;p&gt;Because JSX is code, not markup, you end up mixing presentation with typing rules, interfaces, and generics. You can’t just “return HTML”; you have to satisfy the TypeScript compiler before you can even see what your component looks like.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The Tooling Cascade
&lt;/h3&gt;

&lt;p&gt;Once you add TypeScript, you add linters. Once you add linters, you need formatters. Once you add formatters, you add CI checks to enforce them. Your “simple view component” now depends on Node, npm, ESLint, Prettier, and a small stack of plugins. Isn't that great? :)&lt;/p&gt;

&lt;h3&gt;
  
  
  4. The Build-Time Wall
&lt;/h3&gt;

&lt;p&gt;Every change to the frontend must now pass through a build. You can’t tweak markup and refresh—you have to wait for the bundler to recompile, rebuild, and reload.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. The Fragile Runtime
&lt;/h3&gt;

&lt;p&gt;If JavaScript fails, the UI fails. Since the markup is built at runtime, there’s no fallback HTML. One syntax error, one missing dependency, or one hydration mismatch and the entire interface collapses—blank screen, cryptic console errors, broken user experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. The Over-Delegation Problem
&lt;/h3&gt;

&lt;p&gt;We’ve given JavaScript far more responsibility than it deserves. It’s now responsible for:&lt;/p&gt;

&lt;p&gt;Fetching data&lt;br&gt;
Building markup&lt;br&gt;
Managing state&lt;br&gt;
Rendering updates&lt;br&gt;
Coordinating routing&lt;br&gt;
Each of these used to be handled by the web platform or the backend. Now they all sit on the same fragile runtime that wasn’t designed for this workload.&lt;/p&gt;
&lt;h3&gt;
  
  
  7. The JSON Middleman
&lt;/h3&gt;

&lt;p&gt;Because markup is gone from the backend, every UI element now depends on a JSON contract. Controllers return data, not views; the frontend reinterprets it into markup. So two systems—backend and frontend—must now stay perfectly in sync about what “a post” or “a user” looks like. That’s duplication. That’s maintenance debt.&lt;/p&gt;

&lt;p&gt;It's time to pause and see why we chose a path we never had to in the first place. &lt;/p&gt;
&lt;h2&gt;
  
  
  What Backend Can Do?
&lt;/h2&gt;

&lt;p&gt;The backend has always been capable of full interactivity. It can render and persist state, handle routes, validate input, and return partial HTML—everything a frontend framework does, but faster and more predictably.&lt;/p&gt;

&lt;p&gt;Any backend framework doesn't need a frontend babysitter. It already knows how to build reactive, server-driven interfaces—we just forgot to let it.&lt;/p&gt;
&lt;h2&gt;
  
  
  How Backend Used to Work and Can Still Do
&lt;/h2&gt;

&lt;p&gt;The backend always gave reactivity on two simple tags.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;&lt;a&gt;&lt;/a&gt;&lt;/strong&gt; (anchor) tags for navigation&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;/strong&gt; elements for interaction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Take a look at the snippet below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;a href="/blog"&amp;gt;Blog&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This anchor tag tells a browser:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When a user clicks on this link, issue an HTTP GET request to ‘/blog’ and load the response content into the browser window&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here's with the &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; tag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;form method="post" action="./save-post" &amp;gt;
    &amp;lt;button type="submit" &amp;gt;Save&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The form tag tells the browser:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When form is submitted, issue a post request, process data in the server and let server issue an HTTP Get request in response to form and load the response content into the browser window.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  HTMX: HTML That Fights Back
&lt;/h3&gt;

&lt;p&gt;Then comes HTMX, the library that makes HTML behave like it remembers how the web works. No build step. No bundler. No JavaScript build tools. Just attributes that turn your markup alive.&lt;/p&gt;

&lt;p&gt;Now consider the following HTML snippet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;button hx-post="/clicked"
    hx-trigger="click"
    hx-target="#parent-div"
    hx-swap="outerHTML"&amp;gt;
    Click Me!
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells HTMX:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When a user clicks on this button, issue an HTTP POST request to ‘/clicked’ and use the content from the response to replace the element with the id &lt;code&gt;parent-div&lt;/code&gt; in the DOM.  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;HTMX has gone a step further and implemented this idea to other HTML elements so that they can be interactive as well.&lt;/p&gt;

&lt;p&gt;And here's the summary taken from HTMX docs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Now any element, not just anchors and forms, can issue an HTTP request&lt;/li&gt;
&lt;li&gt;  Now any event, not just clicks or form submissions, can trigger requests&lt;/li&gt;
&lt;li&gt;  Now any HTTP verb, not just &lt;code&gt;GET&lt;/code&gt; and &lt;code&gt;POST&lt;/code&gt;, can be used&lt;/li&gt;
&lt;li&gt;  Now any element, not just the entire window, can be the target for update by the request&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And guess what? What are we receiving from the backend in response?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;HTML, not JSON.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Receiving a response in HTML format is deliberate. This gives rendering/hydration responsibility back to the backend, which Javascript snatched in the name of interactivity.&lt;/p&gt;

&lt;h2&gt;
  
  
  What About Reactivity?
&lt;/h2&gt;

&lt;p&gt;But yes—you’ll sometimes want UI state that reacts instantly without hitting the server. HTMX handles interactivity, not reactivity.&lt;/p&gt;

&lt;p&gt;That’s where most people add &lt;a href="https://alpinejs.dev/" rel="noopener noreferrer"&gt;Alpine.js&lt;/a&gt;. It’s light and works fine. I wanted to go one step further, though—something that truly treats the DOM as the source of truth. So I built JSRibbon.&lt;/p&gt;

&lt;h3&gt;
  
  
  JSRibbon: Reactivity From the DOM Outward
&lt;/h3&gt;

&lt;p&gt;JSRibbon is my ongoing experiment in DOM-first reactivity—a small, component-based library that learns from the last decade of frontend chaos without copying it.&lt;/p&gt;

&lt;p&gt;Here’s the heart of it:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The DOM is The State&lt;/strong&gt;: No virtual layers or hydration. The HTML itself defines current data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The DOM is the Markup&lt;/strong&gt;: No additional markup on the Javascript end. DOM is the actual markup to render more nodes when needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Event Delegation is Core&lt;/strong&gt;: Inspired by jQuery’s .on(), events bubble to component roots, so handlers never die when the DOM changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Component Composition and Context&lt;/strong&gt;: Each component has its own scope but can pass state to children—React’s good ideas, minus the ceremony.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mutation Awareness&lt;/strong&gt;: A built-in MutationObserver means that if new HTML arrives from HTMX, &lt;code&gt;JSRibbon&lt;/code&gt; automatically detects and wires it. Nothing to reinitialize, nothing to refresh manually.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Quiet Revolution
&lt;/h3&gt;

&lt;p&gt;The mix of Backend + HTMX + JSRibbon doesn’t look flashy—and that’s the point. It gives you reactivity, partial page updates, and component logic without a single build step. You deploy PHP, Blade, and two small JS files. That’s it.&lt;/p&gt;

&lt;p&gt;No npm. No transpiler. No “dev mode.” Just clean, reactive, server-first HTML.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why It Matters
&lt;/h3&gt;

&lt;p&gt;The industry has over-engineered frontends into exhaustion. We ship megabytes of JavaScript to re-render what the server already produced perfectly. &lt;em&gt;HTMX and JSRibbon offer an alternative path: HTML as the API, the DOM as the framework, and the server as the brain.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If this approach catches on—and it should—we may finally get to build interactive web apps that age gracefully instead of breaking every six months.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>html</category>
      <category>javascript</category>
      <category>pwa</category>
    </item>
    <item>
      <title>Livewire without Livewire, Inertia without Inertia, GraphQL without GraphQL. And all with plain Laravel</title>
      <dc:creator>Raheel Shan</dc:creator>
      <pubDate>Fri, 10 Oct 2025 19:27:28 +0000</pubDate>
      <link>https://forem.com/raheelshan/livewire-without-livewire-inertia-without-inertia-graphql-without-graphql-and-all-with-plain-3bic</link>
      <guid>https://forem.com/raheelshan/livewire-without-livewire-inertia-without-inertia-graphql-without-graphql-and-all-with-plain-3bic</guid>
      <description>&lt;p&gt;Every few years, the Laravel ecosystem forgets how powerful it actually is. We start believing we need something else on top of it — something that promises “reactivity,” an “SPA feel,” or “exact data fetching.”&lt;/p&gt;

&lt;p&gt;So we bolt on Livewire for “no-JS reactivity.” Inertia to feel “modern.” And GraphQL to “fetch only what you need.”&lt;/p&gt;

&lt;p&gt;And suddenly, a simple Laravel app has more layers then needed as well as additional learning curves, all trying to fix problems that Laravel already solved.&lt;/p&gt;

&lt;p&gt;This article is about walking back from that mess. It's about achieving the goals of these popular tools without ever leaving the comfort and simplicity of plain Laravel.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Foundation: Laravel Was Always the Component Framework
&lt;/h2&gt;

&lt;p&gt;Laravel’s request-response cycle was never a limitation; it’s the cleanest form of state management in web history. The problem isn’t that Laravel can’t be reactive—it’s that developers stopped using Blade the way it was meant to be used.&lt;/p&gt;

&lt;p&gt;Blade was never the problem. It just got treated like a trash bin of mixed markup and business logic. But once you start giving your Blade files contracts and structure, as I explained in &lt;a href="https://dev.to/raheelshan/stop-treating-your-blade-files-like-trash-bins-give-them-contracts-and-structure-43e9"&gt;Don’t Pass Array or Variables to Laravel Blade Views Instead Do This&lt;/a&gt;, the entire architecture changes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Each Blade partial becomes a real, reusable component.&lt;/li&gt;
&lt;li&gt;  Each partial expects a defined data shape, enforced by DTOs or queries.&lt;/li&gt;
&lt;li&gt;  Each partial becomes testable and predictable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the foundation. Now let’s see how it can replace Livewire, Inertia, and GraphQL—one by one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Problem: We Outsourced What Laravel Does Best
&lt;/h2&gt;

&lt;p&gt;The promise of Livewire, Inertia, and GraphQL stems from three core desires:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Dynamic UI updates&lt;/strong&gt; without a full page reload.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Seamless data exchange&lt;/strong&gt; between the backend and frontend.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Reusable, component-based UI parts.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Laravel can already do all of this. We’ve just stopped trusting its native capabilities because of a &lt;em&gt;psychological dependency on JavaScript frameworks&lt;/em&gt;. So instead of using Laravel properly, we’ve started wrapping it in unnecessary layers.&lt;/p&gt;

&lt;p&gt;Let's strip it down and rebuild from the foundation.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Dynamic UI: Livewire without Livewire
&lt;/h3&gt;

&lt;p&gt;Livewire’s idea is clever: run backend logic on the server and send back rendered HTML over AJAX. But you don’t need the entire Livewire runtime to do that. Laravel can already render partial HTML views dynamically.&lt;/p&gt;

&lt;p&gt;Here’s how. You can define a global partial route that acts as a universal entry point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Route::post('{path}', function ($path) {
    // Convert URL path to view dot notation
    $view = str_replace('/', '.', $path);

    // Sanitize to prevent directory traversal, etc.
    $view = preg_replace('/[^A-Za-z0-9_.-]/', '', $view);

    // Logic to resolve models, DTOs, and data based on the path
    // For example, pull the first segment to resolve a model
    $segments = explode('/', $path);
    $modelKey = $segments[0] ?? null;
    $modelInstance = $modelKey ? resolve_model_logic($modelKey) : null; 
    // Your logic here      

    // Check if the Blade partial exists and render it
    if (view()-&amp;gt;exists($view)) {
        // Pass relevant data to the view
        return view($view, [$modelKey =&amp;gt; $modelInstance]);
    }

    abort(404);
})-&amp;gt;where('path', '.*');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This single route acts as a universal entry point. Whenever the frontend hits an API like &lt;code&gt;/user/profile&lt;/code&gt;, this route:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Maps it to &lt;code&gt;resources/views/user/profile.blade.php&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; Decides which model to load (&lt;code&gt;User&lt;/code&gt; in this case)&lt;/li&gt;
&lt;li&gt; Passes it through Laravel's validation, policies, and middleware.&lt;/li&gt;
&lt;li&gt; Returns ready-made HTML, not JSON.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your frontend doesn’t care how it happened—it just receives HTML to render. No component classes, no lifecycle hooks, no hydration overhead. Just Laravel doing what it does best.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Any partial can be loaded with relevant data from this universal route for example &lt;code&gt;resources/views/user/partials/list.blade.php&lt;/code&gt; can be called with this end point &lt;code&gt;user/partials/list&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. SPA Feel: Inertia without Inertia
&lt;/h3&gt;

&lt;p&gt;Inertia’s goal is to make Laravel feel like a single-page app—navigating without full reloads. You can achieve the "SPA feel" for form submissions and partial updates with one tiny JavaScript function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function enhanceForm({
  formId,
  targetId,
  method = null,
  swap = "innerHTML",
  beforeSend = null,
  onSuccess = null,
  onError = null
}) {
  const form = document.getElementById(formId);
  const target = document.getElementById(targetId);

  if (!form || !target) return;

  form.addEventListener("submit", async function (e) {
    e.preventDefault();
    if (typeof beforeSend === "function") beforeSend(form);

    const formData = new FormData(form);
    const reqMethod = method || form.method || "POST";

    try {
      const response = await fetch(form.action, {
        method: reqMethod.toUpperCase(),
        headers: { 'X-Requested-With': 'fetch' },
        body: formData
      });

      const contentType = response.headers.get("content-type") || "";
      const result = contentType.includes("application/json")
        ? await response.json()
        : await response.text();

      if (response.ok) {
        if (typeof onSuccess === "function") onSuccess(result);
        else applySwap(target, result, swap);
      } else {
        if (typeof onError === "function") onError(result, response.status);
        else applySwap(target, `&amp;lt;strong&amp;gt;Error:&amp;lt;/strong&amp;gt; ${response.status}`, swap);
      }
    } catch (err) {
      if (typeof onError === "function") onError(err.message, 0);
      else applySwap(target, `&amp;lt;strong&amp;gt;Network Error:&amp;lt;/strong&amp;gt; ${err.message}`, swap);
    }
  });
}

function applySwap(target, content, swap) {
  switch (swap) {
    case "outerHTML": target.outerHTML = content; break;
    case "append": target.insertAdjacentHTML("beforeend", content); break;
    case "prepend": target.insertAdjacentHTML("afterbegin", content); break;
    case "innerHTML":
    default: target.innerHTML = content;
  }
}

enhanceForm("myForm", "result");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This helper submits a form asynchronously and swaps the server-rendered HTML response into the DOM. That’s all you need for dynamic pages.&lt;/p&gt;

&lt;p&gt;To achieve full SPA-style navigation, you simply extend this idea: write a small script that intercepts link clicks, loads the target page via AJAX, replaces a container like &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;, and updates the browser history using &lt;code&gt;pushState()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Boom. Your app behaves like an SPA, but it’s still a multi-page, server-rendered system under the hood. No JSON hydration, no frontend router, no dual rendering layers.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Fetching Data Like GraphQL, Without GraphQL
&lt;/h3&gt;

&lt;p&gt;GraphQL was invented to fix REST’s under- and over-fetching problems by letting you "fetch only what you need." &lt;a href="https://dev.to/raheelshan/introducing-the-atomic-query-construction-aqc-design-pattern-25l9"&gt;Atomic Query Construction&lt;/a&gt; (AQC) and Data Transfer Objects (DTOs) already solve this in a cleaner, native way.&lt;/p&gt;

&lt;p&gt;With DTOs, you decide what data shape goes into each partial—it’s explicit, documented, and type-safe.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class SimpleProduct extends BaseDTO
{
    public string $name;
    public string $image;
    public float $price;
    public int $stock;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example with &lt;a href="https://dev.to/raheelshan/atomic-query-construction-aqc-design-pattern-explained-1cla"&gt;AQC Design Pattern&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace App\AQC\User;

use App\Models\User;

class GetUsers
{
    public static function handle($params = [], $paginate = true, $columns = '*')
    {
        $userObj = User::latest('id');

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

        // add more conditions for different use cases

        $userObj-&amp;gt;select($columns);

        return $paginate
            ? $userObj-&amp;gt;paginate(User::PAGINATE)
            : $userObj-&amp;gt;get();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pass that DTO straight into your Blade partial. Done. You get precise data with zero over-fetching. No schemas, no resolvers, no query strings—just pure Laravel. GraphQL made this pattern popular; With Laravel you can make it simple.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Full Cycle: Bringing It All Together
&lt;/h2&gt;

&lt;h4&gt;
  
  
  1. Solving SEO — The Biggest Hidden Problem
&lt;/h4&gt;

&lt;p&gt;SPA-like systems often destroy SEO because everything depends on JavaScript to render content. When crawlers visit, they see an empty root div.&lt;/p&gt;

&lt;p&gt;With plain Laravel, we render everything server-side. Your page loads with full HTML and data — perfect for both users and crawlers.&lt;/p&gt;

&lt;p&gt;Then, when something changes, instead of reloading the entire page, we fetch the updated partial using AJAX and replace only the relevant part of the DOM.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Reusability — The Power of Laravel Partials
&lt;/h4&gt;

&lt;p&gt;When you treat every section of your app as a reusable Blade partial, you start to realize something powerful:&lt;/p&gt;

&lt;p&gt;Each partial can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Be loaded on page load (with all data).&lt;/li&gt;
&lt;li&gt;  Be individually fetched later via AJAX for updates.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That means the same file acts both as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  The initial render.&lt;/li&gt;
&lt;li&gt;  The live-update endpoint.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s a component system — but server-driven and native.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Backend Re-render — No Duplicate Validation and Authentication Logic
&lt;/h4&gt;

&lt;p&gt;The backend re-renders just that one partial with updated data, running it through the same validation, policies, and middleware as always. This means backend is the source of truth not javascript.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. DOM Update
&lt;/h4&gt;

&lt;p&gt;The lightweight JavaScript helper swaps the returned HTML directly into the page. No reload, no state loss. No hydeation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Want More Interactivity?
&lt;/h2&gt;

&lt;p&gt;The only missing piece now is navigation. If you can find a tiny javascript library that can do a few things, you are good to go.  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Intercepts link clicks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Updates the browser history using &lt;code&gt;pushState()&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once done, this whole setup stands on the principle of progressive enhancement. It doesn’t punish users—or search engines—if JavaScript takes a day off. Every interaction that happens through Ajax or fetch calls is just a graceful improvement over a fully functional server-rendered page. The HTML you send from Laravel is always complete, crawlable, and independently usable. If someone disables JavaScript, they still get the same data, same structure, and same experience—just with full-page reloads. That’s the real strength of building this way: your site behaves like a single-page app when JavaScript is present, but remains a perfectly valid multi-page Laravel app when it isn’t.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can easily layer AlpineJS, HTMX, or your favorite micro-reactive system on top.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The Critical Advantage You Gain: Perfect SEO
&lt;/h3&gt;

&lt;p&gt;When you heavily depend on frontend framework for rendering and hyderation of HTML, you often compromise SEO by turning your application into a JavaScript-driven experience. Search engines crawl static HTML, and when your content depends on a dynamic render, your ranking suffers.&lt;/p&gt;

&lt;p&gt;With the native Laravel approach, SEO is never a problem. Why? Because we render the full, complete HTML on the first load—just as Laravel does naturally. The content is immediately available for crawlers and users. The dynamic, AJAX-powered partial swaps are a &lt;code&gt;progressive enhancement&lt;/code&gt;, not a requirement. You get reactivity without sacrificing your SEO foundation.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Laravel Native Way Forward
&lt;/h3&gt;

&lt;p&gt;What I’m showing isn’t a library. It’s a mindset:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Render everything through Blade.&lt;/li&gt;
&lt;li&gt;  Deliver HTML, not JSON.&lt;/li&gt;
&lt;li&gt;  Structure data with DTOs and AQC.&lt;/li&gt;
&lt;li&gt;  Swap partials, not full pages.&lt;/li&gt;
&lt;li&gt;  Stay inside Laravel.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;The more layers we add, the more we drift away from Laravel’s real advantage — its native simplicity and coherence. Every added dependency is another mental model, another bundle of complexity, and another place for things to break.&lt;/p&gt;

&lt;p&gt;What I’m advocating for is returning home — to Laravel itself. Let Laravel render your HTML. Let Blade handle your logic. Let small, replaceable partials handle reactivity. And let minimal JavaScript handle your page transitions.&lt;/p&gt;

&lt;p&gt;You’ll end up with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Perfect SEO.&lt;/li&gt;
&lt;li&gt;  Full control over your templates.&lt;/li&gt;
&lt;li&gt;  Zero external build systems.&lt;/li&gt;
&lt;li&gt;  Real-time updates with no extra layer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All native. All Laravel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;We don’t need another framework to make Laravel powerful. We just need to rediscover what it already can do — and stop outsourcing what’s already possible natively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Livewire without Livewire. Inertia without Inertia. GraphQL without GraphQL. All with plain Laravel.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sometimes, the best innovation is realizing you already have everything you need.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>laravel</category>
      <category>programming</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
