<?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>DEV Community: Jefferson Silva</title>
    <description>The latest articles on DEV Community by Jefferson Silva (@jeffsynister).</description>
    <link>https://dev.to/jeffsynister</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%2F3682127%2Fe20faf19-0d97-4c2d-8cfb-3b8c38df7a3c.jpg</url>
      <title>DEV Community: Jefferson Silva</title>
      <link>https://dev.to/jeffsynister</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jeffsynister"/>
    <language>en</language>
    <item>
      <title>The code won’t be your real problem</title>
      <dc:creator>Jefferson Silva</dc:creator>
      <pubDate>Sun, 12 Apr 2026 01:55:02 +0000</pubDate>
      <link>https://dev.to/jeffsynister/the-code-wont-be-your-real-problem-omk</link>
      <guid>https://dev.to/jeffsynister/the-code-wont-be-your-real-problem-omk</guid>
      <description>&lt;h2&gt;
  
  
  The business journey of building a developer tool for PHP
&lt;/h2&gt;

&lt;p&gt;Many of us developers are emotionally invested in proportion to our technical knowledge and inversely proportional to our commercial maturity. I was like that. And I had to learn the hard way.&lt;/p&gt;

&lt;p&gt;Building software that adds value to the end user is much more about how the person sees you from the other side than about how much you deliver technically. It doesn't matter how innovative your technology is — if you don't convey trust and don't have a minimum level of credibility, nobody will use it. And when your audience is developers, selling becomes even harder, because they're skeptical by nature.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understand the audience before writing code
&lt;/h2&gt;

&lt;p&gt;Understanding my audience was the first thing I had to do. Who is going to use this? How do I want them to use it? I need a "wow" moment to keep them engaged. And I need them to come back — retention is just as important as acquisition.&lt;/p&gt;

&lt;p&gt;I had three fronts to manage from the start:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Understand who my primary user was.&lt;/strong&gt; In my case, developers who had never set up a debugger or who gave up trying. People living on &lt;code&gt;dd()&lt;/code&gt; and &lt;code&gt;var_dump()&lt;/code&gt; not by choice, but because there was no accessible alternative.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Drive traffic starting with people I know.&lt;/strong&gt; Brazil has a large PHP community and speaks my language. Using your own country to validate your product in the hands of people who have no idea how it works, but are curious, is incredibly valuable. It's easier to communicate and express exactly what you feel and what you want to say when you do it in your own language. I validated in Portuguese first. Only then did I go after the real conversion market.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prepare the product for the market that pays.&lt;/strong&gt; Tier 1 countries — United States, Canada, United Kingdom, European Union. This audience is more demanding, but by the time I reached them I was already prepared technically and psychologically. The subscription model with a trial exists for these users: they use it, feel it, and decide. If it doesn't deliver, they cancel. Simple. I believe in my product. I use it every day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Traffic without money
&lt;/h2&gt;

&lt;p&gt;You might be wondering: how do you handle traffic without investment?&lt;/p&gt;

&lt;p&gt;The key is using analytics tools to track your audience and publishing in forums, community pages, and relevant spaces. It will grow slowly and organically. But you need to understand something fundamental: to get the users who will pay for your product, you first need the ones who won't. They will bring the ones who pay. And they are the ones who will flood you with bug reports and force you to improve.&lt;/p&gt;

&lt;p&gt;I needed to create a connection and build trust with my audience. Having that bridge is essential, no matter how hard it is to build.&lt;/p&gt;

&lt;h2&gt;
  
  
  The open source engine as a bridge
&lt;/h2&gt;

&lt;p&gt;I created the ddless-engine as an open source project. Not out of idealism. Out of strategy.&lt;/p&gt;

&lt;p&gt;The engine is a way for developers to contribute and see how the DDLess motor works for a purpose quite different from the conventional. They can learn how PHP works at a deeper level — Closure::bind, stream wrappers, AST parsing, debug_backtrace — things that modern frameworks abstract away completely.&lt;/p&gt;

&lt;p&gt;The open engine does three things: it builds trust (the developer can read every line of code), it attracts contributors, and it works as technical marketing (posts about the engine generate interest in the full product).&lt;/p&gt;

&lt;h2&gt;
  
  
  Real visibility
&lt;/h2&gt;

&lt;p&gt;Now that we have the product, understand the market, and built the bridge between user and product, we need real visibility to gain traction.&lt;/p&gt;

&lt;p&gt;And here comes the question: does your product have the conditions to receive external investment?&lt;/p&gt;

&lt;p&gt;In my case, no. The return rate is very low at the start and I did this because I love the PHP community and because I have other projects that need visibility to work. In my case, it's a long-term plan.&lt;/p&gt;

&lt;p&gt;But you can also invest your own money to gain more visibility. When you understand the market, that's already half the way. The other half is creating a communication channel that helps you convert. Your ads will target the right audience and that gives you a return proportional to your investment.&lt;/p&gt;

&lt;h2&gt;
  
  
  The truth nobody talks about
&lt;/h2&gt;

&lt;p&gt;If you're a solo developer like me, you'll have to do everything: code, marketing, support, bureaucracy, design, distribution, certification. And you'll spend hours planning and thinking.&lt;/p&gt;

&lt;p&gt;The code won't be anywhere near your real problem.&lt;/p&gt;

&lt;p&gt;The real problem is: does anyone know your product exists? Does anyone trust it enough to download it? Does anyone see enough value to pay for it? Does anyone come back after the first time?&lt;/p&gt;

&lt;p&gt;If you don't answer these questions before writing the first line of code, you'll build something technically brilliant that nobody uses.&lt;/p&gt;

&lt;p&gt;I almost did that. DDLess almost became just another forgotten repository on GitHub. What saved it wasn't the code. It was understanding that the code is just the beginning.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Jefferson Silva is the creator of DDLess, a PHP development workbench. The technical article about building DDLess was featured in PHP Reads Issue #6 by Stefan Priebsch and Sebastian Bergmann.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Open source engine: &lt;a href="https://github.com/behindSolution/ddless-engine" rel="noopener noreferrer"&gt;github.com/behindSolution/ddless-engine&lt;/a&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Product: &lt;a href="https://ddless.com" rel="noopener noreferrer"&gt;ddless.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>webdev</category>
      <category>php</category>
      <category>tooling</category>
    </item>
    <item>
      <title>I built a framework to turn Laravel + Livewire apps into desktop &amp; mobile apps using PHP WebAssembly, no Electron, no React Native</title>
      <dc:creator>Jefferson Silva</dc:creator>
      <pubDate>Thu, 09 Apr 2026 14:04:14 +0000</pubDate>
      <link>https://dev.to/jeffsynister/i-built-a-framework-to-turn-laravel-livewire-apps-into-desktop-mobile-apps-using-php-3j3k</link>
      <guid>https://dev.to/jeffsynister/i-built-a-framework-to-turn-laravel-livewire-apps-into-desktop-mobile-apps-using-php-3j3k</guid>
      <description>&lt;p&gt;Hey everyone,&lt;/p&gt;

&lt;p&gt;I've been working on a side project called NativeBlade and wanted to share it with the community.&lt;/p&gt;

&lt;p&gt;The idea is simple: take your Laravel + Livewire app and run it as a native desktop or mobile application. No server needed. No Electron. No JavaScript frameworks. Just PHP and Blade.&lt;/p&gt;

&lt;p&gt;How it works&lt;/p&gt;

&lt;p&gt;Your entire Laravel application gets bundled and runs inside a PHP WebAssembly runtime, wrapped in a &lt;a href="https://v2.tauri.app" rel="noopener noreferrer"&gt;https://v2.tauri.app&lt;/a&gt; shell. The architecture looks like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;PHP 8.3 runs in the browser via WebAssembly&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Blade templates and Livewire components work as-is&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SQLite database persists to IndexedDB (survives app restarts)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Native shell components (header, bottom nav, drawer) render outside the WebView — no flicker during navigation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Native OS features (dialogs, notifications, system tray) work through a bridge&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The whole thing started as a weekend experiment: "what if I could just composer require something and turn my Laravel app into a desktop app?"&lt;/p&gt;

&lt;p&gt;What it can do&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Desktop: Windows, macOS, Linux with native menus and system tray&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mobile: Android &amp;amp; iOS with status bar, safe area, swipe back&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;External HTTP requests: Http::get() works transparently through a JS bridge — PHP signals what it needs, JavaScript makes the real fetch, PHP re-executes with the cached&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;response. You can even use NativeBlade::pool() to run multiple requests in parallel via Promise.all()&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1,512 built-in icons from &lt;a href="https://phosphoricons.com/" rel="noopener noreferrer"&gt;https://phosphoricons.com/&lt;/a&gt; — works in both shell components and Blade templates&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hot reload during development via a Vite plugin that watches PHP/Blade files&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Offline-first — everything runs client-side, no internet required after install&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it. Your Laravel app is now a desktop application.&lt;/p&gt;

&lt;p&gt;What doesn't work&lt;/p&gt;

&lt;p&gt;I want to be upfront about the limitations. Since PHP runs in WebAssembly, there's no real server:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;No queues/jobs — no background worker process&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No mail — no SMTP from WASM&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No MySQL/Postgres — SQLite only&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No sessions — uses a built-in state management instead&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No cron/scheduling&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Http::get() works but through a bridge (not native PHP networking)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's not meant to replace server-side Laravel. It's for apps that run locally and don't need a backend, think tools, dashboards, utilities, offline apps.&lt;/p&gt;

&lt;p&gt;Why I'm sharing this&lt;/p&gt;

&lt;p&gt;This started as a learning project and I'd love to get feedback from the PHP community. The codebase touches a lot of interesting areas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;PHP WebAssembly internals&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tauri 2 (Rust-based alternative to Electron)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Livewire's lifecycle inside a non-standard runtime&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Bridging sync PHP with async JavaScript&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If any of this sounds interesting to you — whether you want to contribute, experiment, or just tell me what I'm doing wrong — I'd appreciate it.&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/NativeBlade/NativeBlade" rel="noopener noreferrer"&gt;https://github.com/NativeBlade/NativeBlade&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy to answer any questions!&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>mobile</category>
    </item>
    <item>
      <title>The Technical Journey of Building a PHP Debugger from Scratch</title>
      <dc:creator>Jefferson Silva</dc:creator>
      <pubDate>Mon, 30 Mar 2026 14:22:10 +0000</pubDate>
      <link>https://dev.to/jeffsynister/the-technical-journey-of-building-a-php-debugger-from-scratch-3b1h</link>
      <guid>https://dev.to/jeffsynister/the-technical-journey-of-building-a-php-debugger-from-scratch-3b1h</guid>
      <description>&lt;h2&gt;
  
  
  The Origin
&lt;/h2&gt;

&lt;p&gt;I've been working with PHP and Laravel for years. And like every PHP developer, I've been through that classic cycle: install Xdebug, configure &lt;code&gt;xdebug.mode=debug&lt;/code&gt;, map paths in PHPStorm, pray that Docker doesn't change the internal network IP, and when it finally works... it's slow. Every request takes seconds longer.&lt;/p&gt;

&lt;p&gt;But the real problem was never performance. It was setup. I was working on projects running in Docker via Laravel Sail, others on WSL, some with SSH to staging. Each environment meant a different Xdebug configuration. Network changed? Redo it. Spun up a new container? Remap paths. Switched machines? Start from zero.&lt;/p&gt;

&lt;p&gt;The turning point was on a Sunday. I had a production bug in a Laravel project, needed to debug fast, and spent 40 minutes just trying to get Xdebug to connect through Docker. When it finally did, the breakpoint landed on the wrong line because the path mapping was off by one level. At that moment I thought: &lt;em&gt;"This can't be this hard. What if the debugger didn't need any extension at all? What if it was part of the code itself?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That's how DDLess was born.&lt;/p&gt;




&lt;h2&gt;
  
  
  Iteration 1: phpdbg
&lt;/h2&gt;

&lt;p&gt;Before instrumenting code, my first idea was to use what already existed. PHP has &lt;code&gt;phpdbg&lt;/code&gt;, a built-in interactive debugger. It uses Zend Engine opcodes, it's fast, and it doesn't need an extension. Seemed perfect.&lt;/p&gt;

&lt;p&gt;The problem is that phpdbg was designed to be interactive in the terminal. It has no clean communication protocol for integrating with an external UI. To make it work with a graphical interface, I would have to parse its text output, which changes between PHP versions. On top of that, it doesn't work well inside contexts like Laravel Sail or Docker because it needs to be the process entrypoint, and that doesn't work when you have a web server in front.&lt;/p&gt;

&lt;p&gt;Within a few hours I realized this path would lead me down a compatibility rabbit hole. I scrapped it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Iteration 2: Regex and Tokenizer
&lt;/h2&gt;

&lt;p&gt;The next idea was: what if I injected debug calls directly into the PHP code before execution? Something like a &lt;code&gt;ddless_step_check(__FILE__, __LINE__)&lt;/code&gt; before each executable line. If PHP ran this modified code, I'd have full control over the execution flow without needing any extension.&lt;/p&gt;

&lt;p&gt;The first step was figuring out which lines are "instrumentable," meaning where it makes sense to place a breakpoint. There's no point placing one on a comment, a class declaration, or in the middle of a multiline string.&lt;/p&gt;

&lt;p&gt;My first implementation used PHP's &lt;code&gt;token_get_all()&lt;/code&gt; to tokenize the code and a state machine with regex to decide where to inject. It worked for simple cases:&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;$x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;           &lt;span class="c1"&gt;// instrumentable&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;          &lt;span class="c1"&gt;// instrumentable&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$x&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="c1"&gt;// instrumentable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the edge cases were endless. Chained method calls across multiple lines:&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;-&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;'status'&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="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="s1"&gt;'created_at'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// continuation, not a new statement&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;Heredoc/nowdoc strings with PHP code inside. Multiline arrays where the closing &lt;code&gt;]&lt;/code&gt; was on the next line. &lt;code&gt;elseif&lt;/code&gt; that needed special handling because you can't inject a line before an &lt;code&gt;elseif&lt;/code&gt;, as it would break the &lt;code&gt;if&lt;/code&gt; chain. Same for &lt;code&gt;else&lt;/code&gt;, &lt;code&gt;catch&lt;/code&gt;, and &lt;code&gt;finally&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It was almost 600 lines of code between &lt;code&gt;ddless_analyze_code_tokens()&lt;/code&gt;, &lt;code&gt;ddless_is_safe_to_inject_before()&lt;/code&gt;, and several helper functions. And every week a new case would break things. A developer would use a different coding pattern and the instrumentation would generate a syntax error.&lt;/p&gt;

&lt;p&gt;I knew I was building on sand. But it worked for a while, and it allowed me to validate that the core idea — code instrumentation combined with file-based IPC — was viable.&lt;/p&gt;




&lt;p&gt;See more: &lt;a href="https://ddless.com/blog/technical-journey-building-php-debugger" rel="noopener noreferrer"&gt;https://ddless.com/blog/technical-journey-building-php-debugger&lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>tooling</category>
      <category>development</category>
    </item>
    <item>
      <title>I got tired of dd() and built a visual PHP debugger that doesn't need Xdebug</title>
      <dc:creator>Jefferson Silva</dc:creator>
      <pubDate>Tue, 03 Mar 2026 22:39:24 +0000</pubDate>
      <link>https://dev.to/jeffsynister/i-got-tired-of-dd-and-built-a-visual-php-debugger-that-doesnt-need-xdebug-41pa</link>
      <guid>https://dev.to/jeffsynister/i-got-tired-of-dd-and-built-a-visual-php-debugger-that-doesnt-need-xdebug-41pa</guid>
      <description>&lt;p&gt;Hey everyone.&lt;/p&gt;

&lt;p&gt;I've been a PHP dev for years and like most of us, I've been living between &lt;code&gt;dd()&lt;/code&gt;, &lt;code&gt;var_dump()&lt;/code&gt;, and that twice-a-year attempt to configure Xdebug that works for 3 days until Docker rebuilds and breaks everything.&lt;/p&gt;

&lt;p&gt;After spending way too many hours debugging with &lt;code&gt;echo&lt;/code&gt; where I shouldn't, I decided to build DDLess — a desktop app that does visual debugging for any PHP project without installing an extension, without IDE plugins, without Composer, and without changing a single line of code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it does&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You open a project, set breakpoints by clicking on a line, send a request, and DDLess pauses right there. Variables, call stack, step in/out/over — all visual, the way it should be.&lt;/p&gt;

&lt;p&gt;But it goes beyond just debugging:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dumppoints&lt;/strong&gt; — a visual &lt;code&gt;dd()&lt;/code&gt;: you pick the line and expressions in the UI, without touching your code. It evaluates, displays the results, and terminates execution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Task Runner&lt;/strong&gt; — a REPL with full framework context. Write PHP, run it, see streaming output. With autocomplete for your project's classes, methods, and helpers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Method Execution&lt;/strong&gt; — pick any method from any class and test it directly, with auto-scaffolded parameters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proxy Mode&lt;/strong&gt; — selectively intercepts browser requests without changing URLs or using Postman.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLI Debug&lt;/strong&gt; — debug artisan commands, PHPUnit, migrations, queue workers. Run &lt;code&gt;.ddless/php-ddless 8001 artisan test&lt;/code&gt; and breakpoints just work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code Navigation&lt;/strong&gt; — Ctrl+Click for go-to-definition, file search, and content search across your entire project.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it works everywhere&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;DDLess doesn't use sockets or PHP extensions. It instruments code at runtime via AST (nikic/PHP-Parser) and uses file-based communication. In practice, this means it works on Local, Docker, WSL, and remote SSH with zero extra configuration. If PHP runs there, DDLess debugs there.&lt;/p&gt;

&lt;p&gt;Zero manual path mapping. Zero &lt;code&gt;xdebug.client_host&lt;/code&gt;. Zero "it works on his machine but not mine."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frameworks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Laravel, WordPress, Symfony, Drupal, CakePHP, Slim, or any PHP 7.4+ project. Laravel gets automatic bootstrap. For other frameworks, just point to the entry file and you're good.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Runs on&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Windows, macOS (Intel and Apple Silicon), and Linux (AppImage, .deb, .rpm).&lt;/p&gt;

&lt;p&gt;This is my solo project, built independently. I use it daily and it's stable.&lt;/p&gt;

&lt;p&gt;If you want to try it: &lt;a href="https://ddless.com" rel="noopener noreferrer"&gt;ddless.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Any questions, feedback, or bugs — hit me up here or on &lt;a href="https://discord.gg/xPtXguDrmC" rel="noopener noreferrer"&gt;Discord&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>php</category>
      <category>productivity</category>
      <category>tooling</category>
      <category>laravel</category>
    </item>
    <item>
      <title>Your Database Already Has the Answers — You Just Need to Ask</title>
      <dc:creator>Jefferson Silva</dc:creator>
      <pubDate>Mon, 09 Feb 2026 04:46:21 +0000</pubDate>
      <link>https://dev.to/jeffsynister/your-database-already-has-the-answers-you-just-need-to-ask-3ncf</link>
      <guid>https://dev.to/jeffsynister/your-database-already-has-the-answers-you-just-need-to-ask-3ncf</guid>
      <description>&lt;p&gt;Every Laravel application sits on top of a database full of insights. How many users signed up this month? Which products are underperforming? What's the average order value by region? The data is right there, but getting to it usually means one of two things: writing raw SQL or building custom dashboards that take days to ship.&lt;/p&gt;

&lt;p&gt;I built &lt;strong&gt;LaraGrep&lt;/strong&gt; to close that gap — a Laravel package that lets you ask questions about your database in plain English and get real answers back.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem Every Developer Knows
&lt;/h2&gt;

&lt;p&gt;You're in a meeting. The client asks: "How many active subscriptions do we have that are expiring next month?" You know the data exists. You know which tables to join. But to answer that question right now, you'd need to open a terminal, write a query, run it, maybe fix a typo, run it again.&lt;/p&gt;

&lt;p&gt;Now multiply that by every stakeholder who needs "just a quick number" from the database. Product managers, sales teams, founders — they all have questions, and the developer becomes the bottleneck.&lt;/p&gt;

&lt;p&gt;Some teams build internal dashboards. That works for recurring questions, but not for the ad-hoc ones that come out of nowhere. And maintaining those dashboards is a cost that compounds over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  What LaraGrep Actually Does
&lt;/h2&gt;

&lt;p&gt;LaraGrep connects to your Laravel application, reads your database schema, and uses AI to translate natural language questions into safe, parameterized SQL queries. But the key difference from other text-to-SQL tools is that it doesn't just generate one query and hope for the best.&lt;/p&gt;

&lt;p&gt;It uses an &lt;strong&gt;agent loop&lt;/strong&gt;. The AI executes a query, sees the results, thinks about what it learned, and decides whether to run another query or provide the final answer. This means it can handle questions that require multiple steps — joining data across tables, filtering based on previous results, self-correcting when something unexpected comes back.&lt;/p&gt;

&lt;p&gt;Ask "What's our top-selling product category in Q4 compared to Q3?" and the AI might run three or four queries behind the scenes: first to understand the schema, then to pull Q3 numbers, then Q4, then compare. You just see the final answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Data Analyst That Knows Your Schema
&lt;/h2&gt;

&lt;p&gt;Think of LaraGrep as a junior data analyst who has memorized your entire database structure. You describe your tables, columns, relationships, and even provide hints about what JSON columns contain. The AI uses all of that context to write accurate queries.&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;Table&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'orders'&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;description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Customer orders with payment status.'&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;columns&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;bigInteger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user_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="nf"&gt;unsigned&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;description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'FK to users.id'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'total'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&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;description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Order total in USD'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'status'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'pending'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'paid'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'cancelled'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
        &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'metadata'&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;template&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'shipping_method'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'express'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'coupon_code'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'SAVE20'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
        &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;timestamp&lt;/span&gt;&lt;span class="p"&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="p"&gt;])&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;relationships&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="nc"&gt;Relationship&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;belongsTo&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="s1"&gt;'user_id'&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 more context you provide, the better the answers get. Column descriptions, enum values, JSON templates — all of it helps the AI understand not just the structure, but the meaning behind your data.&lt;/p&gt;

&lt;p&gt;For large databases, you don't even need to define everything manually. Set the schema mode to &lt;code&gt;auto&lt;/code&gt; and LaraGrep reads directly from &lt;code&gt;information_schema&lt;/code&gt;, pulling table and column comments as descriptions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real Use Cases
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;For client projects:&lt;/strong&gt; Imagine delivering an admin panel where the client can type "Show me all users who placed more than 3 orders last month but haven't logged in this week" and get an instant answer. No custom report page. No Jira ticket. No waiting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For your own SaaS:&lt;/strong&gt; You're debugging a billing issue at 11pm. Instead of writing JOIN queries across five tables, you ask: "Which users on the Pro plan have a failed payment in the last 48 hours and an active subscription?" LaraGrep figures out the joins and filters for you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For understanding your numbers:&lt;/strong&gt; Early-stage founders who are technical enough to have a database but don't always have time to query it properly. "What's my MRR?" "What's the churn rate this quarter?" "How does signup-to-first-purchase time compare between organic and paid users?" These questions shouldn't require 20 minutes of SQL each time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For generating reports:&lt;/strong&gt; LaraGrep has a query export mode where the AI consolidates all its reasoning into a single optimized SELECT query and hands it back to you — no LIMIT, no memory constraints. You take that query and run it however you want: &lt;code&gt;cursor()&lt;/code&gt; for streaming, &lt;code&gt;chunk()&lt;/code&gt; for batch processing, pipe it into Laravel Excel. The AI does the thinking, you control the execution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For recurring reports:&lt;/strong&gt; The recipe system auto-saves every successful query chain. Found a question you'll ask every Monday? Replay the recipe with fresh data — the AI adjusts date parameters automatically. Wire it to a scheduled job and you have automated reporting without building a single dashboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep It Local with Ollama
&lt;/h2&gt;

&lt;p&gt;Not every project can send database schemas to external APIs. Healthcare, finance, government contracts — there are legitimate reasons to keep everything on-premises.&lt;/p&gt;

&lt;p&gt;LaraGrep works with Ollama out of the box. Point it to your local instance, pick a model, and every query stays on your machine. No data leaves your network. The setup is three environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LARAGREP_PROVIDER=openai
LARAGREP_API_KEY=ollama
LARAGREP_BASE_URL=http://localhost:11434/v1/chat/completions
LARAGREP_MODEL=qwen3-coder:30b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The trade-off is that local models are less capable than GPT-4o or Claude for complex multi-step reasoning. But for straightforward queries — counts, aggregations, filtered lookups — they work well enough. And for sensitive environments, "good enough locally" beats "perfect in the cloud" every time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security by Design
&lt;/h2&gt;

&lt;p&gt;Letting AI write SQL sounds dangerous, and it should — if done carelessly. LaraGrep takes a strict approach:&lt;/p&gt;

&lt;p&gt;Only &lt;code&gt;SELECT&lt;/code&gt; queries are allowed. Any mutation attempt is rejected before it reaches the database. All values use parameterized bindings — the AI never interpolates user input into raw SQL. Every table reference is validated against the schema you defined — the AI can't query tables you haven't exposed. There's a configurable execution timeout that kills slow queries before they can lock your database. And the agent loop is capped at a maximum number of iterations to prevent runaway API costs.&lt;/p&gt;

&lt;p&gt;This doesn't make it zero-risk. You're still exposing read access to data, so protecting the endpoint with authentication middleware is essential. But the attack surface is significantly smaller than giving someone a database GUI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;If you have a Laravel 10+ application and an API key (or Ollama), you can be running in under five minutes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require behindsolution/laragrep
php artisan vendor:publish &lt;span class="nt"&gt;--tag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;laragrep-config
php artisan migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Define your tables in the config, set your API key in &lt;code&gt;.env&lt;/code&gt;, and send a POST request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost/laragrep &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"question": "How many users registered this week?"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. The response comes back with a natural language summary and, optionally, the full debug trace showing every query the AI ran.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Means for Your Projects
&lt;/h2&gt;

&lt;p&gt;The shift here isn't really about AI writing SQL. It's about making database knowledge accessible to anyone who can describe what they need in words. The developer still controls the schema, the security, the infrastructure. But the barrier to getting answers drops from "know SQL and the schema" to "know what you want to know."&lt;/p&gt;

&lt;p&gt;For agencies delivering client projects, this is a feature that adds immediate value without weeks of dashboard development. For SaaS builders, it's a way to stay close to your data without context-switching into a SQL client every time. For teams with non-technical stakeholders, it's one less reason to interrupt the developer.&lt;/p&gt;

&lt;p&gt;The data was always there. Now it's easier to reach.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;LaraGrep is open source and available on GitHub:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/behindSolution/laragrep" rel="noopener noreferrer"&gt;github.com/behindSolution/laragrep&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Stars, issues, and pull requests are welcome. If you're using it in a project, I'd love to hear about your experience.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>api</category>
      <category>backend</category>
    </item>
    <item>
      <title>Debugging Laravel Beyond Localhost</title>
      <dc:creator>Jefferson Silva</dc:creator>
      <pubDate>Sat, 31 Jan 2026 04:37:33 +0000</pubDate>
      <link>https://dev.to/jeffsynister/debugging-laravel-beyond-localhost-3obf</link>
      <guid>https://dev.to/jeffsynister/debugging-laravel-beyond-localhost-3obf</guid>
      <description>&lt;h3&gt;
  
  
  Lessons learned from real environments
&lt;/h3&gt;

&lt;p&gt;For years, my debugging workflow looked the same.&lt;/p&gt;

&lt;p&gt;Something breaks.&lt;br&gt;&lt;br&gt;
I try to reproduce it locally.&lt;br&gt;&lt;br&gt;
I add logs.&lt;br&gt;&lt;br&gt;
I guess.&lt;/p&gt;

&lt;p&gt;Sometimes it works. Most of the time, it doesn’t.&lt;/p&gt;

&lt;p&gt;The more my Laravel projects moved to Docker, WSL, staging servers and remote environments, the more obvious one thing became:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Most bugs don’t happen on localhost.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This article is about what breaks when we rely too much on local debugging, and what changes when we start debugging &lt;em&gt;real execution&lt;/em&gt; instead.&lt;/p&gt;




&lt;h2&gt;
  
  
  The debugging illusion
&lt;/h2&gt;

&lt;p&gt;Local debugging gives us a sense of control.&lt;/p&gt;

&lt;p&gt;Same machine.&lt;br&gt;&lt;br&gt;
Same IDE.&lt;br&gt;&lt;br&gt;
Same database snapshot.  &lt;/p&gt;

&lt;p&gt;But production and QA don’t look like that.&lt;/p&gt;

&lt;p&gt;Different environment variables.&lt;br&gt;&lt;br&gt;
Different data.&lt;br&gt;&lt;br&gt;
Different timing.&lt;br&gt;&lt;br&gt;
Different infrastructure.&lt;/p&gt;

&lt;p&gt;The bug you are chasing is rarely caused by “wrong syntax”.&lt;br&gt;&lt;br&gt;
It’s caused by &lt;strong&gt;state&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And state is almost always environment-dependent.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why local debugging breaks down
&lt;/h2&gt;

&lt;p&gt;Local debugging tools are great. Until they aren’t.&lt;/p&gt;

&lt;p&gt;Here are the most common failure points I kept hitting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PHP extensions that don’t work well with Docker or WSL
&lt;/li&gt;
&lt;li&gt;Network ports blocked or misconfigured
&lt;/li&gt;
&lt;li&gt;Debuggers tightly coupled to IDEs
&lt;/li&gt;
&lt;li&gt;Only one developer able to debug at a time
&lt;/li&gt;
&lt;li&gt;Debugging tied exclusively to HTTP requests
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these are “tool bugs”.&lt;br&gt;&lt;br&gt;
They are &lt;strong&gt;assumptions baked into the workflow&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The assumption is simple:  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;If I can reproduce it locally, I can debug it.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But real-world bugs don’t respect that assumption.&lt;/p&gt;




&lt;h2&gt;
  
  
  Debugging real execution
&lt;/h2&gt;

&lt;p&gt;At some point, I stopped asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“How do I reproduce this locally?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And started asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“How do I inspect what is actually running?”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That shift changes everything.&lt;/p&gt;

&lt;p&gt;Real execution means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real environment variables&lt;/li&gt;
&lt;li&gt;Real data&lt;/li&gt;
&lt;li&gt;Real filesystem&lt;/li&gt;
&lt;li&gt;Real configuration&lt;/li&gt;
&lt;li&gt;Real side effects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of guessing, you observe.&lt;br&gt;&lt;br&gt;
Instead of reproducing, you inspect.&lt;/p&gt;

&lt;p&gt;Debugging becomes less about stepping through lines and more about &lt;strong&gt;understanding state&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Executing methods instead of reproducing requests
&lt;/h2&gt;

&lt;p&gt;Another realization came shortly after.&lt;/p&gt;

&lt;p&gt;Most bugs don’t live in controllers.&lt;/p&gt;

&lt;p&gt;They live in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;services&lt;/li&gt;
&lt;li&gt;domain logic&lt;/li&gt;
&lt;li&gt;jobs&lt;/li&gt;
&lt;li&gt;listeners&lt;/li&gt;
&lt;li&gt;helpers&lt;/li&gt;
&lt;li&gt;edge-case conditionals&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yet we keep trying to debug them through HTTP requests.&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;HTTP is often the &lt;em&gt;worst&lt;/em&gt; entry point for debugging:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;authentication&lt;/li&gt;
&lt;li&gt;middleware&lt;/li&gt;
&lt;li&gt;CSRF&lt;/li&gt;
&lt;li&gt;cookies&lt;/li&gt;
&lt;li&gt;headers&lt;/li&gt;
&lt;li&gt;external dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So instead of reproducing requests, I started executing &lt;strong&gt;methods directly&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Run the service.&lt;br&gt;&lt;br&gt;
Pass the input.&lt;br&gt;&lt;br&gt;
Inspect the state.&lt;br&gt;&lt;br&gt;
Pause execution.  &lt;/p&gt;

&lt;p&gt;No browser.&lt;br&gt;&lt;br&gt;
No fake requests.&lt;br&gt;&lt;br&gt;
No infrastructure gymnastics.&lt;/p&gt;

&lt;p&gt;Just code, running where it actually matters.&lt;/p&gt;




&lt;h2&gt;
  
  
  A different approach to debugging
&lt;/h2&gt;

&lt;p&gt;These experiences led me to a different mental model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Debug at runtime, not at the VM level
&lt;/li&gt;
&lt;li&gt;Observe execution, don’t simulate it
&lt;/li&gt;
&lt;li&gt;Isolate sessions per developer
&lt;/li&gt;
&lt;li&gt;Avoid network dependencies when possible
&lt;/li&gt;
&lt;li&gt;Treat HTTP as optional, not mandatory
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is not to replace existing tools, but to &lt;strong&gt;cover the gap they were never designed for&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Remote environments.&lt;br&gt;&lt;br&gt;
QA servers.&lt;br&gt;&lt;br&gt;
Production-safe inspection.&lt;br&gt;&lt;br&gt;
Multi-developer workflows.&lt;/p&gt;




&lt;h2&gt;
  
  
  What this unlocks
&lt;/h2&gt;

&lt;p&gt;Once you stop relying on localhost as the source of truth, new possibilities open up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Debug QA without changing infrastructure
&lt;/li&gt;
&lt;li&gt;Inspect production safely, with isolation and timeouts
&lt;/li&gt;
&lt;li&gt;Debug jobs and services without traffic
&lt;/li&gt;
&lt;li&gt;Let multiple developers debug the same server independently
&lt;/li&gt;
&lt;li&gt;Spend less time reproducing and more time understanding
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Debugging stops being a fight against the environment.&lt;/p&gt;

&lt;p&gt;It becomes a conversation with it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Debugging is not about tools.&lt;br&gt;&lt;br&gt;
It’s about &lt;strong&gt;truth&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The closer you are to the real execution, the fewer assumptions you need to make.&lt;/p&gt;

&lt;p&gt;That insight eventually led me to build &lt;strong&gt;DDLess&lt;/strong&gt;, a runtime execution debugger designed for real Laravel environments.&lt;br&gt;&lt;br&gt;
Not as a replacement for local tools, but as a complement when local debugging stops being enough.&lt;/p&gt;

&lt;p&gt;If you’ve ever thought &lt;em&gt;“this bug only happens outside my machine”&lt;/em&gt;, you already understand the problem.&lt;/p&gt;

&lt;p&gt;The rest is just changing how you approach it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ddless.com" rel="noopener noreferrer"&gt;https://ddless.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>debugging</category>
      <category>backend</category>
    </item>
    <item>
      <title>Build Complete REST APIs in Minutes</title>
      <dc:creator>Jefferson Silva</dc:creator>
      <pubDate>Thu, 15 Jan 2026 04:38:45 +0000</pubDate>
      <link>https://dev.to/jeffsynister/build-complete-rest-apis-in-minutes-with-laravel-query-gate-3h3b</link>
      <guid>https://dev.to/jeffsynister/build-complete-rest-apis-in-minutes-with-laravel-query-gate-3h3b</guid>
      <description>&lt;p&gt;Every Laravel developer knows the drill: create a model, write a controller, define routes, add validation, implement filtering, pagination, sorting... rinse and repeat for every single entity in your application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if I told you there's a better way?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I've been working on &lt;a href="https://github.com/behindSolution/laravel-query-gate" rel="noopener noreferrer"&gt;Laravel Query Gate&lt;/a&gt;, a package that transforms how you build APIs. Instead of writing repetitive boilerplate code, you declare what you want — and Query Gate handles the rest.&lt;/p&gt;

&lt;p&gt;Let me show you how I built a complete blog API with &lt;strong&gt;Posts, Comments, Categories, Tags, Authentication, Custom Actions, and API Versioning&lt;/strong&gt; — all without writing a single controller.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem We All Face
&lt;/h2&gt;

&lt;p&gt;Here's what a typical Laravel API setup looks like:&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;// PostController.php - Just ONE of many controllers you'll write&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostController&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="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;$query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Post&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="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'status'&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;'status'&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="n"&gt;status&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="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;has&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;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&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="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'author'&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;whereHas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'author'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$q&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="s2"&gt;"%&lt;/span&gt;&lt;span class="si"&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;author&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&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;// ... 20 more filters&lt;/span&gt;

        &lt;span class="k"&gt;if&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;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'sort'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// parsing logic...&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;paginate&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="n"&gt;per_page&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;15&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;$validated&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;validate&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|string|max:255'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'content'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|string'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="c1"&gt;// ... more rules&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;Post&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;$validated&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(), destroy(), show()... you get the idea&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now multiply this by every model in your application. &lt;strong&gt;It's exhausting.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Query Gate Way
&lt;/h2&gt;

&lt;p&gt;Here's the same functionality with Laravel Query Gate:&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;// app/Models/Post.php&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Model&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;HasQueryGate&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;queryGate&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;QueryGate&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;QueryGate&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;make&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;alias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'posts'&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;middleware&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'auth:sanctum'&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;filters&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="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'string'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'in:draft,published,archived'&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="s1"&gt;'integer'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'author.name'&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;'string'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'max:100'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="s1"&gt;'created_at'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'date'&lt;/span&gt;&lt;span class="p"&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;allowedFilters&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="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'eq'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'in'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'neq'&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="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'eq'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'in'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="s1"&gt;'author.name'&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;'like'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="s1"&gt;'created_at'&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;'gte'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'lte'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'between'&lt;/span&gt;&lt;span class="p"&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;select&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="s1"&gt;'title'&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="s1"&gt;'status'&lt;/span&gt;&lt;span class="p"&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="s1"&gt;'author.name'&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;sorts&lt;/span&gt;&lt;span class="p"&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="s1"&gt;'title'&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;actions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$actions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$actions&lt;/span&gt;
                &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$action&lt;/span&gt;
                    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validations&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
                        &lt;span class="s1"&gt;'title'&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;'required'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'string'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'max:255'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                        &lt;span class="s1"&gt;'content'&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;'required'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'string'&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;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="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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;That's it.&lt;/strong&gt; No controller. No routes. No repetitive code.&lt;/p&gt;

&lt;p&gt;Query Gate automatically gives you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET    /query/posts              → List with filters, sorting, pagination
GET    /query/posts/{id}         → Show single post
POST   /query/posts              → Create post
PATCH  /query/posts/{id}         → Update post
DELETE /query/posts/{id}         → Delete post
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Real-World Example: A Complete Blog API
&lt;/h2&gt;

&lt;p&gt;I built a &lt;a href="https://github.com/behindSolution/LQG-example" rel="noopener noreferrer"&gt;complete example project&lt;/a&gt; to showcase what Query Gate can do. Here's what's included:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Advanced Filtering
&lt;/h3&gt;

&lt;p&gt;Filter by any field with operators like &lt;code&gt;eq&lt;/code&gt;, &lt;code&gt;neq&lt;/code&gt;, &lt;code&gt;like&lt;/code&gt;, &lt;code&gt;in&lt;/code&gt;, &lt;code&gt;between&lt;/code&gt;, &lt;code&gt;gt&lt;/code&gt;, &lt;code&gt;gte&lt;/code&gt;, &lt;code&gt;lt&lt;/code&gt;, &lt;code&gt;lte&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Posts published in 2024&lt;/span&gt;
GET /query/posts?filter[published_at][between]&lt;span class="o"&gt;=&lt;/span&gt;2024-01-01,2024-12-31

&lt;span class="c"&gt;# Posts by specific tags&lt;/span&gt;
GET /query/posts?filter[tag_slugs][in]&lt;span class="o"&gt;=&lt;/span&gt;laravel,php

&lt;span class="c"&gt;# Posts with 1000+ views, sorted by popularity&lt;/span&gt;
GET /query/posts?filter[views_count][gte]&lt;span class="o"&gt;=&lt;/span&gt;1000&amp;amp;sort&lt;span class="o"&gt;=&lt;/span&gt;views_count:desc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Relationship Filtering
&lt;/h3&gt;

&lt;p&gt;Query through relationships using dot notation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Posts by author name&lt;/span&gt;
GET /query/posts?filter[author.name][like]&lt;span class="o"&gt;=&lt;/span&gt;John

&lt;span class="c"&gt;# Comments on published posts&lt;/span&gt;
GET /query/comments?filter[post.status][eq]&lt;span class="o"&gt;=&lt;/span&gt;published
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Custom Actions
&lt;/h3&gt;

&lt;p&gt;Need more than CRUD? Define custom actions:&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;// app/Actions/QueryGate/Posts/PublishPost.php&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PublishPost&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;AbstractQueryGateAction&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;action&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'publish'&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;handle&lt;/span&gt;&lt;span class="p"&gt;(&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;$model&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;$payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$model&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="s1"&gt;'status'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'published'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'published_at'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;now&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="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'message'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Post published!'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'post'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$model&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;authorize&lt;/span&gt;&lt;span class="p"&gt;(&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;$model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;?bool&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;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;user&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;can&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'update'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$model&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;Now you have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;POST /query/posts/1/publish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The example includes actions for: &lt;strong&gt;Publish, Unpublish, Archive, Feature, Unfeature, Duplicate posts&lt;/strong&gt; and &lt;strong&gt;Approve, Reject, Mark as Spam comments&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. API Versioning (Yes, Built-In!)
&lt;/h3&gt;

&lt;p&gt;Evolve your API without breaking existing clients:&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;QueryGate&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;make&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;version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'2024-01-01'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;QueryGate&lt;/span&gt; &lt;span class="nv"&gt;$gate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$gate&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'string'&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;'string'&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;select&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="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'status'&lt;/span&gt;&lt;span class="p"&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;version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'2025-01-01'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;QueryGate&lt;/span&gt; &lt;span class="nv"&gt;$gate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$gate&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'string'&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;'string'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'views_count'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'integer'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// New in 2025!&lt;/span&gt;
            &lt;span class="s1"&gt;'author.name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'string'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// New in 2025!&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;select&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="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'status'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'views_count'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'author.name'&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;Clients choose their version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;GET /query/posts &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Query-Version: 2024-01-01"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And there's a changelog endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;GET /query/posts/__changelog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Automatic OpenAPI Documentation
&lt;/h3&gt;

&lt;p&gt;Query Gate generates OpenAPI/Swagger docs automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /query/docs      → Interactive documentation UI
GET /query/docs.json → OpenAPI JSON spec
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No manual documentation. It's always in sync with your code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Start
&lt;/h2&gt;

&lt;p&gt;Want to try it? Here's how to get started:&lt;/p&gt;

&lt;h3&gt;
  
  
  Install the Package
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require behindsolution/laravel-query-gate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add the Trait to Your Model
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;BehindSolution\LaravelQueryGate\Traits\HasQueryGate&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;BehindSolution\LaravelQueryGate\Support\QueryGate&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;Post&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Model&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;HasQueryGate&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;queryGate&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;QueryGate&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;QueryGate&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;make&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;alias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'posts'&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;filters&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'string'&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;'string'&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;allowedFilters&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'title'&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;'like'&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="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'eq'&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;actions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;create&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;update&lt;/span&gt;&lt;span class="p"&gt;()&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;h3&gt;
  
  
  Register in Config
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// config/query-gate.php&lt;/span&gt;
&lt;span class="s1"&gt;'models'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nc"&gt;App\Models\Post&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&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;&lt;strong&gt;Done.&lt;/strong&gt; You now have a fully functional REST API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try the Example Project
&lt;/h2&gt;

&lt;p&gt;Clone the complete example to see everything in action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone git@github.com:behindSolution/LQG-example.git
&lt;span class="nb"&gt;cd &lt;/span&gt;LQG-example
composer &lt;span class="nb"&gt;install
cp&lt;/span&gt; .env.example .env
php artisan key:generate
php artisan migrate &lt;span class="nt"&gt;--seed&lt;/span&gt;
php artisan serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Import the included Postman collection and start exploring.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Built This
&lt;/h2&gt;

&lt;p&gt;I was tired of writing the same code over and over. Every project needed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Filtering logic&lt;/li&gt;
&lt;li&gt;Sorting logic&lt;/li&gt;
&lt;li&gt;Pagination&lt;/li&gt;
&lt;li&gt;Validation&lt;/li&gt;
&lt;li&gt;Authorization checks&lt;/li&gt;
&lt;li&gt;API versioning (if you're lucky)&lt;/li&gt;
&lt;li&gt;Documentation (if you have time)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Query Gate handles all of this declaratively. You focus on your business logic, not boilerplate.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/behindSolution/laravel-query-gate" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt; - Star it if you find it useful!&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/behindSolution/LQG-example" rel="noopener noreferrer"&gt;Example Project&lt;/a&gt; - Full blog API demo&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://laravelquerygate.com/docs" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt; - Complete guide&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Have questions or feedback?&lt;/strong&gt; Drop a comment below or open an issue on GitHub. I'd love to hear how you're using Query Gate in your projects!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If this saved you some time, consider giving the repo a star. It helps more developers discover the project.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>backend</category>
      <category>api</category>
    </item>
    <item>
      <title>🔥 Laravel Query Gate v1.2.0 is AWESOME</title>
      <dc:creator>Jefferson Silva</dc:creator>
      <pubDate>Tue, 06 Jan 2026 03:48:51 +0000</pubDate>
      <link>https://dev.to/jeffsynister/laravel-query-gate-v120-is-awesome-11jj</link>
      <guid>https://dev.to/jeffsynister/laravel-query-gate-v120-is-awesome-11jj</guid>
      <description>&lt;p&gt;API versioning is one of those topics everyone talks about…&lt;br&gt;
and almost everyone implements in a painful way.&lt;/p&gt;

&lt;p&gt;Multiple controllers.&lt;br&gt;
Duplicated logic.&lt;br&gt;
Hidden conditionals.&lt;br&gt;
Broken clients.&lt;/p&gt;

&lt;p&gt;Laravel Query Gate v1.2.0 introduces a different approach:&lt;/p&gt;

&lt;p&gt;Version your API behavior at the query layer.&lt;/p&gt;

&lt;p&gt;No boilerplate.&lt;br&gt;
No magic.&lt;br&gt;
No surprises.&lt;/p&gt;

&lt;p&gt;🎯 The Problem With Traditional API Versioning&lt;/p&gt;

&lt;p&gt;Most APIs evolve like this:&lt;/p&gt;

&lt;p&gt;You change filters&lt;/p&gt;

&lt;p&gt;You remove fields&lt;/p&gt;

&lt;p&gt;You add sorting&lt;/p&gt;

&lt;p&gt;You break someone’s integration&lt;/p&gt;

&lt;p&gt;Even when using /v1/users and /v2/users, the real problem remains:&lt;/p&gt;

&lt;p&gt;👉 Query behavior changes are scattered across the codebase.&lt;/p&gt;

&lt;p&gt;Laravel Query Gate v1.2.0 centralizes this.&lt;/p&gt;

&lt;p&gt;🧠 The Key Idea&lt;/p&gt;

&lt;p&gt;Clients define the version they want:&lt;/p&gt;

&lt;p&gt;X-Query-Version: 2026-01-05&lt;/p&gt;

&lt;p&gt;The same endpoint adapts its behavior safely.&lt;/p&gt;

&lt;p&gt;GET /query/users&lt;/p&gt;

&lt;p&gt;No duplicated controllers.&lt;br&gt;
No branching logic in services.&lt;/p&gt;

&lt;p&gt;🧾 Built-in Changelog (Yes, Really)&lt;/p&gt;

&lt;p&gt;Every versioned query automatically exposes a changelog endpoint:&lt;/p&gt;

&lt;p&gt;GET /query/users/__changelog&lt;/p&gt;

&lt;p&gt;Example response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "model": "App\\Models\\User",
  "alias": "users",
  "default": "2026-01-06",
  "active": "2026-01-05",
  "versions": [
    {
      "version": "2026-01-05",
      "changes": []
    },
    {
      "version": "2026-01-06",
      "changes": [
        "Removed filter: created_at",
        "Removed operator: created_at.gte",
        "Removed operator: created_at.lte",
        "Removed operator: created_at.between",
        "Removed select: created_at",
        "Added sort: name",
        "Added sort: email",
        "Added sort: created_at"
      ]
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Instant API transparency&lt;/p&gt;

&lt;p&gt;Easier debugging&lt;/p&gt;

&lt;p&gt;Clear communication with frontend and integrations&lt;/p&gt;

&lt;p&gt;🧩 The Code That Makes It Happen&lt;/p&gt;

&lt;p&gt;Here’s a real example using Laravel Query Gate v1.2.0:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public static function queryGate(): QueryGate
{
    return QueryGate::make()
        -&amp;gt;alias('users')

        -&amp;gt;version('2026-01-05', function (QueryGate $qg) {
            return $qg-&amp;gt;filters([
                'name' =&amp;gt; 'string',
                'email' =&amp;gt; 'email',
                'created_at' =&amp;gt; 'date',
            ])
            -&amp;gt;allowedFilters([
                'name' =&amp;gt; ['like'],
                'email' =&amp;gt; ['eq', 'like'],
                'created_at' =&amp;gt; ['gte', 'lte', 'between'],
            ])
            -&amp;gt;select(['id', 'name', 'email', 'created_at']);
        })

        -&amp;gt;version('2026-01-06', function (QueryGate $qg) {
            return $qg-&amp;gt;filters([
                'name' =&amp;gt; 'string',
                'email' =&amp;gt; 'email',
            ])
            -&amp;gt;allowedFilters([
                'name' =&amp;gt; ['like'],
                'email' =&amp;gt; ['eq', 'like'],
            ])
            -&amp;gt;sorts(['name', 'email', 'created_at'])
            -&amp;gt;select(['id', 'name', 'email']);
        });
}

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

&lt;/div&gt;



&lt;p&gt;What changed between versions?&lt;/p&gt;

&lt;p&gt;created_at filter removed&lt;/p&gt;

&lt;p&gt;Operators removed safely&lt;/p&gt;

&lt;p&gt;Sorting added&lt;/p&gt;

&lt;p&gt;Select fields adjusted&lt;/p&gt;

&lt;p&gt;Old clients keep working&lt;/p&gt;

&lt;p&gt;New clients get improvements&lt;/p&gt;

&lt;p&gt;💡 Why This Is Powerful&lt;/p&gt;

&lt;p&gt;Laravel Query Gate v1.2.0 gives you:&lt;/p&gt;

&lt;p&gt;Explicit API contracts per version&lt;/p&gt;

&lt;p&gt;Safe evolution without fear&lt;/p&gt;

&lt;p&gt;One source of truth for queries&lt;/p&gt;

&lt;p&gt;Less human error&lt;/p&gt;

&lt;p&gt;Cleaner codebases&lt;/p&gt;

&lt;p&gt;It treats API evolution as a first-class concern, not an afterthought.&lt;/p&gt;

&lt;p&gt;🌐 Learn More&lt;/p&gt;

&lt;p&gt;This is just one of the features.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://laravelquerygate.com" rel="noopener noreferrer"&gt;https://laravelquerygate.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you care about:&lt;/p&gt;

&lt;p&gt;Maintainable APIs&lt;/p&gt;

&lt;p&gt;Predictable behavior&lt;/p&gt;

&lt;p&gt;Laravel-native solutions&lt;/p&gt;

&lt;p&gt;You’ll feel right at home.&lt;/p&gt;

&lt;p&gt;Feedback, ideas, and real-world edge cases are always welcome 🚀&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>api</category>
      <category>backend</category>
    </item>
    <item>
      <title>Stop Writing CRUD Controllers</title>
      <dc:creator>Jefferson Silva</dc:creator>
      <pubDate>Sun, 28 Dec 2025 04:27:43 +0000</pubDate>
      <link>https://dev.to/jeffsynister/stop-writing-crud-controllers-5dlg</link>
      <guid>https://dev.to/jeffsynister/stop-writing-crud-controllers-5dlg</guid>
      <description>&lt;p&gt;Building APIs shouldn't mean writing the same CRUD controllers over and over. &lt;strong&gt;Laravel Query Gate&lt;/strong&gt; turns your Eloquent models into powerful, secure APIs through simple configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;How many times have you written something 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;UserController&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="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;$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;// Filter by email&lt;/span&gt;
        &lt;span class="k"&gt;if&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;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'email'&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;'email'&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;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;email&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="c1"&gt;// Filter by creation date&lt;/span&gt;
        &lt;span class="k"&gt;if&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;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'created_after'&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;'created_at'&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;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;created_after&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Filter by status&lt;/span&gt;
        &lt;span class="k"&gt;if&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;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'status'&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;'status'&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="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Sort&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;$request&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="s1"&gt;'sort_by'&lt;/span&gt;&lt;span class="p"&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;$sortOrder&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'sort_order'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'desc'&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;$sortOrder&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Paginate&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;paginate&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'per_page'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&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;Now multiply that by every model in your application. Add validation. Add security checks. Add caching. Add eager loading for relations. The boilerplate grows exponentially.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// config/query-gate.php&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;BehindSolution\LaravelQueryGate\Support\QueryGate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'models'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;App\Models\User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;QueryGate&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;make&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;middleware&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'auth:sanctum'&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;filters&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
                &lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'created_at'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'date'&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;'in:active,inactive,pending'&lt;/span&gt;&lt;span class="p"&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;allowedFilters&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
                &lt;span class="s1"&gt;'email'&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;'like'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="s1"&gt;'created_at'&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;'gte'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'lte'&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="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'eq'&lt;/span&gt;&lt;span class="p"&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;cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&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 it. Now you have a fully functional, secure API endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;GET /query?model&lt;span class="o"&gt;=&lt;/span&gt;App&lt;span class="se"&gt;\M&lt;/span&gt;odels&lt;span class="se"&gt;\U&lt;/span&gt;ser
    &amp;amp;filter[email][like]&lt;span class="o"&gt;=&lt;/span&gt;%@gmail.com
    &amp;amp;filter[created_at][gte]&lt;span class="o"&gt;=&lt;/span&gt;2024-01-01
    &amp;amp;filter[status][eq]&lt;span class="o"&gt;=&lt;/span&gt;active
    &amp;amp;sort&lt;span class="o"&gt;=&lt;/span&gt;created_at:desc
    &amp;amp;per_page&lt;span class="o"&gt;=&lt;/span&gt;50
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Alias
&lt;/h3&gt;

&lt;p&gt;Keep the name short.&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;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;

    &lt;span class="s1"&gt;'models'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;App\Models\User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;QueryGate&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;make&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;alias&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'auth:sanctum'&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;filters&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
                &lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'created_at'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'date'&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;'in:active,inactive,pending'&lt;/span&gt;&lt;span class="p"&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;allowedFilters&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
                &lt;span class="s1"&gt;'email'&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;'like'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="s1"&gt;'created_at'&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;'gte'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'lte'&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="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'eq'&lt;/span&gt;&lt;span class="p"&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;cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&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;Expose pretty REST-like routes (e.g. &lt;code&gt;/query/users&lt;/code&gt;, &lt;code&gt;/query/users/{id}&lt;/code&gt;) in addition to the canonical query-string &lt;/p&gt;

&lt;h3&gt;
  
  
  🔍 Powerful Filtering
&lt;/h3&gt;

&lt;p&gt;Support for multiple operators out of the box:&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'price'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'numeric'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'string'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'published_at'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'date'&lt;/span&gt;&lt;span class="p"&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;allowedFilters&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'price'&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;'eq'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'lt'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'lte'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'gt'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'gte'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'between'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s1"&gt;'title'&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;'like'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s1"&gt;'published_at'&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;'eq'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'between'&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;&lt;strong&gt;Available operators&lt;/strong&gt;: &lt;code&gt;eq&lt;/code&gt;, &lt;code&gt;neq&lt;/code&gt;, &lt;code&gt;lt&lt;/code&gt;, &lt;code&gt;lte&lt;/code&gt;, &lt;code&gt;gt&lt;/code&gt;, &lt;code&gt;gte&lt;/code&gt;, &lt;code&gt;like&lt;/code&gt;, &lt;code&gt;in&lt;/code&gt;, &lt;code&gt;between&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🔗 Relation Filtering with Dot Notation
&lt;/h3&gt;

&lt;p&gt;Filter through relationships seamlessly:&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'posts.title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'string'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'posts.comments.author.name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'string'&lt;/span&gt;&lt;span class="p"&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;allowedFilters&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'posts.title'&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;'like'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s1"&gt;'posts.comments.author.name'&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;'eq'&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;GET /query?model&lt;span class="o"&gt;=&lt;/span&gt;App&lt;span class="se"&gt;\M&lt;/span&gt;odels&lt;span class="se"&gt;\U&lt;/span&gt;ser
    &amp;amp;filter[posts.title][like]&lt;span class="o"&gt;=&lt;/span&gt;%Laravel%
    &amp;amp;filter[posts.comments.author.name][eq]&lt;span class="o"&gt;=&lt;/span&gt;John
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🔒 Security First
&lt;/h3&gt;

&lt;p&gt;Whitelist everything. No surprises.&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'auth:sanctum'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'throttle:60,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;filters&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="c1"&gt;// Only these fields can be filtered&lt;/span&gt;
&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;allowedFilters&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'email'&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;'eq'&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt; &lt;span class="c1"&gt;// Only these operators allowed&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;'id'&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;'email'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="c1"&gt;// Only these fields returned&lt;/span&gt;
&lt;span class="o"&gt;-&amp;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;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$q&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&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="nv"&gt;$q&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;'tenant_id'&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;user&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tenant_id&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;Integrate with Laravel Policies:&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'view'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Uses your existing policy&lt;/span&gt;
&lt;span class="c1"&gt;// or&lt;/span&gt;
&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&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;$model&lt;/span&gt;&lt;span class="p"&gt;)&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="nf"&gt;user&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;isAdmin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; 
    &lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;user_id&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;user&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ⚡ Smart Caching
&lt;/h3&gt;

&lt;p&gt;Cache responses automatically with intelligent invalidation:&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cache key includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Model class&lt;/li&gt;
&lt;li&gt;All filters&lt;/li&gt;
&lt;li&gt;Sort parameters&lt;/li&gt;
&lt;li&gt;Pagination settings&lt;/li&gt;
&lt;li&gt;Authenticated user ID&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cache is automatically cleared after create, update, or delete operations.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔧 Raw Filters for Complex Cases
&lt;/h3&gt;

&lt;p&gt;Need custom logic? No problem:&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'full_name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'string'&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;rawFilters&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'full_name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$builder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$operator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$column&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="nv"&gt;$builder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;whereRaw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s2"&gt;"CONCAT(first_name, ' ', last_name) LIKE ?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"%&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%"&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;You keep the validation and whitelisting, but control the query logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  📝 Optional CRUD Operations
&lt;/h3&gt;

&lt;p&gt;Enable create, update, and delete when needed:&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$actions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$actions&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$action&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validations&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|string|max:255'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|email|unique:users'&lt;/span&gt;&lt;span class="p"&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;policy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'create'&lt;/span&gt;&lt;span class="p"&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;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$action&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validations&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'sometimes|string|max:255'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'sometimes|email|unique:users,email'&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;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="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$action&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 
            &lt;span class="nv"&gt;$req&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;user&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;isAdmin&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;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Endpoints&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;POST   /query?model&lt;span class="o"&gt;=&lt;/span&gt;App&lt;span class="se"&gt;\M&lt;/span&gt;odels&lt;span class="se"&gt;\U&lt;/span&gt;ser          &lt;span class="c"&gt;# Create&lt;/span&gt;
PATCH  /query/&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;?model&lt;span class="o"&gt;=&lt;/span&gt;App&lt;span class="se"&gt;\M&lt;/span&gt;odels&lt;span class="se"&gt;\U&lt;/span&gt;ser     &lt;span class="c"&gt;# Update&lt;/span&gt;
DELETE /query/&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;?model&lt;span class="o"&gt;=&lt;/span&gt;App&lt;span class="se"&gt;\M&lt;/span&gt;odels&lt;span class="se"&gt;\U&lt;/span&gt;ser     &lt;span class="c"&gt;# Delete&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🎯 Flexible Pagination
&lt;/h3&gt;

&lt;p&gt;Choose what fits your use case:&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;paginationMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'cursor'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// For infinite scroll&lt;/span&gt;
&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;paginationMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'classic'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Standard page-based (default)&lt;/span&gt;
&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;paginationMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'none'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;// Return everything&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  📊 Column Selection
&lt;/h3&gt;

&lt;p&gt;Reduce payload size by selecting only needed fields:&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="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;'id'&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;'email'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'posts.title'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Primary and foreign keys are automatically included for proper relation hydration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Multi-tenant SaaS
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;App\Models\Project&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;QueryGate&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;make&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;middleware&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'auth:sanctum'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'tenant.isolate'&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;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$q&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&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="nv"&gt;$q&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;'tenant_id'&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;user&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tenant_id&lt;/span&gt;&lt;span class="p"&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;filters&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;'in:planning,active,completed,archived'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'created_at'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'date'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'members.user_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'integer'&lt;/span&gt;&lt;span class="p"&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;allowedFilters&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="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'eq'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'in'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="s1"&gt;'created_at'&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;'between'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="s1"&gt;'members.user_id'&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;'eq'&lt;/span&gt;&lt;span class="p"&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;cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;300&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;actions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$actions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$actions&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$action&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validations&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
                &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|string|max:255'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'description'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'nullable|string'&lt;/span&gt;&lt;span class="p"&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;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&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;$model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tenant_id&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;user&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tenant_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;save&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;$model&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;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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  E-commerce Product Catalog
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;App\Models\Product&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;QueryGate&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;make&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;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$q&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;selectRaw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'products.*, AVG(reviews.rating) as avg_rating'&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;leftJoin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'reviews'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'reviews.product_id'&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="s1"&gt;'products.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="nf"&gt;groupBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'products.id'&lt;/span&gt;&lt;span class="p"&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;filters&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="s1"&gt;'integer'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'price'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'numeric'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'tags.slug'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'string'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'in_stock'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'boolean'&lt;/span&gt;&lt;span class="p"&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;allowedFilters&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="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'eq'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'in'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="s1"&gt;'price'&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;'between'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'lte'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'gte'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="s1"&gt;'tags.slug'&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;'in'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="s1"&gt;'in_stock'&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;'eq'&lt;/span&gt;&lt;span class="p"&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;select&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="s1"&gt;'name'&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;'avg_rating'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'image_url'&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;cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Analytics Dashboard
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;App\Models\Order&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;QueryGate&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;make&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;middleware&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'auth:sanctum'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'role: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;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$q&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;selectRaw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'
            DATE(created_at) as date,
            COUNT(*) as total_orders,
            SUM(total) as revenue,
            AVG(total) as average_order_value
        '&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;groupBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'date'&lt;/span&gt;&lt;span class="p"&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;filters&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s1"&gt;'created_at'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'date'&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;'in:pending,completed,cancelled,refunded'&lt;/span&gt;&lt;span class="p"&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;allowedFilters&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s1"&gt;'created_at'&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;'between'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'gte'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'lte'&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="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'eq'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'in'&lt;/span&gt;&lt;span class="p"&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;paginationMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'none'&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;cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'orders-analytics'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require behindsolution/laravel-query-gate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Optionally publish the config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan vendor:publish &lt;span class="nt"&gt;--tag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;query-gate-config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  When to Use Query Gate
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;✅ Perfect for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Admin dashboards&lt;/li&gt;
&lt;li&gt;Internal tools&lt;/li&gt;
&lt;li&gt;BI/Analytics integrations&lt;/li&gt;
&lt;li&gt;Mobile app backends&lt;/li&gt;
&lt;li&gt;Rapid prototyping&lt;/li&gt;
&lt;li&gt;Reports and data exports&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;❌ Not ideal for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Public APIs with complex business logic&lt;/li&gt;
&lt;li&gt;Operations requiring multi-step workflows&lt;/li&gt;
&lt;li&gt;Cases where you need fine-grained control over every query&lt;/li&gt;
&lt;li&gt;GraphQL (use Laravel Lighthouse instead)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Security Best Practices
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Always authenticate&lt;/strong&gt;: Use &lt;code&gt;-&amp;gt;middleware(['auth:sanctum'])&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Whitelist explicitly&lt;/strong&gt;: Only expose models you intend to&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Restrict operators&lt;/strong&gt;: Don't allow all operators on all fields&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate inputs&lt;/strong&gt;: Use &lt;code&gt;-&amp;gt;filters()&lt;/code&gt; with validation rules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apply rate limiting&lt;/strong&gt;: Protect against abuse&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scope by tenant&lt;/strong&gt;: Use &lt;code&gt;-&amp;gt;query()&lt;/code&gt; for multi-tenant isolation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use policies&lt;/strong&gt;: Integrate with your existing authorization
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ Good&lt;/span&gt;
&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'email'&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;allowedFilters&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'email'&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;'eq'&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;middleware&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'auth:sanctum'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'throttle:60,1'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;// ❌ Bad&lt;/span&gt;
&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'string'&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;allowedFilters&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'email'&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;'eq'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'neq'&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;'in'&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt; &lt;span class="c1"&gt;// Too permissive&lt;/span&gt;
&lt;span class="c1"&gt;// No middleware!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Performance Considerations
&lt;/h2&gt;

&lt;p&gt;Query Gate delegates to Laravel's Eloquent, so standard optimization practices apply:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use eager loading&lt;/strong&gt; in &lt;code&gt;-&amp;gt;query()&lt;/code&gt; to avoid N+1:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;   &lt;span class="o"&gt;-&amp;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;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$q&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="s1"&gt;'posts'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'comments'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Add database indexes&lt;/strong&gt; for filtered/sorted columns&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enable caching&lt;/strong&gt; for expensive queries:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;   &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Limit pagination size&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;   &lt;span class="c1"&gt;// config/query-gate.php&lt;/span&gt;
   &lt;span class="s1"&gt;'pagination'&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;'per_page'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="s1"&gt;'max_per_page'&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="p"&gt;],&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use cursor pagination&lt;/strong&gt; for large datasets:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;   &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;paginationMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'cursor'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Comparison with Alternatives
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Query Gate&lt;/th&gt;
&lt;th&gt;Custom Controllers&lt;/th&gt;
&lt;th&gt;GraphQL&lt;/th&gt;
&lt;th&gt;API Platform&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Setup Time&lt;/td&gt;
&lt;td&gt;5 minutes&lt;/td&gt;
&lt;td&gt;Hours&lt;/td&gt;
&lt;td&gt;Days&lt;/td&gt;
&lt;td&gt;Days&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Laravel Native&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Learning Curve&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flexibility&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Total&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auto Validation&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Caching&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q: Does this replace controllers?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A: No. Use Query Gate for standard CRUD + filtering. Create controllers for complex business logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Is it production-ready?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A: Yes. It uses Laravel's core components (Eloquent, Validator, Policies, Cache). When configured properly, it's as secure as any Laravel API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Can I customize the query logic?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A: Absolutely. Use &lt;code&gt;-&amp;gt;query()&lt;/code&gt;, &lt;code&gt;-&amp;gt;rawFilters()&lt;/code&gt;, &lt;code&gt;-&amp;gt;authorize()&lt;/code&gt;, and &lt;code&gt;-&amp;gt;handle()&lt;/code&gt; callbacks for full control.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: How does it handle relationships?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A: Via dot notation in filters and select. Query Gate automatically handles joins and eager loading.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: What about multi-tenancy?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A: Use &lt;code&gt;-&amp;gt;query()&lt;/code&gt; to scope by tenant or apply tenant isolation middleware.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Laravel Query Gate solves a specific problem: &lt;strong&gt;eliminating boilerplate for standard CRUD operations&lt;/strong&gt;. It's not trying to replace controllers or be a full GraphQL alternative. &lt;/p&gt;

&lt;p&gt;It's a tool that saves you time on the 80% of straightforward cases, while giving you complete escape hatches for the 20% that need custom logic.&lt;/p&gt;

&lt;p&gt;Try it in your next Laravel project. Your future self will thank you for not writing another paginated, filtered, sorted CRUD controller.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📦 &lt;a href="https://laravelquerygate.com" rel="noopener noreferrer"&gt;Site&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📚 &lt;a href="https://laravelquerygate.com/docs" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💬 &lt;a href="https://github.com/behindsolution/laravel-query-gate/issues" rel="noopener noreferrer"&gt;Issues &amp;amp; Discussions&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Requirements:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PHP 8.2+&lt;/li&gt;
&lt;li&gt;Laravel 9, 10, 11, or 12&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Have you used Query Gate? Share your experience in the comments! Found a bug or have a feature request? Open an issue on GitHub.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>api</category>
      <category>backend</category>
    </item>
  </channel>
</rss>
