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, share logic across classes with mixins, and build custom Collection methods that survive Octane restarts and team code reviews.

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

ShareCopy linkCopied

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

  On this page +1. [Laravel Macros, Mixins, and Custom Collection Methods That Actually Ship](#laravel-macros-mixins-and-custom-collection-methods-that-actually-ship)
2. [How Macroable Works Under the Hood](#how-codemacroablecode-works-under-the-hood)
3. [Registering Macros in a Service Provider](#registering-macros-in-a-service-provider)
4. [Mixins: Sharing Many Methods at Once](#mixins-sharing-many-methods-at-once)
5. [Octane Safety: Static State Is Shared Across Requests](#octane-safety-static-state-is-shared-across-requests)
6. [IDE Support with @mixin and Laravel IDE Helper](#ide-support-with-code-at-mixincode-and-laravel-ide-helper)
7. [Extending Beyond Collection](#extending-beyond-collection)
8. [Takeaways](#takeaways)

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

The `Macroable` trait is one of Laravel's quieter superpowers. Used well, it lets you extend core classes without forking the framework. Used carelessly, it produces invisible globals that confuse teammates and break under Octane. This article covers the practical patterns that survive production.

---

### How `Macroable` Works Under the Hood

Any class that uses `Illuminate\Support\Traits\Macroable` stores closures in a static `$macros` array. When you call an unknown method, `__call` (or `__callStatic`) checks that array and invokes the closure, binding `$this` to the current instance.

```php
// Simplified internals
public static function macro(string $name, callable $macro): void
{
    static::$macros[$name] = $macro;
}

public function __call(string $method, array $parameters)
{
    if (isset(static::$macros[$method])) {
        return Closure::bind(static::$macros[$method], $this, static::class)(...$parameters);
    }
    throw new BadMethodCallException(...);
}

```

Because `$macros` is **static**, macros registered in a service provider persist for the lifetime of the PHP process — which is exactly what you want in a traditional FPM setup, and exactly what you must reason about under Octane.

---

### Registering Macros in a Service Provider

Always register in `boot()`, never in `register()`. The framework classes you're extending may not be bound yet during `register()`.

```php
namespace App\Providers;

use Illuminate\Support\Collection;
use Illuminate\Support\ServiceProvider;

class CollectionMacroServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Collection::macro('filterMap', function (callable $callback): Collection {
            /** @var Collection $this */
            return $this->map($callback)->filter()->values();
        });

        Collection::macro('groupByFirst', function (string $key): Collection {
            /** @var Collection $this */
            return $this->keyBy(fn ($item) => data_get($item, $key));
        });
    }
}

```

Register the provider in `bootstrap/providers.php` (Laravel 11+) or `config/app.php`.

---

### Mixins: Sharing Many Methods at Once

When you have a family of related methods, a **mixin class** is cleaner than a long list of `macro()` calls. Each public method on the mixin class must return a `Closure`.

```php
namespace App\Support\Mixins;

use Illuminate\Support\Collection;

class CollectionMixin
{
    public function toAssoc(): \Closure
    {
        return function (string $keyField, string $valueField): Collection {
            /** @var Collection $this */
            return $this->mapWithKeys(
                fn ($item) => [data_get($item, $keyField) => data_get($item, $valueField)]
            );
        };
    }

    public function chunkWhile(): \Closure
    {
        // Laravel already has chunkWhile; this is illustrative
        return function (callable $callback): Collection {
            /** @var Collection $this */
            $chunks = [];
            $chunk = [];
            foreach ($this->items as $item) {
                if (empty($chunk) || $callback($item, end($chunk))) {
                    $chunk[] = $item;
                } else {
                    $chunks[] = $chunk;
                    $chunk = [$item];
                }
            }
            if ($chunk) {
                $chunks[] = $chunk;
            }
            return new static($chunks);
        };
    }
}

```

Register the mixin in one call:

```php
Collection::mixin(new CollectionMixin());

```

---

### Octane Safety: Static State Is Shared Across Requests

Because macros live in static arrays, they are registered **once** when the worker boots and remain for every subsequent request. This is fine — registration is idempotent. The danger is registering macros **inside a request cycle** (e.g., in a controller or middleware), which can cause subtle state leakage if the closure captures request-scoped objects.

**Rule:** register macros only in service providers, never inside request-handling code.

---

### IDE Support with `@mixin` and Laravel IDE Helper

Without hints, PhpStorm treats macro calls as errors. Two approaches:

1. Add a `/** @mixin CollectionMixin */` docblock to a stub file that `ide-helper` picks up.
2. Use `barryvdh/laravel-ide-helper` with `php artisan ide-helper:generate` — it reads registered macros and writes stubs automatically.

---

### Extending Beyond Collection

The same pattern works on `Request`, `Builder` (query builder), `Carbon`, and `Response`:

```php
use Illuminate\Http\Request;

Request::macro('isHtmx', function (): bool {
    /** @var Request $this */
    return $this->hasHeader('HX-Request');
});

// Usage in a controller:
if ($request->isHtmx()) {
    return view('partials.table');
}

```

Extending `Illuminate\Database\Query\Builder` follows the same pattern but be careful: the query builder is instantiated per query, so closures must not assume singleton state.

---

### Takeaways

- Register macros in `boot()` inside a dedicated service provider — never inside request handlers.
- Use **mixins** when you have more than two or three related methods; it keeps the provider clean.
- Closures in macros bind `$this` to the host instance, so you get full access to internal properties.
- Under Octane, static macro registration is safe because it happens once at worker boot.
- Add IDE stubs via `ide-helper` or `@mixin` docblocks so teammates get autocomplete.
- Extending `Request` with domain-specific helpers (e.g., `isHtmx()`, `tenantId()`) is one of the highest-value uses of macros in a real application.

- [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 
---------------------------

  Can I override an existing Collection method with a macro?No. `\_\_call` is only invoked for methods that do not exist natively on the class. If a method is already defined, the macro is silently ignored. Use a subclass or a decorator if you need to override native behaviour.

   Is it safe to type-hint macro return values in strict PHP 8.3 codebases?Macros are resolved at runtime, so PHP's static analyser cannot infer their return types. Annotate the closure's return type explicitly and add a `@method` docblock to a stub class so PHPStan or Psalm can follow the type through call sites.

   Should every team utility live as a macro, or is there a better place?Prefer macros for methods that genuinely feel native to the extended class (e.g., a Collection helper that reads like a built-in). For domain logic, a dedicated service or action class is clearer and easier to test in isolation.

   ![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 articleLivewire v3 Lazy Components, Islands, and Deferred Loading in Practice](https://www.msaied.com/public/articles/livewire-v3-lazy-components-islands-and-deferred-loading-in-practice) [Next articleLaravel Horizon Deep Dive: Queue Tuning, Supervisor Strategies, and Job Reliability](https://www.msaied.com/public/articles/laravel-horizon-deep-dive-queue-tuning-supervisor-strategies-and-job-reliability)  

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

1. [Laravel Macros, Mixins, and Custom Collection Methods That Actually Ship](#laravel-macros-mixins-and-custom-collection-methods-that-actually-ship)
2. [How Macroable Works Under the Hood](#how-codemacroablecode-works-under-the-hood)
3. [Registering Macros in a Service Provider](#registering-macros-in-a-service-provider)
4. [Mixins: Sharing Many Methods at Once](#mixins-sharing-many-methods-at-once)
5. [Octane Safety: Static State Is Shared Across Requests](#octane-safety-static-state-is-shared-across-requests)
6. [IDE Support with @mixin and Laravel IDE Helper](#ide-support-with-code-at-mixincode-and-laravel-ide-helper)
7. [Extending Beyond Collection](#extending-beyond-collection)
8. [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/743/8998fac3a41451ab3fe1588194e17a43.png) Filament · 3 min read### Securing Filament Plugins with Plumb: Automated Security Scoring for PHP Packages

5 Oct 2026 ](https://www.msaied.com/public/articles/securing-filament-plugins-with-plumb-automated-security-scoring-for-php-packages) [ ![](https://cdn.msaied.com/742/2d02018669cdeedccb5de2efb898f0ee.png) Filament · 3 min read### Filament v3.3.56 Released: File Hash Names and Livewire Upload Fix

5 Oct 2026 ](https://www.msaied.com/public/articles/filament-v3356-released-file-hash-names-and-livewire-upload-fix) [ ![](https://cdn.msaied.com/741/5b55c123ad08e4d34e1f4b99ad6a428b.png)  · 3 min read### Filament v4 Schema-Based Forms, Infolists, and the Unified Schema API

5 Oct 2026 ](https://www.msaied.com/public/articles/filament-v4-schema-based-forms-infolists-and-the-unified-schema-api-5) 

  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)
