Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Overridable Model Laravel Package

genealabs/laravel-overridable-model

Override Eloquent model methods and properties at runtime without touching vendor code. Define per-model overrides in your app to customize package models, swap behavior, or inject your own logic—keeping third-party code untouched and upgrades painless.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. The package solves one narrow, well-defined problem: a Laravel package ships an Eloquent model (Acme\Blog\Models\Post) referenced by hard-coded FQCN across relationships, factories, policies, and static calls, and the consuming application needs its own subclass instantiated instead — without forking vendor code. The mechanism is idiomatic Laravel: a trait on the base model opts in, the override is registered via a config map or Base::overrideWith(Subclass::class), and the container resolves the correct class at instantiation. That fits Laravel's service-container conventions and keeps vendor packages pristine across upgrades, which is exactly the property a TPM cares about when scoping an extension seam for a plugin, SDK, or white-label surface. The cost is indirection in the model-instantiation path — the hottest path in any Eloquent application — so the architectural trade is "no vendor forks" against "runtime class resolution nobody can see in the source."

Integration feasibility. Low technical effort, high governance effort. Install is a single Composer require with auto-discovered provider, publishable config, and a first real use case (two extra columns, a cast, a belongsTo back to your User) that the source material estimates at an afternoon. Source surface is roughly 100 lines, MIT-licensed, so vendoring or reimplementing is genuinely practical. The hard constraint that gates feasibility: the vendor model must already use the OverridableModel trait. You cannot retrofit an override onto a third-party model that hasn't opted in. That moves the real feasibility question from "can we install this?" to "do the packages we depend on expose this seam, or do we control both sides?"

Technical risk. Dominated by lifecycle, not code quality. The repository is archived (last release 2024-05-11, Laravel 11 compatibility only, zero dependents, 93 stars), which means no security-patch channel, no Laravel 12 compatibility work, and no maintainer response. Laravel 12+ is explicitly unverified. The dominant runtime failure mode is silent fallback: if the override isn't registered — config not published, wrong key, provider not booted, model resolved before overrideWith() runs, closure lost under config:cache — you get the base model with no exception and subtly wrong application behaviour discovered by users. Secondary risks: Octane and long-lived workers persisting class substitution across requests; PHPStan/Larastan and IDE navigation going blind to runtime substitution; get_class($post) === Base::class comparisons breaking; TypeError from eager-loaded collections typed against the base class; vendor factories that new Post directly bypassing the override entirely.

Key questions. (1) How many vendor models do we actually need to substitute — is it 0, 1, or 8? (2) Does the vendor package already expose config('blog.models.post') or accept a model class in config, in which case this package is unnecessary? (3) Do we control the base-model side (internal SDK, our own packages) or are we dependent on a third party opting in? (4) Who is the named owner of the fork, the contract test, and the framework-upgrade matrix? (5) Is our actual need class substitution, or really data/behaviour customization that composition or a config-driven model string would satisfy more cheaply? (6) Are we on Laravel 12, and if so, will we pin a Composer conflict so upgrades fail loudly?

Integration Approach

Stack fit. Laravel 10/11 with PHP 8.x and standard Eloquent. Fit degrades sharply outside that envelope — Laravel 12 is unverified and the package will not be updated to close the gap. Works with config caching, Octane, policies, and factories only if each is explicitly verified; none of them are safe by default.

Migration path. The defensible path is reference design, not dependency. Two viable routes:

  • Adopt-as-dependency (only if the vendor model already uses the trait and we accept the archived risk): composer require genealabs/laravel-overridable-model, pin the exact version, add a Composer conflict on Laravel 12 so a framework bump fails loudly rather than resolving into broken behaviour.
  • Adopt-as-pattern (recommended): copy the small MIT-licensed trait into app/Support/ behind our own ModelOverrides registry facade. Every call site depends on our registry, not the package, which makes the eventual divergence from upstream a one-file change and removes the archived dependency from our supply chain while preserving the convention.

Either way, do not let twenty call sites reference the package directly, and do not mix the config-map and overrideWith() registration styles in one codebase — pick one and document it.

Compatibility. Three compatibility checks must be explicit, not assumed. Config caching: overrides must be registered as plain ::class strings in a service provider boot()/register(), never inside a config-file closure, and CI must run the contract test against php artisan config:cache. Static analysis: PHPStan/Larastan and IDE go-to-definition degrade under runtime substitution — fix with @method/@property docblocks or a PHPStan stub, not by disabling rules. Relationship graphs: a vendor Post::author() returning BelongsTo(User::class) points at their User; either override that model too or override the relationship method to point at App\Models\User. Half-overridden graphs produce instanceof surprises.

Sequencing. (1) Inventory actual substitution need — count the vendor models; if 0–1 and config-driven injection exists, stop here. (2) Confirm the base-model side is under our control or already opted in. (3) Choose dependency vs. pattern; default to pattern. (4) Build the ModelOverrides registry and register in a service provider. (5) Ship the contract test as a first-class artifact — assert every registered override resolves via new $base and $base::query()->make(), plus one "seam" test per model that goes through the vendor's public API (relationship, repository, service). (6) Run it under config:cache and on Octane request 1 and request 500. (7) Document the convention in onboarding and make IDE auto-import prefer the app namespace.

Operational Impact

Maintenance. This is the dominant cost and it does not amortize well. An archived dependency in the model-instantiation path means we own the fork, the Laravel-upgrade matrix, and any framework-major compatibility work indefinitely. Each override is a surface we own against vendor upgrades, because parent:: calls into vendor code that the archived package will not keep compatible for us. Overriding 8 models to change 2 is how this pattern becomes a maintenance tax — scope discipline is the control. Budget an owner plus a recurring per-framework-bump task, not a one-time integration ticket.

Support. No CVE channel, no maintainer response, no release cadence. For anything customer-facing or in the model-instantiation path, that is a hard blocker on the dependency route. It is acceptable on the vendored-pattern route precisely because we become the maintainer of ~100 lines we can read in one sitting. The safety net is a named person who owns the fork, the contract test, and the upgrade matrix; if nobody takes that, don't ship it in either form.

Scaling. Runtime class resolution is negligible in throughput terms; the scaling risk is temporal, not volumetric. Octane and long-lived workers persist class substitution and static state across requests, so verify the override resolves identically on request 1 and request 500 — this alone justifies a smoke test before shipping. Config caching is the other cliff: "worked locally" dies at config:cache if any registration lives in a closure or an env() call outside config.

Failure modes. Rank-ordered by likelihood and blast radius: (1) silent fallback to the base model — no exception, subtly wrong behaviour, discovered by users; mitigate with the contract test and a provider-boot assertion that every mapped base extends the expected parent and the subclass loads. (2) Config-cache loss of closure-based registration. (3) Vendor factories hard-coding new Post, so tests assert against the wrong class. (4) Policy registration mismatch when resolution doesn't actually happen — add a get_class() assertion to at least one policy test. (5) Exact-class comparisons and relationship type mismatches surfacing as TypeError under strict types. (6) Unannounced breakage on a Laravel major bump.

Ramp-up. Cheap to learn, expensive to learn correctly. The afternoon first use case is real. The recurring bug is a new engineer writing new \Acme\Blog\Models\Post or importing the base namespace, which bypasses the override with no error — so onboarding documentation, an IDE auto-import convention, and the contract test in CI are load-bearing, not nice-to-haves. A TPM should treat "document the convention" as a deliverable with an owner, and treat the contract test as the acceptance criterion for the whole integration rather than an engineering afterthought.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource