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. Macros, Mixins, and Custom Collection Methods in Laravel

 Macros, Mixins, and Custom Collection Methods in Laravel
=========================================================

 Learn how to extend Laravel's core classes with macros, mixins, and custom Collection methods — without monkey-patching or bloating service providers.

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

ShareCopy linkCopied

 ![Macros, Mixins, and Custom Collection Methods in Laravel](https://cdn.msaied.com/251/1879274587b94ac9ed89c7ccf6d75a64.png) 

  On this page +1. [Extending Laravel Without Forking It](#extending-laravel-without-forking-it)
2. [Macros: One-Off Extensions](#macros-one-off-extensions)
3. [Mixins: Grouping Related Macros](#mixins-grouping-related-macros)
4. [Custom Collection Classes: When Macros Aren't Enough](#custom-collection-classes-when-macros-arent-enough)
5. [Organising Registration: Avoid the God Provider](#organising-registration-avoid-the-god-provider)
6. [Key Takeaways](#key-takeaways)

 Extending Laravel Without Forking It
------------------------------------

Laravel ships with a `Macroable` trait that lets you bolt new methods onto core classes at runtime. Used carelessly, this becomes a dumping ground in `AppServiceProvider`. Used well, it's a clean extension point that keeps your domain vocabulary inside the framework's fluent API.

This article covers three distinct patterns: standalone macros, mixin classes, and purpose-built Collection methods — with concrete examples and the tradeoffs of each.

---

Macros: One-Off Extensions
--------------------------

The `Macroable` trait is used by `Collection`, `Builder`, `Request`, `Response`, `Str`, `Arr`, and more. Registering a macro is straightforward:

```php
use Illuminate\Support\Collection;

Collection::macro('toAssoc', function (string $keyField, string $valueField): array {
    /** @var Collection $this */
    return $this->mapWithKeys(
        fn ($item) => [$item[$keyField] => $item[$valueField]]
    )->all();
});

// Usage
$map = collect($rows)->toAssoc('id', 'name');

```

Inside the closure, `$this` is bound to the macro's host object, so you get full access to its public and protected API. PHPStan and IDE plugins won't know about it by default — address that with a `@method` annotation on a stub or a dedicated `_ide_helper_macros.php` file.

---

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

When you have five or more related macros, a mixin class keeps them cohesive and testable:

```php
namespace App\Support\Mixins;

class CollectionMixin
{
    public function toAssoc(): \Closure
    {
        return function (string $keyField, string $valueField): array {
            return $this->mapWithKeys(
                fn ($item) => [$item[$keyField] => $item[$valueField]]
            )->all();
        };
    }

    public function groupByFirst(): \Closure
    {
        return function (string $key): self {
            return $this->groupBy(fn ($item) => $item[$key] ?? null);
        };
    }
}

```

Register the mixin once, typically in a focused service provider:

```php
use Illuminate\Support\Collection;
use App\Support\Mixins\CollectionMixin;

Collection::mixin(new CollectionMixin());

```

`mixin()` reflects over every public method, calls it to retrieve the closure, and registers each as a macro. The result is identical to registering macros individually, but the code is organised and unit-testable in isolation.

---

Custom Collection Classes: When Macros Aren't Enough
----------------------------------------------------

For domain-specific pipelines, a typed custom collection beats macros every time. You get return-type safety, IDE completion, and no global side effects:

```php
namespace App\Domain\Billing\Collections;

use Illuminate\Support\Collection;
use App\Domain\Billing\ValueObjects\Money;

/**
 * @extends Collection
 */
class InvoiceCollection extends Collection
{
    public function totalDue(): Money
    {
        return Money::ofMinorUnits(
            $this->sum(fn ($invoice) => $invoice->amount_due_cents)
        );
    }

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

```

Wire it to your Eloquent model so `get()` and `all()` return the typed collection automatically:

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

```

Now `Invoice::where('user_id', $id)->get()` returns an `InvoiceCollection`, and `->totalDue()` is fully typed.

---

Organising Registration: Avoid the God Provider
-----------------------------------------------

Don't pile macros into `AppServiceProvider`. Instead:

1. Create a `MacroServiceProvider` per domain or package.
2. Register it in `bootstrap/providers.php` (Laravel 11+) or `config/app.php`.
3. Keep each mixin class in `app/Support/Mixins/` or inside the relevant domain folder.

```php
// app/Providers/BillingMacroServiceProvider.php
public function boot(): void
{
    Collection::mixin(new CollectionMixin());
    Request::mixin(new RequestMixin());
}

```

This makes it trivial to extract a domain into a package later — the service provider and mixin classes move together.

---

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

- Use **standalone macros** for one-off, cross-cutting helpers.
- Use **mixin classes** when you have a cohesive group of related extensions; they're unit-testable and self-documenting.
- Use **custom Collection subclasses** for domain-specific pipelines where return-type safety matters.
- Register macros in **dedicated service providers**, not `AppServiceProvider`.
- Generate IDE helper stubs (`php artisan ide-helper:generate`) or maintain `@method` annotations so static analysis tools stay accurate.

- [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 macros on classes that don't use the Macroable trait?No. Only classes that include the `Macroable` trait support `::macro()` and `::mixin()`. If you need to extend a non-macroable class, subclass it or use a decorator instead.

   Do macros survive between Octane requests?Yes — macros are registered on the class itself (stored in a static array), so they persist across requests in long-lived workers. Register them once in a service provider's `boot()` method and they remain available for the lifetime of the worker process.

   When should I prefer a custom Collection subclass over a mixin?Prefer a subclass when the methods are domain-specific, return typed values, or only make sense for a particular model. Mixins are better for generic, reusable helpers that apply across many collection types.

   ![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: Custom Panels, Multi-Panel Auth, and Table Query Tuning](https://www.msaied.com/public/articles/filament-at-scale-custom-panels-multi-panel-auth-and-table-query-tuning) [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)  

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

1. [Extending Laravel Without Forking It](#extending-laravel-without-forking-it)
2. [Macros: One-Off Extensions](#macros-one-off-extensions)
3. [Mixins: Grouping Related Macros](#mixins-grouping-related-macros)
4. [Custom Collection Classes: When Macros Aren't Enough](#custom-collection-classes-when-macros-arent-enough)
5. [Organising Registration: Avoid the God Provider](#organising-registration-avoid-the-god-provider)
6. [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)
