Laravel Macros, Mixins &amp; Custom Collections | 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. Laravel Macros, Mixins, and Custom Collection Methods That Actually Ship

 Laravel Macros, Mixins, and Custom Collection Methods That Actually Ship
=========================================================================

 Go beyond toy examples: learn how to register macros safely, build reusable mixins, and add typed custom Collection methods that survive Octane restarts and static analysis.

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

ShareCopy linkCopied

 ![Laravel Macros, Mixins, and Custom Collection Methods That Actually Ship](https://cdn.msaied.com/442/b239b69e5a334711c8b4ca8804556c64.png) 

  On this page +1. [Why Macros Deserve More Respect](#why-macros-deserve-more-respect)
2. [Pattern 1 — Targeted Macros in Focused Providers](#pattern-1-targeted-macros-in-focused-providers)
3. [Octane Safety](#octane-safety)
4. [Pattern 2 — Mixins for Cohesive Method Groups](#pattern-2-mixins-for-cohesive-method-groups)
5. [Pattern 3 — Typed Collection Subclasses (The Underused One)](#pattern-3-typed-collection-subclasses-the-underused-one)
6. [Testing Your Macros](#testing-your-macros)
7. [Takeaways](#takeaways)

 Why Macros Deserve More Respect
-------------------------------

Most teams discover `Macro` when they want a one-liner on `Str` or `Collection`. They drop it in `AppServiceProvider::boot`, ship it, and move on. That works — until you run under Octane, add PHPStan, or onboard a developer who has no idea where `->toAssocBy()` came from.

This article covers the three patterns that actually hold up in production: **targeted macros**, **mixins**, and **typed Collection subclasses**.

---

Pattern 1 — Targeted Macros in Focused Providers
------------------------------------------------

Registering every macro in `AppServiceProvider` creates a god-provider. Instead, create a dedicated provider per domain.

```php
// app/Providers/CollectionMacroServiceProvider.php
class CollectionMacroServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Collection::macro('toAssocBy', function (string $key): Collection {
            /** @var Collection $this */
            return $this->keyBy($key);
        });

        Collection::macro('filterMap', function (Closure $callback): Collection {
            /** @var Collection $this */
            return $this->map($callback)->filter()->values();
        });
    }
}

```

Register it in `bootstrap/providers.php` (Laravel 11+) or `config/app.php`. The provider is small, testable, and easy to grep.

### Octane Safety

Macros are stored in a static `$macros` array on the `Macroable` trait. Under Octane the worker boots once, so macros registered in `boot()` persist across requests — that is exactly what you want. The danger is re-registering inside a request lifecycle (e.g., inside a Livewire component). Keep macro registration in providers only.

---

Pattern 2 — Mixins for Cohesive Method Groups
---------------------------------------------

A mixin is a plain class whose public methods become macros in bulk. Use it when you have five or more related methods.

```php
// app/Support/CollectionDateMixin.php
/**
 * @mixin \Illuminate\Support\Collection
 */
class CollectionDateMixin
{
    public function betweenDates(): Closure
    {
        return function (string $field, Carbon $from, Carbon $to): Collection {
            /** @var Collection $this */
            return $this->filter(
                fn ($item) => data_get($item, $field) >= $from
                    && data_get($item, $field) values();
        };
    }

    public function latestBy(): Closure
    {
        return function (string $field): mixed {
            /** @var Collection $this */
            return $this->sortByDesc($field)->first();
        };
    }
}

```

```php
// In your provider
Collection::mixin(new CollectionDateMixin());

```

The `@mixin` docblock is picked up by **Laravel IDE Helper** and **PHPStan** (with the `larastan/larastan` extension), giving you autocompletion and type inference without extra stubs.

---

Pattern 3 — Typed Collection Subclasses (The Underused One)
-----------------------------------------------------------

For domain-specific collections, a subclass beats a macro every time. You get real return types, no `@var` hacks, and Eloquent integrates natively.

```php
// app/Domain/Billing/InvoiceCollection.php
/**
 * @extends Collection
 */
class InvoiceCollection extends Collection
{
    public function totalOwed(): Money
    {
        return $this->reduce(
            fn (Money $carry, Invoice $invoice) => $carry->add($invoice->amount),
            Money::of(0, 'GBP')
        );
    }

    public function overdue(): static
    {
        return $this->filter(
            fn (Invoice $i) => $i->due_at->isPast() && ! $i->paid
        )->values();
    }
}

```

Tell Eloquent to use it:

```php
// app/Models/Invoice.php
class Invoice extends Model
{
    public function newCollection(array $models = []): InvoiceCollection
    {
        return new InvoiceCollection($models);
    }
}

```

Now `Invoice::all()` returns `InvoiceCollection`, and PHPStan knows it.

---

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

```php
// tests/Unit/CollectionMacroTest.php
it('filterMap removes null results', function () {
    $result = collect([1, 2, 3, 4])
        ->filterMap(fn ($n) => $n % 2 === 0 ? $n * 10 : null);

    expect($result->values()->all())->toBe([20, 40]);
});

```

Unit-test macros in isolation — no HTTP, no database. Fast feedback, easy CI.

---

Takeaways
---------

- Register macros in **dedicated providers**, never inside request-scoped code.
- Use **mixins** when you have a cohesive group of five or more methods; the `@mixin` docblock unlocks IDE and static analysis support.
- Prefer **typed Collection subclasses** for domain models — they give real generics, no magic strings, and Eloquent wires them up automatically.
- Macros survive Octane restarts because they live in static state bootstrapped once per worker.
- Always write a unit test per macro; they are trivial to test and painful to debug silently broken.

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

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

  Are Laravel macros safe to use under Octane?Yes, as long as you register them in a service provider's boot() method. Macros are stored in static arrays that persist for the worker's lifetime, which is exactly what you want. Never register macros inside request-scoped code like controllers or Livewire components.

   When should I use a mixin versus a typed Collection subclass?Use a mixin when you want to add utility methods to the base Collection class for general use across the app. Use a typed subclass when the collection belongs to a specific domain model — you get real generic type hints, PHPStan support, and Eloquent integration via newCollection().

   How do I get PHPStan to recognise custom macros?Add the @mixin docblock to your mixin class and ensure larastan/larastan is installed. For standalone macros, you can write a PHPStan extension or use IDE Helper's generated \_ide\_helper.php, which larastan reads automatically.

   ![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 v3 Custom Field Plugins, Custom Columns, and Render Hooks in Practice](https://www.msaied.com/public/articles/filament-v3-custom-field-plugins-custom-columns-and-render-hooks-in-practice) [Next articlePostgreSQL Partial, Covering, and Expression Indexes for Laravel Query Tuning](https://www.msaied.com/public/articles/postgresql-partial-covering-and-expression-indexes-for-laravel-query-tuning)  

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

1. [Why Macros Deserve More Respect](#why-macros-deserve-more-respect)
2. [Pattern 1 — Targeted Macros in Focused Providers](#pattern-1-targeted-macros-in-focused-providers)
3. [Octane Safety](#octane-safety)
4. [Pattern 2 — Mixins for Cohesive Method Groups](#pattern-2-mixins-for-cohesive-method-groups)
5. [Pattern 3 — Typed Collection Subclasses (The Underused One)](#pattern-3-typed-collection-subclasses-the-underused-one)
6. [Testing Your Macros](#testing-your-macros)
7. [Takeaways](#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)
