codememory/entity-response-control
Define response prototypes to shape arrays from Doctrine entities using PHP attributes. Build API-friendly payloads with custom formatting, naming strategies, and decorators, including extra per-object data from additional queries.
Installation
composer require codememory/entity-response-control
Basic Setup
Initialize the ResponsePrototypeManager with required dependencies (cache, reflector, factories, and decorators):
use Codememory\EntityResponseControl\ResponsePrototypeManager;
use Codememory\Reflection\ReflectorManager;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
$cache = new FilesystemAdapter('codememory', ['directory' => storage_path('cache')]);
$reflectorManager = new ReflectorManager($cache);
$manager = new ResponsePrototypeManager(
$reflectorManager,
new PrototypeExecutionContextFactory(new PropertyWrapperFactory()),
new PropertyExecutionContextFactory(),
new BaseCollector(new DecoratorRegistrar(), new SnakeCamelNamingStrategy()),
new DecoratorRegistrar()
);
First Use Case
Define a prototype class (e.g., UserPrototype) with properties matching your entity structure, then collect data:
class UserPrototype {
private ?string $name = null;
private ?string $surname = null;
}
$user = new User(); // Your entity
$result = $manager->collect(UserPrototype::class, $user);
Prototype Design
#[Property\Getter]) to map entity methods to prototype properties.class PostPrototype {
#[Property\Getter('getTitle')]
private ?string $title = null;
#[Property\Getter('getPublishedAt')]
#[Property\DateFormat('Y-m-d')]
private ?string $publishedAt = null;
}
Nested Collections
Handle relationships (e.g., User → Order) with #[Property\Nested]:
class UserPrototype {
#[Property\Nested(OrderPrototype::class)]
private array $orders = [];
}
Metadata Integration
Combine entity data with external metadata (e.g., analytics) using #[Property\FromObjectMetadata]:
class UserPrototype {
#[Property\FromObjectMetadata('getId', 'success_orders')]
private ?int $successOrders = null;
}
$result = $manager->collect(UserPrototype::class, $users, ['success_orders' => [...]]);
Dynamic Metadata via Callbacks Fetch metadata dynamically via services:
#[Prototype\Callback([AnalyticsService::class, 'fetchUserStats'])]
class UserPrototype { ... }
Service Provider Bind the manager to Laravel’s container for dependency injection:
public function register(): void {
$this->app->singleton(ResponsePrototypeManager::class, fn($app) => new ResponsePrototypeManager(
new ReflectorManager($app['cache']->getAdapter('codememory')),
// ... other dependencies
));
}
API Responses Use in controllers to transform entities:
public function show(User $user) {
$prototype = $this->manager->collect(UserPrototype::class, $user);
return response()->json($prototype);
}
Caching
Leverage Symfony’s cache adapter (e.g., FilesystemAdapter) for reflection caching to improve performance.
Attribute Parsing
#[Property\Getter] must be imported explicitly (e.g., use Codememory\EntityResponseControl\Decorators\Property).Metadata Mismatches
FromObjectMetadata keys don’t match entity getter values, properties will default to null.collect().Nested Collection Handling
#[Property\Nested]) require the nested prototype class to exist and be properly configured.Cache Invalidation
$cache->clear();
Enable Debug Mode
Temporarily disable caching in ReflectorManager for live debugging:
$reflectorManager = new ReflectorManager(null); // Disables cache
Inspect Execution Context
Use PrototypeExecutionContextInterface to debug metadata or decorators:
$manager->collect(UserPrototype::class, $user, fn($context) => $context->setMetadata([...]));
Custom Decorators
Extend functionality by creating new decorators (e.g., #[Property\CustomFormat]):
use Codememory\EntityResponseControl\Interfaces\PropertyDecoratorInterface;
class CustomFormatDecorator implements PropertyDecoratorInterface { ... }
Naming Strategies
Override default naming (e.g., SnakeCamelNamingStrategy) for API consistency:
$manager = new ResponsePrototypeManager(..., new CustomNamingStrategy());
Collectors
Replace BaseCollector to implement custom logic (e.g., filtering, transformations):
class CustomCollector extends BaseCollector { ... }
Batch Processing For large collections, process in chunks to avoid memory issues:
$results = [];
foreach ($users as $user) {
$results[] = $manager->collect(UserPrototype::class, $user);
}
Avoid Redundant Calls
Reuse the ResponsePrototypeManager instance (e.g., as a singleton) to cache reflection data.
How can I help you explore Laravel packages today?