Overview
In v1, a Lunar model could be replaced entirely with a consumer’s subclass, registered throughModelManifest. v2 removes this. Lunar’s models keep a single concrete class identity throughout the framework: Lunar\Core\Models\Product is always Lunar\Core\Models\Product, never a consumer’s subclass standing in for it.
Every recipe on this page is exercised against real Lunar models in the package’s own test suite, so each is guaranteed to work against the current release.
Add a relationship
Use Laravel’s dynamic relationships from a service provider’sboot method. This works on any Lunar model without needing to replace it.
Add a method
Use a model macro, registered from a service provider’sboot method.
Add a column
Add the column with a migration; nothing else is needed. Lunar’s models are unguarded, so a new column is immediately a readable and writable attribute.Cast an added column
Register the cast from a service provider’sboot method with addCasts(), backed by Lunar\Core\Models\Concerns\HasExtendableCasts, a trait every Lunar model uses. It accepts the same values as a model’s own casts() method, including a custom cast class — the way to attach an accessor/mutator pair to an added column.
addCasts() merges into whatever casts the model already declares, so it never has to be aware of Lunar’s own casts. It only makes sense for columns the consumer has added; Lunar’s own columns are already cast correctly.
Constrain every query
Use a global scope, registered from a service provider’sboot method.
Add an optional query scope
Global scopes are always on. For a scope that should only apply when called — the equivalent of a local scope method, but registered from outside the model class — useaddLocalScope(), a method every Lunar model gets from Models\Base.
Product is callable only on Product queries; calling it on Order::query() throws BadMethodCallException, the same as a mistyped native scope. If a registered name collides with one of Lunar’s own local scopes, macros, or query builder methods, the native one always wins — a registered scope can never shadow a built-in.
This works even for
Lunar\Core\Models\Collection, which uses its own nested-set query builder rather than Lunar’s default Lunar\Core\Models\Builders\Builder. Both compose the same underlying resolution logic, so Collection::addLocalScope() behaves identically to any other model.Type safety for registered scopes
A registered scope is resolved at runtime, so there’s nothing static for an IDE or PHPStan to read — the same limitation that already applies to macros and dynamic relationships. Declare the signature once in a stub file the application owns and excludes from autoload:Product class, so Product::featured()->priorityOver(5)->get() completes and type-checks. For PhpStorm, barryvdh/laravel-ide-helper writes a similar guarded, never-executed redeclaration into _ide_helper_models.php; since its generator cannot see runtime-registered closures, add the @method lines to that file’s Product block by hand. For PHPStan or Psalm, point stubFiles at a stub declaring the same lines.
React to lifecycle events
Use a model observer or an event listener, exactly as with any other Eloquent model.Override behavior
Model verb methods —$order->cancel(), $cart->createOrder() — are thin delegations to an action contract resolved from the container. To change what happens, bind a different implementation of the contract in a service provider; the verb picks it up automatically. See Extending Orders and Extending Carts for the contracts available on each model.
Serialization
For array or JSON output, useappend() or makeVisible() per instance, or wrap the model in an API Resource in the consuming application’s own layer.
What replaced model class substitution
The
Lunar\Core\Models\Contracts\* interfaces that v1 used to resolve a model to its (possibly substituted) class no longer exist, since there is nothing left to resolve — a Lunar model is always the Lunar class. Route model binding, relationship loading, and the morph map all resolve directly to Lunar\Core\Models\* classes.