Eloquent Query Objects: Typed, Testable Builder Scopes | Mohamed Said        [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com) [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles  ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

       [  ](https://github.com/EG-Mohamed)       

 [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

  [ home ](https://msaied.com)    [ articles ](https://msaied.com/articles)    Laravel Macro-Free Extensibility: Extending Eloquent Builder with Custom Query Objects        On this page       1. [  The Problem With Macro Sprawl ](#the-problem-with-macro-sprawl)
2. [  What Is a Query Object? ](#what-is-a-query-object)
3. [  Composing Multiple Query Objects ](#composing-multiple-query-objects)
4. [  Binding Query Objects via the Service Container ](#binding-query-objects-via-the-service-container)
5. [  Testing in Isolation With Pest ](#testing-in-isolation-with-pest)
6. [  When to Prefer Local Scopes Instead ](#when-to-prefer-local-scopes-instead)
7. [  Takeaways ](#takeaways)

  ![Laravel Macro-Free Extensibility: Extending Eloquent Builder with Custom Query Objects](https://cdn.msaied.com/621/b0c176a363378658e83bb44ed379879b.png)

  #laravel   #eloquent   #clean-architecture   #testing   #pest  

 Laravel Macro-Free Extensibility: Extending Eloquent Builder with Custom Query Objects 
========================================================================================

     2 Sep 2026      1 min read    ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said  

       Table of contents

1. [  01   The Problem With Macro Sprawl  ](#the-problem-with-macro-sprawl)
2. [  02   What Is a Query Object?  ](#what-is-a-query-object)
3. [  03   Composing Multiple Query Objects  ](#composing-multiple-query-objects)
4. [  04   Binding Query Objects via the Service Container  ](#binding-query-objects-via-the-service-container)
5. [  05   Testing in Isolation With Pest  ](#testing-in-isolation-with-pest)
6. [  06   When to Prefer Local Scopes Instead  ](#when-to-prefer-local-scopes-instead)
7. [  07   Takeaways  ](#takeaways)

 The Problem With Macro Sprawl
-----------------------------

Macros are convenient, but registering a dozen `Builder::macro()` calls in a service provider creates invisible global state. There is no type hint, no IDE completion, and no obvious place to test them in isolation. For teams working on a modular monolith or a domain-rich application, typed query objects are a better trade-off.

What Is a Query Object?
-----------------------

A query object is a plain PHP class that accepts an `Illuminate\Database\Eloquent\Builder` instance and applies one cohesive set of constraints. It is not a repository — it does not fetch results. It only *scopes* the query, leaving the caller in control of pagination, eager loading, and execution.

```php
