ardalanamini/laravel-custom-relation
A Laravel package that lets you define custom Eloquent relationships beyond the built-in hasMany, belongsTo, and morphTo types. Build relations with your own query logic and constraints, keeping complex model associations flexible and readable.
Architecture fit. ardalanamini/laravel-custom-relation occupies a narrow, high-leverage slot in the stack: it lets a Laravel model expose an application-specific association as a first-class Eloquent relation instead of repeating a query-scope chain in every caller. From a product perspective the value is not new capability — anything it does can be written by hand — it is deduplication and discoverability. That makes the business case conditional: it pays off only if the team can name three or more relations today that are currently hand-rolled, duplicated, or hidden behind inconsistently used scopes (tenant-scoped joins, composite-key links, "same author, last 30 days" style filters, subquery-backed associations). If standard hasMany/belongsTo/morph* cover the domain, this is indirection with no payer.
Integration feasibility. Low ceremony and cheaply reversible, which is the strongest reason to evaluate it. Composer install, Laravel auto-discovery, no migrations, no config publishing, no service provider implied by the release surface. Adoption is incremental per-model, and the rollback is composer remove plus reverting the handful of models that referenced the new relation methods. Frame this to stakeholders as a spike, not a commitment: the decision to trial it costs a fraction of the decision to standardise on it.
Technical risk. The dominant risk is maturity, not correctness of what exists. At v0.1.x the API is explicitly unstable and SemVer gives no protection against breaking changes in a minor bump. Compounding factors visible from the release record: low adoption (double-digit stars), a single-maintainer project whose latest release was contributed by a first-time contributor and touched only composer.json, and a hard dependency on Eloquent internals (Relation, HasOneOrMany, existence/constraint hooks) that Laravel has historically reshaped across majors. Note also that v0.1.4 → v0.1.5 is metadata-only — a composer.json change with no source, feature, or documented behaviour delta. Treat the 0.44 score as the baseline for the 0.1.x line, not as a signal about this specific release.
Key questions to close before any code is written (owner, answer, due date in the spike ticket):
with() (including constrained eager loading), has()/whereHas(), and withCount()? Partial support here is the difference between a working feature and a silent N+1 or a wrong aggregate in production.composer.json in v0.1.5 — widened, narrowed, autoload, or metadata only? This is the only mechanism by which this otherwise-inert release can break an existing install.Stack fit. Laravel-only, PHP 8.1+/Laravel 9+ as the working assumption until the spike confirms the resolved constraint range. There is no meaning outside Laravel; do not let a "maybe we'll port it" argument into the decision. The realistic fit is a Laravel service where the ORM is already the primary data-access layer and the team is fluent in Eloquent — not a service mid-migration to another data layer.
Migration path. Purely additive: no schema, data, or existing-relation changes. Sequence the pilot deliberately:
whereHas clauses. Reject anything used in exactly one place.main on day one.with() on a realistic page.Compatibility. Pin exactly — "ardalanamini/laravel-custom-relation": "0.1.5" — not a caret range. On a 0.x package a caret range permits breaking minors straight into CI. Commit the lockfile and treat every future release as a change requiring a changelog read; the fact that a metadata-only release exists in the history is evidence that release titles are not a reliable summary.
Sequencing. Run the evaluation before the next Laravel major upgrade, not during it. If the relation classes bind to internal Eloquent signatures, you want the answer to "does this package keep pace?" while the upgrade is still hypothetical. Concretely: spike in a low-traffic service, carry it through one framework upgrade on a branch, then decide on broader standardisation. Decide the exit criteria up front — supported-matrix confirmation, passing parity tests, and a named internal owner — so the spike cannot drift into de-facto adoption without a decision.
Maintenance. Today this is the lowest-effort tier: no background jobs, no runtime services, no schema, no config to drift, and a small surface (a trait plus a relation class). The cost is deferred rather than absent. A 0.1.x dependency with one active maintainer means you must budget for the possibility of vendoring or forking it. Because the used surface should stay small, the honest framing to stakeholders is: "we could reimplement what we use in days, not weeks" — and if that statement stops being true, adoption has gone too far.
Support. Community-only, no vendor SLA. Assign a named engineer as owner in the same ticket that approves the install; without that, the package becomes orphaned knowledge the moment the original advocate changes teams. That engineer needs comfort reading Eloquent's Relation internals, not just using them — a meaningful scoping constraint on who can own this.
Scaling. The abstraction is query-shape neutral; it compiles to SQL the same way hand-written scopes do, so there is no inherent throughput cost. The scaling risk is behavioural. A custom relation that does not fully participate in eager loading converts a single-query page into an N+1 under load, and the symptom is latency under traffic, not an exception in staging. Mitigation is procedural: instrument query counts per request on every endpoint that adopts a custom relation, and gate adoption on that metric.
Failure modes, ranked by expected cost:
composer.json-only release narrowing a PHP or Laravel range and breaking composer update in CI. Cheap to detect with an exact pin and a committed lockfile; expensive without.Ramp-up. Consuming existing custom relations is roughly an hour for a Laravel-fluent engineer. Authoring them is a different skill set — it requires understanding how Relation composes constraints, how getResults() and addConstraints() interact, and how eager-load keys are matched — so budget one to two days of focused reading plus a paired spike before a mid-level engineer owns a production relation. Require one artefact per custom relation: the SQL it is expected to produce, checked into the repo beside it. That document carries most of the onboarding and debugging load and is what makes the reimplement-in-house fallback credible.
How can I help you explore Laravel packages today?