vuryss/doctrine-lazy-json-odm
Adds lazy-loading JSON fields to Doctrine MongoDB ODM documents, so large JSON blobs stay unserialized until accessed. Keeps hydration fast and memory low while preserving a simple typed API for reading and updating nested JSON data.
composer require vuryss/doctrine-lazy-json-odm:^3.0. Before anything else, verify php -v — v3.0.0 requires PHP 8.5. Confirm your container image, Forge/Ploi/Vapor runtime, and CI matrix are on 8.5, otherwise stay on 2.x.
This is a Doctrine package, not an Eloquent one. It only matters if your app already runs Doctrine via laravel-doctrine/orm (or a hand-wired EntityManager) inside Laravel. Eloquent-only apps can stop here.
First place to look: the package's DBAL Type class (to get the exact registered type name) and the mapping snippet in the README. Then register that type where your Doctrine config lives — config/doctrine.php under 'types' => [...], or Type::addType(...) in a service provider if you wire Doctrine manually.
Map the property on the entity with that type instead of json/json_array:
#[ORM\Column(type: 'lazy_json')] // confirm exact name from the package
private array $payload = [];
First use case to try it on: a wide legacy table (audit_logs.payload, products.attributes, webhook_events.body) where list screens hydrate thousands of rows but only a handful ever read the JSON blob. The win is skipping json_decode() during hydration — measure memory_get_peak_usage() on a 1k-row findAll() before and after.
json type as default and switch only the fat columns. Mixing is fine and keeps behaviour predictable for the columns you actually read often.JSON/JSONB/TEXT; only the Doctrine mapping changes. Write a data-free migration only if you're also changing column type at the DB level.->toArray(), or the package's unwrap accessor) rather than returning the entity and hoping the wrapper serialises as a plain array.$qb->select('p.id', 'p.name') avoids hydrating the field at all — cheaper than lazy-loading it.json_decode spy) so a future refactor doesn't silently undo the lazy behaviour.array — the property type may now be a wrapper object.^8.2 app constraint first. Symfony 8.0 is now tested; 7.4 LTS support is retained, which is your bridge if you're upgrading the framework separately.$entity->payload['a'] = 1) may not mark the field dirty, so flush() writes nothing. Always reassign the full value.===, empty(), count(), isset($e->payload['k']) and foreach can behave differently than on a plain array. Type-hint the concrete wrapper (or unwrap in a getter) rather than leaving it as array.json_encode($entity) and toArray() on the entity may emit wrapper internals. Use API Resources/JsonSerializable explicitly.Unknown column type "lazy_json" at getRepository() time. After changing the mapping, clear Doctrine's metadata cache (php artisan cache:clear, and the doctrine metadata cache command if installed).memory_get_usage(), Blackfire, or a temporary json_decode shim to confirm the field is actually deferred. If memory is unchanged, you're probably touching the property in a lifecycle callback (postLoad, accessor in __construct), which defeats the laziness.json, so you keep the lazy behaviour.How can I help you explore Laravel packages today?