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

Doctrine Specification Laravel Package

happyr/doctrine-specification

Happyr Doctrine Specification brings the Specification pattern to Doctrine: compose small, reusable, testable specification classes into query expressions instead of bloated repositories with long QueryBuilder calls and duplicated logic.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

This is a small, single-purpose library that applies the Specification pattern to Doctrine ORM query building. It targets a real and well-understood pain point: repositories that accumulate dozens of parameterized findX() methods with duplicated QueryBuilder logic that cannot be composed. The library converts query predicates into composable value objects (Spec::andX, Spec::orX, Spec::eq, Spec::join, Spec::isNull, and so on), which can then be applied through a repository match() call or evaluated in-memory via isSatisfiedBy() and filterCollection().

Architecturally, this fits best in a layered PHP application where read paths are non-trivial and business rules are reused across use cases (e.g. "adverts we should close" AND "owned by company"). The in-memory satisfiability API is the strategically interesting part for a TPM: it lets the same rule be enforced at query time in SQL and validated against an entity in a unit test, removing the database from part of the test loop. That is a testability and defect-prevention argument, not just a code-cleanliness one.

The trade-off is that it is an abstraction over the QueryBuilder, not a query language. Any specification you cannot express through the provided combinators either escapes the abstraction or forces custom Specification classes. It also deliberately hides ORM implementation details, which is an asset for teams that keep producing inconsistent DQL, and a liability for teams with genuinely complex fetching (multi-root joins, WITH clauses, native SQL, window functions).

Integration feasibility

Integration is cheap and reversible. It is a Composer package with no service container requirements, no configuration files, no database schema changes, and no runtime infrastructure. Adoption is essentially: require the package, make the target repository extend the provided base repository or add a match() method, and write specs. A single read path can be converted in an afternoon and evaluated in a branch.

The main gating factors are version alignment, not effort. The library's 2.x line required Doctrine ORM 2.5+ and PHP 7.2+ at the time of the 2.0 release; 2.2.0 added Doctrine ORM 3 support while explicitly restoring ORM 2 compatibility. This is a positive signal — the maintainers responded to the ecosystem's most significant breaking upgrade — but it is also where most integration risk lives, because a project pinned to an older Doctrine or an older PHP will have to reconcile constraints before the first commit.

Technical risk

  • Adoption signal is weak. 449 GitHub stars but a reported zero dependents means very little observable downstream usage. That is a legitimate concern for a TPM: it implies few external teams have exercised this against unusual query shapes, and the ecosystem has not stress-tested it on our behalf.
  • Bus factor. This is a consultancy-backed open-source project with a small maintainer set (predominantly one contributor across the 2.x changelog). No commercial support tier. If the maintainer disappears, we inherit a small, readable, MIT-licensed codebase — a tolerable outcome, but it must be a conscious one.
  • Major-version churn. 2.0.0 was a large breaking release (visibility changes, removed classes such as EntitySpecificationRepository and Having, abstracted LogicX/Comparison, moved classes into a Repository namespace). Teams that adopted 1.x paid a real migration tax. Assume a 3.x will arrive and budget accordingly.
  • Abstraction escape hatches. The provided combinators cover the common 80–90%; the tail requires custom classes. There is a risk of the team re-implementing a query DSL poorly rather than writing plain DQL when that is the right answer.
  • Repository inheritance coupling. If the integration path is extending a base repository class, that consumes your single inheritance slot and can conflict with existing base repositories in a larger codebase.

Key questions

  1. Which Doctrine ORM and PHP versions does the current release actually declare, and does that matrix include both our target and our next-upgrade target?
  2. Does our codebase's repository structure allow extending the provided base repository, or do we need the composition/decorator route?
  3. Who owns the decision to add a dependency with no observable dependents, and what is the exit plan if upstream stalls?
  4. Is the team's dominant query complexity within the combinator set, or does it rely on native SQL, multi-entity FROM, or vendor-specific DQL that this cannot express?
  5. Is there an existing pagination/filtering stack (Pagerfanta, API Platform, custom filters) that would need to compose with specifications, and does that composition work?
  6. Is any security-relevant filtering (row-level tenancy, authorization scopes) a candidate? If so, the in-memory isSatisfiedBy() path becomes an enforcement point and must be covered by tests.

Integration Approach

Stack fit

This is a native fit for any Laravel application using Doctrine ORM directly — typically via LaravelDoctrine/orm or a Doctrine bundle equivalent. It is not relevant to Laravel's default Eloquent stack. A TPM must establish this early, because "Laravel/PHP package" in a catalogue entry can mislead a stakeholder into assuming Eloquent applicability. If the product uses Eloquent with a Doctrine island for reporting or a legacy module, the scope of adoption is that island only, which materially reduces the business case.

Where it fits: read-heavy domains with repeatable filter combinations — catalog/search, eligibility and entitlement checks, moderation queues, saved-segment reporting. Where it does not: simple CRUD, single-use queries, or teams already standardized on API Platform filters or a CQRS/read-model layer that owns query construction.

Migration path

Adopt incrementally, never as a rewrites. Recommended path:

  1. Add the package; make no behavioural change.
  2. Write one specification for a rule that already exists in two places (the duplication is the justification).
  3. Convert one caller to match($spec) and one test to isSatisfiedBy(); confirm results are identical against a seeded fixture.
  4. Only then promote the pattern into the code review checklist for new queries.

Existing repository methods should be left untouched until a spec naturally displaces them. A "convert all repositories" mandate is the failure mode here: high effort, no user-visible benefit, and a large diff for reviewers.

Compatibility

  • Verify the Doctrine ORM constraint against the application's lockfile before planning anything.
  • Confirm the ORM 2 vs ORM 3 branch of the library matches whether the app is mid-upgrade — 2.2.0 supports both, which is convenient, but the code paths are not identical.
  • Check for naming collisions with existing custom DQL functions (Count, Concat, Trim declarations were added or corrected in 2.x); duplicate registration is a runtime failure, not a compile-time one.
  • Confirm the base repository can coexist with existing base classes, or use the composition form of the integration.

Sequencing

Doctrine ORM upgrade (if pending) → package install and spike → one production read path → test-suite adoption of isSatisfiedBy() → coding standard / ADR documenting when to write a spec versus plain DQL → broader retrofit. The ADR step is the one most often skipped; without it, some engineers embrace the abstraction for everything and others ignore it, and the repository ends up with two idioms.

Operational Impact

Maintenance

Ongoing maintenance is genuinely light: no services, no schema, no caches, no daemons. Cost concentrates in two places — keeping the Composer constraint current across Doctrine and PHP upgrades, and reviewing new spec classes for correct alias and parameter handling. Budget a small recurring line item for dependency-upgrade work and a named owner for the package relationship.

Support

Community-only. GitHub issues, MIT licence, best-effort maintainer response. There is no vendor to escalate a production DQL failure to. Mitigation: pin to a known-good minor, keep the package at a leaf of the dependency graph so it can be replaced, and require a fixture-backed test for every specification that encodes a business rule. The specs themselves are unit-testable without a database, which is the cheapest form of support insurance available here.

Scaling

The library adds no runtime overhead beyond object construction and QueryBuilder assembly, so database performance is unchanged — good news, and worth stating plainly to anyone who hears "abstraction layer" and assumes a tax. Scaling concerns are therefore application-level: specifications must still produce index-friendly predicates, must not encourage N+1 through naive joins, and must not be used to build deeply nested andX/orX trees that generate unreadable, unindexable SQL. Review specs the way you review SQL.

Operational scale of the codebase is the real target: this exists to stop repository classes becoming 20-function, many-parameter dumping grounds. The measurable benefit is fewer duplicated filter implementations and fewer bugs where two "active user" definitions drift apart.

Failure modes

  • Runtime DQL errors. Aliases and parameters are strings; mistakes surface as exceptions at query time, not at build time. Mitigate with integration tests per spec.
  • Silent semantic drift. If an in-memory isSatisfiedBy() implementation and the SQL path diverge, authorization or eligibility checks pass in one and fail in the other. This is the highest-severity risk in the package and deserves explicit test pairing.
  • Abstraction overreach. Custom specs accumulate into a home-grown query DSL that is worse than DQL and unknown to new hires.
  • Upstream abandonment. Low likelihood of breaking the build, but blocks future Doctrine major upgrades over time.
  • Naming collisions. Duplicate DQL function registration after a minor upgrade.

Ramp-up

For a PHP/Doctrine developer, the mental model is a few hours: the README and the two docs pages (0-usage, 1-creatingSpecs) are sufficient to write first specs. Composition (andX/orX over new Spec(...)) is the only concept needing reinforcement. Realistically plan one day for a mid-level developer to be productive and one week before they write specs others should imitate. Senior reviewers need a documented convention for "spec vs. plain DQL" or the codebase will fork into two idioms within a quarter.

Overall: low-cost, low-infrastructure, moderate-value tool whose justification rests on duplicated business rules and testability rather than performance. Approve as a scoped experiment on one or two read paths, with a decision review after the first production spec ships.

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.
roundly-consulting/kubernetes-api-for-laravel
roundly-consulting/plausible-for-laravel
gvp/gvp-laravel-template
metasyncsite/laravel-metasync-client
roundly-consulting/onboarding-for-laravel
andydefer/laravel-notification
adachsoft/cli-process-tester
jhaskell13/laraflow
sebastienheyd/boilerplate-media-manager
ccast/tagixo-primix
pretendtrue/laravel-builder
sawmainek/laravel-scout-tntsearch-driver
tomshaw/laravel-calendar-table
dibakar/laravel-ownership
steadrun/laravel-monitor
labyrinthe/connect-laravel
elmogy/layan
drewdan/sentinel-client
dskripchenko/laravel-llm
fatryst/nuonuo