chamber-orchestra / view-bundle
Symfony bundle providing a typed, reusable view layer for building JSON API responses with automatic property binding and cache-warmed serialization
Installs: 57
Dependents: 2
Suggesters: 0
Security: 0
Stars: 228
Watchers: 3
Forks: 23
Open Issues: 0
Type:symfony-bundle
pkg:composer/chamber-orchestra/view-bundle
Requires
- php: ^8.5
- doctrine/common: ^3.5
- symfony/config: 8.0.*
- symfony/dependency-injection: 8.0.*
- symfony/framework-bundle: ^8.0
- symfony/http-kernel: 8.0.*
- symfony/property-access: 8.0.*
- symfony/runtime: ^8.0
- symfony/serializer: 8.0.*
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.94
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^13.0
- symfony/test-pack: ^1.2
Conflicts
- dev-master
- 8.0.x-dev
- v8.0.19
- v8.0.18
- v8.0.17
- v8.0.16
- v8.0.15
- v8.0.14
- v8.0.13
- v8.0.12
- v8.0.11
- v8.0.10
- v8.0.9
- v8.0.8
- v8.0.7
- v8.0.6
- v8.0.5
- v8.0.4
- v8.0.3
- v8.0.2
- v8.0.1
- 7.3.x-dev
- v7.3.0
- 7.2.x-dev
- v7.2.1
- v7.2.0
- 7.0.x-dev
- v7.0.1
- dev-dependabot/github_actions/actions/checkout-6
- dev-dependabot/github_actions/actions/cache-5
- dev-performance-optimisations
- dev-wtorsi-patch-1
- dev-update_versions
This package is auto-updated.
Last update: 2026-02-16 18:23:59 UTC
README
ChamberOrchestra View Bundle
A Symfony bundle that provides a typed view layer for JSON API responses. Define response shapes as PHP classes, return them from controllers, and let the bundle handle serialization automatically — no manual JsonResponse construction needed.
Built for Symfony 8.0 and PHP 8.5+, the bundle eliminates boilerplate in REST API controllers by introducing view models with automatic property binding, collection mapping, and production-ready cache warming.
Key Features
- Typed view models — define JSON response structures as PHP classes with typed properties
- Automatic property binding —
BindViewmaps domain object properties to view properties via reflection - Collection mapping —
IterableViewtransforms arrays and iterables with typed element views - Null stripping — null values are automatically excluded from serialized JSON output
- Build-time cache warming — pre-computed metadata and property mappings eliminate reflection overhead in production
- Build-versioned caching — cache files are tied to
container.build_idfor zero-downtime deployments - Doctrine proxy support — transparent lazy-load initialization before property access
Requirements
- PHP 8.5+
- Symfony 8.0 components (http-kernel, serializer, property-access, dependency-injection, config, framework-bundle)
- doctrine/common ^3.5
Installation
composer require chamber-orchestra/view-bundle:8.0.*
Enable the bundle in config/bundles.php:
return [ // ... ChamberOrchestra\ViewBundle\ChamberOrchestraViewBundle::class => ['all' => true], ];
Quick Start
Define a view model that maps properties from a domain object:
use ChamberOrchestra\ViewBundle\View\BindView; use ChamberOrchestra\ViewBundle\Attribute\Type; use ChamberOrchestra\ViewBundle\View\IterableView; final class UserView extends BindView { public string $id; public string $name; #[Type(ImageView::class)] public IterableView $images; public function __construct(User $user) { parent::__construct($user); } } final class ImageView extends BindView { public string $path; }
Return the view from a controller — the bundle converts it to a JsonResponse automatically:
#[Route('/user/me', methods: ['GET'])] final class GetMeAction { public function __invoke(): UserView { return new UserView($this->getUser()); } }
ViewSubscriber converts any ViewInterface result into a JsonResponse. Non-view results pass through unchanged.
View Types
| View | Purpose |
|---|---|
ResponseView |
Base response with HTTP status (200) and Content-Type: application/json headers |
DataView |
Wraps any view or array under a "data" key |
BindView |
Maps matching properties from a source object using reflection |
IterableView |
Maps collections via a callback or view class string |
KeyValueView |
Produces associative array output for metadata blocks |
BindView Property Binding
BindView uses BindUtils to synchronize properties between source objects and view instances. It handles:
- Built-in PHP types and custom objects
ViewInterfacesubclasses (auto-constructed)IterableViewproperties with#[Type(ViewClass::class)]attribute for typed collections- Skips union types and incompatible type pairs
Architecture
Request/Response Flow
- SetVersionSubscriber (priority 256) — configures
BindUtilswithkernel.share_dir,container.build_id, and enables property accessor caching whenAPP_DEBUG=false - Controller returns a
ViewInterfaceobject - ViewSubscriber — detects
ViewInterfaceresults, wraps non-ResponseViewInterfaceinDataView, serializes to JSON viaViewNormalizer
View Auto-Discovery
Views implementing ViewInterface are automatically tagged with chamber_orchestra.view via #[AutoconfigureTag]. The ViewPass compiler pass collects these classes and passes them to cache warmers for pre-computation.
Performance Optimizations
The bundle includes a two-phase optimization strategy for production environments:
Phase 1: Runtime Metadata Caching
ViewMetadataFactorycaches property metadata in memory- Direct property access eliminates repeated reflection calls
- 30-50% faster normalization on repeated calls
Phase 2: Build-Time Cache Warming
ViewMetadataCacheWarmerpre-computes view property metadata at build timeBindUtilsCacheWarmerpre-computes view-to-view property mappings- Generated opcache-optimized PHP files stored in
kernel.share_dir - Cache files are versioned with
container.build_idfor safe deployments - 60-80% reduction in reflection overhead on production requests
- Automatic fallback to reflection when warmed cache is unavailable
Cache Configuration
SetVersionSubscriber configures BindUtils with the share directory and build ID. When APP_DEBUG=false, property accessor caching is enabled with a 24-hour lifetime.
Warm the cache in production:
bin/console cache:warmup --env=prod
This generates build-versioned files in the shared cache directory:
- View property metadata (nullability, defaults, types)
- View-to-view property mappings for
BindUtils
Benchmarks
Benchmark scripts are included to measure serialization performance and cache impact:
# Quick normalization timing php benchmark/simple-timing.php # Cache warmup impact test php benchmark/cache-warmup-test.php # Memory usage analysis php benchmark/memory-test.php
PHPBench integration:
composer require --dev phpbench/phpbench vendor/bin/phpbench run --report=default
Development
composer install # Install dependencies composer test # Run all tests (87 tests, 326 assertions) ./bin/phpunit # Run tests directly ./bin/phpunit --filter X # Run specific test class or method
License
MIT