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

webit/doctrine-jms-json

A PHP package that bridges Doctrine with JMS serialization for JSON handling, helping you serialize and deserialize entities cleanly in Laravel or Doctrine-based applications.

View on GitHub
Deep Wiki
Context7

Getting Started

webit/doctrine-jms-json gives Doctrine a custom column type that stores a PHP object graph as a JSON string, using JMS Serializer for the conversion. It is a "value object column" tool: you get a single TEXT/JSON column that round-trips an object (or an array of objects) without a join table or a hand-written json_encode/hydrate() pair.

Minimal setup (Symfony shown, but the logic is framework-agnostic):

composer require webit/doctrine-jms-json

The package dates from 2020, so check composer why-not against your doctrine/dbal and jms/serializer versions before writing any code. That compatibility check is step zero.

Register the type before Doctrine touches entity metadata. The type needs a configured JMS serializer instance, so it is a factory-built singleton rather than a plain class name:

use Doctrine\DBAL\Types\Type;
use JMS\Serializer\SerializerBuilder;
use Webit\DoctrineJmsJson\DBAL\JmsJsonTypeFactory; // confirm exact name in src/DBAL

$serializer = SerializerBuilder::create()
    ->addMetadataDir(__DIR__ . '/src/Domain', 'App\Domain')
    ->setCacheDir(__DIR__ . '/var/cache/jms')
    ->build();

$type = JmsJsonTypeFactory::createType($serializer /*, DefaultObjectClass::class */);
Type::addType('jms_json', $type); // DBAL 2 style; see Gotchas for DBAL 3+

Then map a property:

/** @ORM\Column(type="jms_json") */
private Address $shippingAddress;

Where to look first. In order: src/DBAL/JmsJsonType.php (read convertToDatabaseValue/convertToPHPValue — that is the package), the factory that wires the serializer in, and tests/. The README is thin and the test suite is the real specification, especially for how array element types are declared and what the default-object-class fallback does.

First use case. Pick something you'd otherwise model as an owned value object: Address, Money, OpeningHours, or a collection like array<OrderLine> that is only ever read and written as a whole. If you need to query or aggregate on a field inside it, this is the wrong tool — that's a real column.

Implementation Patterns

Single immutable value object. The natural fit. Map the property, keep the VO immutable, and reassign the whole object on change:

public function changeShippingAddress(Address $address): void
{
    $this->shippingAddress = $address; // replace, never mutate in place
}

Collections of objects. JMS needs a type hint to know what to instantiate on the way back. Declare it on the property docblock so the serializer's type inference picks it up:

/** @var OrderLine[] @ORM\Column(type="jms_json") */
private array $lines = [];

If your PHP version and JMS config don't provide that hint, set the default object class on the type at registration time, or wrap the collection in a dedicated OrderLines collection class registered with metadata — the wrapper is the more robust choice because it makes the type explicit at the registration site.

Polymorphism. Store a heterogeneous list (promotions, payment methods, shipping rules) by relying on JMS @Discriminator on an interface/abstract base and registering the concrete subtypes in the serializer metadata. This is the pattern that makes the package genuinely more valuable than json_encode, because JMS resolves the class back from the serialized @type.

Querying. Treat the column as opaque. You can LIKE/REGEXP it, or use native JSON functions in a raw SQL/DQL WHERE on Postgres jsonb or MySQL 5.7+, but there is no index-friendly path and no partial update. Reach for this column only for data you always load with the row.

Migrations. Let the package's SQL declaration decide the type, then verify what it emits on each platform. In practice you want JSON/jsonb on Postgres (indexable, validated) rather than TEXT; if the type emits TEXT, override getSQLDeclaration in a small subclass or set the column type explicitly in the migration while keeping the Doctrine mapping as jms_json. On MySQL, LONGTEXT is often safer than TEXT because nested objects balloon fast.

Schema evolution. Because class names and property names end up in the payload, treat the JSON as a wire format: @SerializedName, @Until/@Since versioning, and JMS class aliases are your migration tools. When you rename App\Domain\Address to App\Domain\Location, old rows still say Address — register an alias or write a one-off backfill, and do it before you ship.

Testing. Add one round-trip test per mapped type against a fixture JSON string committed to the repo. That test is what catches a JMS config change (date format, null handling) before production does. Use SQLite in-memory for the Doctrine side; the serialization behaviour is what you are actually testing.

DTO/API layering. Keep a separate serialization context (JMS groups) for the API and for persistence. Reusing one metadata set means internal fields can leak into your JSON API the moment someone adds a property annotation.

Gotchas and Tips

  • Change tracking will bite you. Doctrine compares the mapped value to the original snapshot; mutating an object inside the mapped value (e.g. $order->getShippingAddress()->setCity(...)) may not register as a change. Always replace the object or reassign the property, and cover it with a functional test that flushes and reloads.
  • Concurrent writes lose data. The whole document is read-modify-written. Two requests editing different sub-fields will clobber each other. Add an @Version optimistic-lock column if the entity is multi-writer.
  • DBAL 3+ compatibility is the big one. Type::getName() was removed and static type registration changed. Expect to register via Type::addType('jms_json', JmsJsonType::class) plus a connection-level mapping, or via DoctrineBundle's doctrine.dbal.types config. If the factory only supports the DBAL 2 signature, shim it in a thin subclass rather than patching vendor code.
  • Unmaintained since 2020. Pin the version, audit the lockfile, and budget for owning the type. It's ~200 lines of code — forking it into src/DBAL/ of your app is a legitimate, low-risk option and removes the dependency entirely.
  • Null vs empty. Confirm null, [], and {} round-trip to what you expect. json_encode flags matter: without JSON_PRESERVE_ZERO_FRACTION, a 1.0 comes back as 1 and your float typed property silently becomes int.
  • Date format stability. JMS's default DateTime format is baked into every stored row. Configure the handler explicitly and never change it without a backfill.
  • Serializer cache in production. Build the serializer with a cache dir or you re-parse metadata on every request. Warm it during deploy.
  • Metadata wiring errors surface late. If a class isn't in a registered metadata dir, serialization silently falls back to reflection or throws on deserialize. Add a smoke test that hydrates every mapped column from a real row.
  • Size and packet limits. A few hundred nested objects can exceed MySQL's max_allowed_packet or a TEXT column's 64 KB. Log payload sizes once in staging.
  • Extension points worth knowing: the type's convertToDatabaseValue/convertToPHPValue and getSQLDeclaration are the only three methods that matter. Subclass for custom serialization context (groups, serializeNull) or a different SQL declaration — that subclass is usually the cleanest fix for every quirk above.
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/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
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