Laravel Collection Macros, Mixins &amp; Proxies | Mohamed Said       [Skip to content](#main)  [ ![](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png) Mohamed SaidLaravel Backend Engineer ](https://www.msaied.com/public) - [Home](https://www.msaied.com/public)
- [Projects](https://www.msaied.com/public/projects)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [About](https://www.msaied.com/public#about)

           [  Contact](https://www.msaied.com/public#contact) Menu 

Menu
----

Close 

 - [HomeStart here](https://www.msaied.com/public)
- [ProjectsCase studies](https://www.msaied.com/public/projects)
- [ArticlesEngineering notes](https://www.msaied.com/public/articles)
- [CertificatesCredentials](https://www.msaied.com/public/certificates)
- [AboutHow I work](https://www.msaied.com/public#about)
- [ContactGet in touch](https://www.msaied.com/public#contact)

  [Start a conversation](https://www.msaied.com/public#contact) [WhatsApp](https://wa.me/201094619204) [Email](mailto:hello@msaied.com) 

 1. [Home](https://www.msaied.com/public)
2. /
3. [Articles](https://www.msaied.com/public/articles)
4. /
5. Extending Laravel Collections: Macros, Mixins, and Higher-Order Proxies

 Extending Laravel Collections: Macros, Mixins, and Higher-Order Proxies
========================================================================

 Learn how to add domain-specific methods to Laravel's Collection class using macros, mixins, and higher-order proxies — without polluting your codebase with one-off helpers.

 ![](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp) [Mohamed Said](https://www.msaied.com/public#person) Published 28 Jun 2026 · Updated 28 Jun 2026 · 4 min read

ShareCopy linkCopied

 ![Extending Laravel Collections: Macros, Mixins, and Higher-Order Proxies](https://cdn.msaied.com/312/7bf5ac9ba3c8384848342d23b87fef21.png) 

  On this page +1. [Why Extend Collections at All?](#why-extend-collections-at-all)
2. [Macros: One-Off Domain Methods](#macros-one-off-domain-methods)
3. [Mixins: Grouping Related Macros](#mixins-grouping-related-macros)
4. [Higher-Order Proxies: Chainable Magic](#higher-order-proxies-chainable-magic)
5. [Lazy Collections and Macros](#lazy-collections-and-macros)
6. [Testing Your Macros](#testing-your-macros)
7. [Key Takeaways](#key-takeaways)

 Why Extend Collections at All?
------------------------------

Laravel's `Collection` class covers the common 80%, but real domain code always needs more. You could reach for a static helper, a standalone function, or a one-off `map()` chain — but each of those scatters intent across the codebase. Macros and mixins let you encode domain vocabulary directly on the collection, so call sites read like prose.

---

Macros: One-Off Domain Methods
------------------------------

A macro is the simplest extension point. Register it in a service provider and it becomes available on every `Collection` instance.

```php
// app/Providers/CollectionServiceProvider.php
use Illuminate\Support\Collection;
use Illuminate\Support\ServiceProvider;

class CollectionServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Collection::macro('toIndexedById', function (string $key = 'id'): Collection {
            /** @var Collection $this */
            return $this->keyBy($key);
        });

        Collection::macro('sumMoney', function (string $attribute): int {
            /** @var Collection $this */
            return $this->sum(fn ($item) => (int) data_get($item, $attribute));
        });
    }
}

```

Call sites are now expressive:

```php
$invoices->toIndexedById();          // keyed by 'id'
$lineItems->sumMoney('amount_cents'); // domain-aware sum

```

> **IDE support tip:** Add a `/** @mixin \Illuminate\Support\Collection */` docblock to a stub class and reference it in your `_ide_helper_macros.php` so PHPStorm resolves the methods.

---

Mixins: Grouping Related Macros
-------------------------------

When you have a cluster of related methods, a mixin class keeps the service provider lean. `Collection::mixin()` reflects over every public method and registers each one as a macro.

```php
// app/Collections/MoneyCollectionMixin.php
class MoneyCollectionMixin
{
    public function sumMoney(): Closure
    {
        return function (string $attribute): int {
            return $this->sum(fn ($item) => (int) data_get($item, $attribute));
        };
    }

    public function averageMoney(): Closure
    {
        return function (string $attribute): float {
            return $this->avg(fn ($item) => (int) data_get($item, $attribute)) ?? 0.0;
        };
    }

    public function formatAsCurrency(): Closure
    {
        return function (string $attribute, string $currency = 'USD'): Collection {
            return $this->map(function ($item) use ($attribute, $currency) {
                data_set($item, $attribute, number_format(
                    data_get($item, $attribute) / 100, 2
                ) . ' ' . $currency);
                return $item;
            });
        };
    }
}

```

```php
// In the service provider:
Collection::mixin(new MoneyCollectionMixin());

```

Each public method returns a `Closure`; inside that closure `$this` is the `Collection` instance. This pattern scales cleanly — add a new mixin per bounded context.

---

Higher-Order Proxies: Chainable Magic
-------------------------------------

Laravel ships with higher-order proxies for methods like `each`, `map`, `filter`, `reject`, `every`, `first`, `flatMap`, `groupBy`, `keyBy`, `max`, `min`, `partition`, `reject`, `skipUntil`, `skipWhile`, `sortBy`, `sortByDesc`, `sum`, `takeUntil`, `takeWhile`, and `unique`. They let you call a property on the collection and chain a method name directly:

```php
$orders->each->recalculateTotals();
$users->filter->isActive()->values();
$invoices->sortByDesc->createdAt();

```

This works because `Collection::$proxies` is a public static array. You can register your own model methods into it:

```php
// In a service provider boot()
Collection::$proxies[] = 'approve';
Collection::$proxies[] = 'archive';

```

Now `$invoices->each->approve()` dispatches `approve()` on every item — no explicit closure needed.

---

Lazy Collections and Macros
---------------------------

`LazyCollection` is a separate class but also uses `Macroable`. Register macros on it independently if you need them on cursor-based result sets:

```php
use Illuminate\Support\LazyCollection;

LazyCollection::macro('filterActive', function (): LazyCollection {
    return $this->filter(fn ($item) => $item->is_active);
});

```

---

Testing Your Macros
-------------------

```php
// tests/Unit/Collections/MoneyCollectionMixinTest.php
use Illuminate\Support\Collection;

it('sums money attributes in cents', function () {
    $items = Collection::make([
        ['amount_cents' => 1000],
        ['amount_cents' => 2500],
    ]);

    expect($items->sumMoney('amount_cents'))->toBe(3500);
});

```

Register the mixin inside a `beforeEach` or rely on the full application bootstrap — either works with Pest.

---

Key Takeaways
-------------

- **Macros** are ideal for one-off domain methods; register them in a dedicated service provider.
- **Mixins** group related macros into a class, keeping the provider clean and the logic testable.
- **Higher-order proxies** eliminate boilerplate closures for single-method dispatches on collection items.
- **`LazyCollection`** has its own `Macroable` trait — register macros on it separately when working with large datasets.
- Always write unit tests for macros; they are pure functions and trivially testable with Pest.

- [laravel](https://www.msaied.com/public/articles?search=laravel)
- [collections](https://www.msaied.com/public/articles?search=collections)
- [macros](https://www.msaied.com/public/articles?search=macros)
- [php](https://www.msaied.com/public/articles?search=php)

 Frequently asked questions 
---------------------------

  Can I use type hints and return types inside a Collection macro closure?Yes, but because the closure is bound to the Collection instance at runtime, you cannot use `self` or `static` as return types. Use `Collection` explicitly, or omit the return type and rely on docblocks for IDE support.

   Do macros registered on Collection also apply to Eloquent's Collection subclass?No. `Illuminate\\Database\\Eloquent\\Collection` extends the base Collection but has its own class. Register the macro on both classes, or register it only on the base class and call `parent::` methods — Eloquent Collection will inherit macros registered on the base via PHP's method resolution if the macro is not overridden.

   Is there a performance cost to using macros over native Collection methods?The overhead is a single `\_\_call` dispatch and a closure invocation per macro call — negligible in practice. The real cost is always the underlying iteration, not the dispatch mechanism.

   ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)About the author
----------------

[Mohamed Said](https://www.msaied.com/public#person)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

[About](https://www.msaied.com/public#about) [GitHub ↗](https://github.com/EG-Mohamed) [LinkedIn ↗](https://www.linkedin.com/in/msaiedm/) [WhatsApp ↗](https://wa.me/201094619204) [Email Address ↗](mailto:hello@msaied.com) [My CV ↗](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)  

   [Previous articleFilament at Scale: Multi-Panel Auth, Custom Panels, and Table Query Tuning](https://www.msaied.com/public/articles/filament-at-scale-multi-panel-auth-custom-panels-and-table-query-tuning-1) [Next articleLaravel Horizon: Queue Metrics, Supervisor Tuning, and Reliable Job Throughput](https://www.msaied.com/public/articles/laravel-horizon-queue-metrics-supervisor-tuning-and-reliable-job-throughput-1)  

   On this page
-------------

1. [Why Extend Collections at All?](#why-extend-collections-at-all)
2. [Macros: One-Off Domain Methods](#macros-one-off-domain-methods)
3. [Mixins: Grouping Related Macros](#mixins-grouping-related-macros)
4. [Higher-Order Proxies: Chainable Magic](#higher-order-proxies-chainable-magic)
5. [Lazy Collections and Macros](#lazy-collections-and-macros)
6. [Testing Your Macros](#testing-your-macros)
7. [Key Takeaways](#key-takeaways)

 ###  Have a technical challenge?

 Tell me what you’re building. I reply within two working days.

[Start a conversation](https://www.msaied.com/public#contact) 

   Related articles
-----------------

 [ ![](https://cdn.msaied.com/740/cce86edc21eddcbdd2f2454fadaf9c70.png)  · 3 min read### The Pipeline Pattern in Laravel: Custom Pipelines Beyond Middleware

5 Oct 2026 ](https://www.msaied.com/public/articles/the-pipeline-pattern-in-laravel-custom-pipelines-beyond-middleware-1) [ ![](https://cdn.msaied.com/739/2d6897fdcdcf090613f96f72a64b8a78.png)  · 4 min read### MySQL Full-Text Search in Laravel: Indexes, Relevance Scoring, and Boolean Mode

4 Oct 2026 ](https://www.msaied.com/public/articles/mysql-full-text-search-in-laravel-indexes-relevance-scoring-and-boolean-mode) [ ![](https://cdn.msaied.com/738/073696a3fefe18bec825beec5ac658f5.png)  · 4 min read### Laravel Queue Rate-Limited Middleware: Throttling Jobs Without Losing Work

4 Oct 2026 ](https://www.msaied.com/public/articles/laravel-queue-rate-limited-middleware-throttling-jobs-without-losing-work) 

  Have a technical challenge?
----------------------------

Tell me what you’re building. I reply within two working days.

 [Discuss your project ↗](https://www.msaied.com/public#contact) 

  © 2026 Mohamed Said · Built with Laravel, meant to last.Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

 - [Home](https://www.msaied.com/public)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [GitHub](https://github.com/EG-Mohamed)
- [LinkedIn](https://www.linkedin.com/in/msaiedm/)
- [WhatsApp](https://wa.me/201094619204)
- [Email Address](mailto:hello@msaied.com)
- [My CV](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
- [Sitemap](https://www.msaied.com/public/sitemap.xml)
