$ lexprog.com

// notes from an old coder -- php, databases, and the occasional rant

[January 03, 2025] Eloquent ORM

Eloquent Macroable: Extending Models

Eloquent Macroable: Extending Models

────────────────────────────────────────────────────────

Eloquent Macroable: Extending Models

Tip: Collection Macros

Collection::macro('toUpper', function () {
    return $this->map(fn($v) => strtoupper($v));
});

collect(['a', 'b'])->toUpper(); // ['A', 'B']

Gotcha: Macros Are Global

Once registered, a macro is available everywhere. Use namespacing in macro names to avoid collisions.

Tip: Model Macros

Eloquent models are macroable too:

Post::macro('recent', function () {
    return $this->where('created_at', '>', now()->subDays(7));
});

Gotcha: Macros Don't Have Access to $this Correctly

In macros, $this refers to the collection/model instance. But type hints won't work.

Tip: Mixin for Multiple Macros

Collection::mixin(new CollectionMacros());

Group related macros in a class.

Gotcha: Macro Registration Timing

Register macros in a service provider's boot() method, not register().

Tip: Use cursor() for Memory-Neutral Iteration

When exporting 100K rows, get() loads everything into memory. cursor() uses yield and keeps memory flat regardless of row count. Perfect for artisan commands.

Tip: whereHas() vs load() — Two Different Things

whereHas() filters the parent query by relationship existence. load() eager-loads relationships AFTER the query. Mixing them up is a common source of logic bugs.

Gotcha: withCount() Adds a Subquery

withCount('comments') runs a correlated subquery on every row. On large tables, this can be slower than a separate query. Profile before relying on it.

Senior Insight

The Macroable trait is one of Laravel's most elegant extensibility mechanisms. I've used it to add toCsv() to collections, sendSlackNotification() to the Notification facade, and custom whereLike() to the query builder. The discipline I follow: macro only when the extension is genuinely framework-level. If the functionality is application-specific, it belongs in a dedicated service class, not a macro.

Source: Laravel Docs (https://laravel.com/docs/eloquent), Laravel News (https://laravel-news.com/), Freek.dev (https://freek.dev/tags/eloquent)

────────────────────────────────────────────────────────
<-- back to posts