vuryss / doctrine-lazy-json-odm
A high-performance Doctrine JSON ODM with lazy loading using vuryss/serializer v2
Installs: 25
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
Type:symfony-bundle
Requires
- php: >=8.4
- doctrine/dbal: ^4.0
- doctrine/orm: ^3.4
- halaxa/json-machine: ^1.2
- symfony/config: ^7.0
- symfony/dependency-injection: ^7.0
- symfony/framework-bundle: ^7.0
- vuryss/serializer: ^2.0
Requires (Dev)
This package is auto-updated.
Last update: 2025-08-21 10:55:25 UTC
README
A high-performance Symfony bundle that provides a Doctrine JSON ODM (Object Document Mapper) with advanced lazy loading capabilities. This bundle stores complex object structures as JSON in database fields while providing transparent lazy loading to improve performance.
Features
- 🚀 Lazy Loading: Uses PHP 8.4 lazy objects for deferred JSON deserialization
- âš¡ Serializer: Uses vuryss/serializer v2 for maximum performance
- 🎯 Type Mapping: Map class names to aliases for storage optimization
- 📦 Collection Support: Lazy-loaded arrays of objects with transparent usage
- 📊 Performance Optimized: Significant memory and CPU savings for unused data
Requirements
- PHP 8.4 or higher
- Symfony 7.0+
- Doctrine ORM 3.4+
Installation
Install the bundle via Composer:
composer require vuryss/doctrine-lazy-json-odm
Register the bundle in your config/bundles.php
:
return [ // ... \Vuryss\DoctrineLazyJsonOdm\DoctrineLazyJsonOdmBundle::class => ['all' => true], ];
Configuration
Create a configuration file config/packages/doctrine_lazy_json_odm.yaml
:
doctrine_json_odm: type_map: user: 'App\Entity\User' product: 'App\Entity\Product' order: 'App\Entity\Order' cache_pool: 'app.doctrine_lazy_json_odm.cache_pool'
Usage
Basic Entity Definition
Define your entities with JSON ODM fields using the LazyJsonDocumentType
:
<?php use Doctrine\ORM\Mapping as ORM; use Vuryss\DoctrineLazyJsonOdm\Lazy\LazyJsonArray; use Vuryss\DoctrineLazyJsonOdm\Type\LazyJsonDocumentType; #[ORM\Entity] #[ORM\Table(name: 'users')] class User { #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column(type: 'integer')] private ?int $id = null; #[ORM\Column(type: 'string', length: 255)] private string $name; #[ORM\Column(type: 'string', length: 255, unique: true)] private string $email; // Single object stored as JSON with lazy loading #[ORM\Column(type: LazyJsonDocumentType::NAME, nullable: true)] private ?Profile $profile = null; // Array of objects stored as JSON with lazy loading #[ORM\Column(type: LazyJsonDocumentType::NAME, nullable: true)] private ?LazyJsonArray $orders = null; // Getters and setters... }
Value Objects for JSON Storage
Create readonly classes for your JSON-stored objects:
<?php readonly class Profile { public function __construct( public string $firstName, public string $lastName, public int $age, public ?string $bio = null, public ?Address $address = null, public array $socialLinks = [], ) { } public function getFullName(): string { return $this->firstName . ' ' . $this->lastName; } } readonly class Address { public function __construct( public string $street, public string $city, public string $postalCode, public string $country, public ?string $state = null, ) { } }
Working with Entities
<?php // Create and persist entities $address = new Address('123 Main St', 'New York', '10001', 'USA'); $profile = new Profile('John', 'Doe', 30, 'Software Developer', $address); $user = new User('John Doe', 'john@example.com'); $user->setProfile($profile); $entityManager->persist($user); $entityManager->flush(); // Retrieve and access lazy-loaded data $user = $userRepository->find(1); // Profile is lazy-loaded only when accessed $fullName = $user->getProfile()->getFullName(); // Triggers lazy loading $city = $user->getProfile()->address->city; // Nested objects work transparently
Working with Lazy Arrays
<?php use Vuryss\DoctrineLazyJsonOdm\Lazy\LazyJsonArray; // Create array of objects $orderItems = [ new OrderItem(1, 'Laptop', 1, '999.99', '999.99'), new OrderItem(2, 'Mouse', 1, '29.99', '29.99'), ]; $user->setOrders(new LazyJsonArray($orderItems)); $entityManager->flush(); // Access lazy array $orders = $user->getOrders(); // LazyJsonArray instance $firstOrder = $orders[0]; // Lazy loading triggered here $orderCount = $orders->count(); // Available without loading items // Iterate over lazy array foreach ($user->getOrders() as $order) { echo $order->productName . "\n"; }
Advanced Configuration
Configure type aliases for optimized storage:
# config/packages/doctrine_lazy_json_odm.yaml doctrine_json_odm: type_map: # Short aliases reduce JSON size user: 'App\Entity\User' profile: 'App\Entity\Profile' address: 'App\Entity\Address' order: 'App\Entity\Order' order_item: 'App\Entity\OrderItem' # Namespace-based aliases for organization catalog.product: 'App\Entity\Product' catalog.category: 'App\Entity\Category' # Version-specific aliases user_v2: 'App\Entity\UserV2' # Optional: Configure cache pool for metadata caching cache_pool: 'app.doctrine_lazy_json_odm.cache_pool'
Performance Optimization
<?php // Use type aliases to reduce JSON size $typeMap = [ 'u' => User::class, 'p' => Profile::class, 'a' => Address::class, ]; // JSON with full class names: ~200 bytes // {"#type":"App\\Entity\\Profile","firstName":"John",...} // JSON with aliases: ~150 bytes // {"#type":"p","firstName":"John",...} // Access only what you need to benefit from lazy loading $user = $userRepository->find(1); $name = $user->getName(); // No JSON deserialization yet $profileName = $user->getProfile()->getFullName(); // Profile loaded here // Lazy arrays $orders = $user->getOrders(); // No loading yet $orderCount = $orders->count(); // Loading all orders $firstOrder = $orders[0]; // Already loaded
Testing
Run the test suite:
# Run all tests
vendor/bin/pest
Performance Considerations
Memory Efficiency
- Lazy Loading: Objects are only deserialized when accessed, saving memory for unused data
- Type Aliases: Short aliases can reduce JSON size significantly
- Selective Access: Access only the properties you need
Best Practices
- Use Type Aliases: Configure short, meaningful aliases for frequently used classes
- Cache Configuration: Configure metadata caching for optimal performance
Benchmarks
// Without lazy loading: ~50MB memory for 10,000 user profiles // With lazy loading: ~5MB memory (90% reduction) // JSON size comparison: // Full class names: {"#type":"App\\Entity\\Profile",...} (~200 bytes) // Type aliases: {"#type":"p",...} (~140 bytes) - 30% smaller
Code Quality
The bundle maintains high code quality standards:
# PHP CS Fixer vendor/bin/php-cs-fixer fix --using-cache=no # PHPStan vendor/bin/phpstan analyse -v
Contributing
- Fork the repository
- Create a feature branch
- Add tests for your changes
- Ensure all tests pass
- Run code quality tools
- Submit a pull request
License
This bundle is released under the MIT License. See the LICENSE file for details.
Credits
- Inspired by dunglas/doctrine-json-odm
- Uses vuryss/serializer for high-performance serialization
- Built with PHP 8.4 lazy objects feature