stinger-soft/doctrine-commons
A shared Doctrine commons library for PHP projects, providing reusable persistence utilities and foundational components that help standardize entity, repository, and database setup across Stinger Soft applications.
Architecture fit
stinger-soft/doctrine-commons is not a Laravel package in any meaningful sense — it is a Doctrine ORM companion library. Laravel's default persistence layer is Eloquent, so the addressable surface is limited to projects that have already adopted Doctrine via laravel-doctrine/orm (or use Doctrine standalone inside a Laravel service). That is a small, opinionated subset of the ecosystem, and this package assumes the team has already made that architectural bet.
The package bundles two unrelated concerns under one name: (a) general-purpose PHP helper functions and (b) DQL custom function nodes registered into the Doctrine parser. Bundling "commons" utilities with query-layer extensions is a coupling smell — the DQL extensions are the only part with genuine switching cost, while the helpers are trivially replaceable with Illuminate\Support\Str/Arr or symfony/string. A TPM should treat these as two separate adoption decisions.
The real value of DQL extensions is portability: functions like FIELD(), FIND_IN_SET(), GROUP_CONCAT variants, IFNULL, and date-truncation helpers let teams keep query logic in DQL rather than dropping to raw SQL. Where a codebase has scattered native-SQL fragments that exist only because DQL lacks a function, this package has a defensible role. Where it does not, the package adds a registration dependency for no query-layer benefit.
Integration feasibility
Mechanically straightforward but version-sensitive. Doctrine DQL custom functions implement Doctrine\ORM\Query\AST\Functions\FunctionNode and are registered through the ORM configuration's customStringFunctions / customNumericFunctions / customDatetimeFunctions maps. Under Laravel Doctrine this maps cleanly into config/doctrine.php; standalone Doctrine maps into the Setup/Configuration object. Registration is a config change, not a code change — low blast radius and easy to reverse.
The friction is compatibility, not wiring. Custom function nodes subclass parser internals (Parser, Lexer, SqlWalker) that shifted between ORM 2.x and 3.x, and DBAL 3→4 removed several platform APIs the SQL generation likely depends on. With zero stars, zero dependents, and no visible release cadence, there is no evidence of a tested compatibility matrix — expect to verify against your exact ORM/DBAL/PHP triple manually.
Technical risk
composer audit coverage and a pinned version. No evidence of a signed release or CI.FIELD, FIND_IN_SET) will fail or silently misbehave on PostgreSQL/SQLite. If the estate is multi-engine, this narrows applicability further.Key questions
composer why-not cleanly constrain them?Stack fit
Adopt only in Doctrine-backed Laravel services. In Eloquent-first applications, the correct answer is no: use Illuminate\Database\Query\Expression, scopes, or a small custom grammar extension instead. The helper-function half should be rejected outright in favour of first-party framework utilities to avoid duplicating Str/Arr/Carbon.
The DQL half fits cleanly where the codebase already registers custom DQL functions and where a handful of native-SQL escape hatches could be retired. Registration is additive and namespaced by function name, so collision risk with existing custom functions is checkable by inspection.
Migration path
Recommended path: absorb, don't depend. Clone the repository, audit each FunctionNode and helper, retain what we use, and re-publish as an internal package or a app/Doctrine/DQL namespace inside the consuming service. This trades a trivial amount of copying for full control over ORM upgrades — a favourable trade for a ~0-star, 0-dependent dependency.
If timeline pressure favours a dependency: pin to an exact version, add it to the dependency-review allowlist, and schedule a re-audit at the next ORM major.
Migration from raw SQL to the new DQL functions should be incremental and query-by-query, each one covered by an existing feature test.
Compatibility
Validate before committing: PHP version, doctrine/orm 2.x vs 3.x, doctrine/dbal 3 vs 4, and database engine. Build a compatibility matrix table in the ADR. Confirm custom function registration key names against the installed Laravel Doctrine version, since the config schema differs between major releases.
Sequencing
getSql() implementations for platform assumptions and NULL semantics.config/doctrine.php in a non-production environment; run the full suite.If step 1 fails to produce two concrete wins, stop — the package is not justified.
Maintenance
Effectively zero upstream maintenance; the burden transfers entirely to us. Budget an owner and a recurring review at each ORM/DBAL major upgrade. Because function nodes touch parser internals, upgrades are the highest-cost events — expect a small but non-zero effort each time Doctrine ships a major. Internalising the code makes this effort tractable; depending on it makes the effort contingent on a vendor who has shown no cadence.
Support
No commercial support, no issue triage, no security response channel. Internal escalation must terminate at our own on-call/DBA group. Document this explicitly so future maintainers do not assume upstream assistance. Documentation is a README; assume we author our own usage notes as part of the fork.
Scaling
The DQL functions themselves are query-time SQL generation — negligible overhead. The real scaling concern is behavioural: functions wrapping non-sargable predicates (FIND_IN_SET, FIELD) prevent index use and degrade under table growth. Enforce a review rule that these appear only on small or already-filtered sets, and instrument slow-query logging around any migrated query.
Failure modes
Ramp-up
Low. Registration is a config edit and the function-node pattern is well documented in Doctrine's own codebase. A mid-level PHP engineer unfamiliar with Doctrine's AST will need roughly a day to read the parser internals and understand how a FunctionNode produces SQL; an engineer already fluent in Doctrine needs under an hour. The larger cost is conceptual — teams must understand why a query was moved into DQL and what portability guarantee it buys, otherwise these functions become unexplained magic that the next maintainer rips out. Pair the adoption with a short ADR recording the specific migrated queries and the rejected alternatives.
Recommendation: Conditional reject as a Composer dependency; conditional approve as a vendored source of a small number of DQL function nodes, gated on confirmed Doctrine usage and at least two concrete raw-SQL migrations. In an Eloquent-first Laravel estate, decline outright.
How can I help you explore Laravel packages today?