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 Json Odm Laravel Package

dunglas/doctrine-json-odm

Store PHP objects as JSON documents in PostgreSQL JSONB or MySQL JSON columns alongside traditional Doctrine ORM mappings, enabling schema-less, NoSQL-like hybrid data models with fast querying and indexing.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

  • This is a Doctrine ORM/DBAL extension, not a Laravel package. Its entire value proposition rests on registering two DBAL column types (json_document, and jsonb_document on DBAL ≥ 4.3) plus a Symfony Serializer-based (de)serializer. Laravel's default persistence layer is Eloquent, which has no DBAL type system and no Unit of Work — so the package cannot be "installed and used" in a normal Laravel app.
  • Fit is only credible if the target service either (a) already runs Doctrine via laravel-doctrine/orm, or (b) is a bounded module where standing up a second EntityManager is acceptable.
  • The conceptual model it enables — relational columns alongside schema-less JSON documents with rich PHP object graphs — is genuinely useful for audit/event payloads, integration envelopes, CMS-style flexible blocks, feature/config documents, and third-party webhook bodies. That is the real opportunity, and it is real.
  • The Symfony Serializer dependency is portable (installable standalone in Laravel), but it introduces a second serialization stack alongside Laravel's JsonResource/cast pipeline. Two serialization contracts for the same data is a long-term source of drift.

Integration feasibility

  • Technically feasible: Composer install, AppServiceProvider::boot() registers types behind a Type::hasType() guard, an EntityManager is bound, and mappings use attributes on Doctrine entities.
  • Non-trivial: Laravel gives you nothing for Doctrine metadata, DQL, schema tooling, or transaction integration with DB::transaction. Every one of those becomes bespoke wiring or a bridge package's opinion. laravel-doctrine/orm is community-maintained and historically lags Doctrine majors — verify DBAL/ORM version support (specifically DBAL ≥ 4.3 for jsonb_document) before committing.
  • Effort profile: a spike is ~1–2 weeks to prove type registration + hydration round-trip + query path. Production hardening (schema ownership, migration strategy, observability) is materially more.

Technical risk

  • Highest risk — silent non-persistence. The README explicitly documents that Doctrine compares objects by reference, so nested mutations inside the JSON column produce no UPDATE. This is a data-loss class of bug that is invisible to code review and surfaces in support tickets. Requires a project-wide convention (clone-before-mutate) enforced by review, not by the library.
  • Schema-version coupling. For documents without type aliases, the #type field stores the FQCN; renaming or moving a class requires a data migration (the README provides raw JSON_REPLACE recipes). Any refactor touching POPO namespaces becomes a database migration. type_map aliases mitigate this but must be adopted before data volume grows and are a hard-to-retrofit discipline.
  • Global mutable state. DBAL type registration is process-global and boot-order sensitive. Under Octane/long-running workers this must be idempotent; misconfiguration fails at hydration time, not boot time.
  • Maintenance concentration. Small maintainer group (Dunglas / Les-Tilleuls), MIT, no SLA. Recent releases track Symfony 8 and DBAL 4.3 reasonably well, but a Laravel-first team would be adopting a package whose release cadence is driven by the Symfony/API Platform ecosystem, not theirs.
  • Adoption signal is thin relative to the 630 stars — zero dependents in the supplied metadata suggests limited production hardening outside the API Platform orbit. Score 33.6 with opportunity 48.6 reads as "real capability, weak ecosystem pull."
  • Query ergonomics. All JSON querying is native SQL or DQL functions (scienta/doctrine-json-functions) — no Eloquent query builder, no whereJsonContains ergonomics. This shifts query work from typical Laravel developers to whoever knows DQL and Postgres JSON operators.

Key questions

  1. Does this codebase already run Doctrine anywhere, or would we be introducing a second persistence stack into a Laravel-first application?
  2. If we only need "structured PHP object in a JSON column," why not a custom Eloquent cast over the same Symfony Serializer — same storage, no DBAL/EntityManager surface?
  3. Which DB engine is authoritative — Postgres (JSONB + GIN, supported fully) or MySQL (JSON only, indexing via generated columns)? This changes both capability and cost.
  4. Who owns the document schema contract: type_map alias policy, serialization context, and the migration runbook for class renames?
  5. What is the maximum realistic document size and mutation frequency? Whole-column rewrite on every change is the scaling cliff.
  6. Are write paths through this column auditable and recoverable if a nested mutation silently fails?

Integration Approach

Stack fit

  • Only pursue in one of two shapes: (1) a Laravel service already using laravel-doctrine/orm, where this is additive; or (2) an isolated module where a dedicated EntityManager is an explicit architectural decision, scoped to documents that are read-mostly or write-once.
  • Preferred database is PostgreSQL. JSONB gives indexable storage (GIN), containment operators, and the dedicated jsonb_document type on DBAL ≥ 4.3. MySQL 5.7+ is supported but restricted to JSON with generated-column indexing — a degraded experience, not parity.
  • Do not plan to use this for high-churn, large, deeply nested aggregates. Plan it for bounded documents (typically < 64KB) with coarse-grained updates.

Migration path

  • Incremental, not wholesale. Keep existing Eloquent models untouched; introduce a Doctrine-mapped entity for one new bounded concern (audit payload, external webhook envelope, CMS block tree) behind a repository interface.
  • Adopt type_map aliases from day one — never persist FQCNs. Retrofitting aliases after data exists is the single most expensive correction available here.
  • Treat DDL as Laravel-owned: use Laravel migrations to create the JSON/JSONB columns and indexes, and keep Doctrine to mapping + DML only. Running two schema tools against one database is a foreseeable drift incident; pick one and document it.

Compatibility

  • Pin and verify: PHP 8.1+, Doctrine ORM 2.6+/DBAL 2.6+ (realistically DBAL 4.3+ if you want jsonb_document), Symfony Serializer version compatible with both your Laravel release and the bridge package. Recent releases add Symfony 8 support and a fix for typed object collections on Serializer 8.1 — check your exact matrix.
  • Test suite must run against the real engine. SQLite will not exercise JSONB semantics or the jsonb_document path, so CI needs a Postgres service container.
  • Register types idempotently in a service provider (if (!Type::hasType(...))) and assert boot-time validity with a smoke test that persists and reloads a nested graph.

Sequencing

  1. Week 1–2 — spike. Prove: type registration in a Laravel provider, EntityManager wiring, round-trip of a nested POPO graph, one JSONB indexed query, and Octane compatibility.
  2. Week 2–3 — contracts. Define the serialization context (escaped slashes, date formats), the type_map alias policy, the document versioning field, and the clone-before-mutate convention documented in the repo's contributing guide.
  3. Week 3–4 — pilot. One entity, one write path, one read path, with structured logging on hydrate failures and a backfill/repair script.
  4. Post-pilot gate. Decide on expansion only after measuring document sizes, update frequency, and query latency against realistic volume. If the pilot shows frequent nested mutation, stop and switch to a custom Eloquent cast.

Operational Impact

Maintenance

  • Upstream is a small, high-quality but thinly staffed project. Expect to own the integration layer permanently: type registration, serializer configuration, bridge version compatibility, and the class-rename migration runbook. Budget a recurring fraction of an engineer, not a one-off setup cost.
  • Version upgrades are gated on three moving parts (Doctrine DBAL major, Symfony Serializer major, Laravel major). Each is a potential breaking intersection. Pin conservatively and upgrade deliberately; do not float constraints.
  • The package itself is small and stable — the maintenance burden is dominated by its dependency graph and your own serialization contract, not by its code.

Support

  • Community-only: GitHub issues, no SLA, no commercial backing for this specific library. Internal escalation path must exist before adoption.
  • The highest-value internal artifact is not documentation of the library but a runbook: how to add a document type, how to rename a class safely, how to inspect and repair malformed JSON, how to detect silent non-persistence.

Scaling

  • Reads scale well: JSONB with GIN indexes and containment queries is genuinely competitive with document stores for bounded documents.
  • Writes are the constraint: the entire JSON value is rewritten whenever Doctrine flushes a changed entity. Large documents under frequent mutation produce write amplification, WAL pressure, and TOAST/replication overhead.
  • Query patterns must be designed up front. Once documents are in production without targeted indexes, retrofitting indexes means table scans on large tables. Plan generated columns or expression indexes alongside the initial migration.
  • Connection/EntityManager lifecycle under Octane needs explicit validation — long-lived workers plus global DBAL type state is a known foot-gun.

Failure modes

  • Silent lost updates from nested-object mutation (reference comparison in Doctrine's Unit of Work). No error, no log — just stale JSON. Mitigation: clone-before-mutate convention, plus integration tests that mutate nested state and assert persistence.
  • Hydration failure after refactor — class renamed or moved without type_map adoption; reads throw at denormalization. Mitigation: aliases from day one, plus a CI check that no persisted #type value is an FQCN.
  • Serializer drift — a normalizer added or reordered changes the on-disk shape. Mitigation: pin the serializer service definition, snapshot-test serialized output.
  • Schema drift between Laravel migrations and Doctrine metadata; manifests as runtime "column not found" or mapping validation errors, usually in one environment only.
  • Boot-order / duplicate type registration errors under queue workers or Octane; typically fatal, occasionally intermittent.
  • Oversized documents degrading replication and backups; needs a size guard at the write boundary and alerting on document size percentiles.
  • Cross-engine portability loss — anything built on JSONB operators will not run on MySQL; treat engine choice as effectively permanent.

Ramp-up

  • Moderate to steep for an Eloquent-native team: Doctrine Unit of Work, entity metadata, DQL, and repository patterns are all new mental models, and several Eloquent habits (mass assignment, save(), implicit dirty tracking) actively mislead here.
  • Expect 2–4 weeks for a mid-level engineer to be productive, longer to be safe around change detection. Concentrate ownership in one or two engineers rather than distributing it.
  • The Symfony Serializer mental model (normalizers, context, denormalization of nested types) is the second learning curve and is where hydration bugs will cluster.

TPM recommendation: the capability is legitimate and the Postgres JSONB story is strong, but this is a Doctrine-ecosystem package being evaluated in a Laravel context — the integration cost, not the library quality, is the decision driver. Fund a two-week spike only if the codebase already carries Doctrine or the use case is narrowly scoped and write-light; otherwise the cheaper, lower-risk equivalent is a custom Eloquent cast backed by the same Symfony Serializer, which delivers most of the value with none of the EntityManager, DBAL-type, or class-rename-migration surface.

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.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route