Installation:
composer require ashleydawson/class-meta-bundle
Register the bundle in config/bundles.php (Symfony 4+):
return [
// ...
AshleyDawson\ClassMetaBundle\AshleyDawsonClassMetaBundle::class => ['all' => true],
];
First Use Case:
Annotate a class/constant (e.g., src/Entity/Status.php):
namespace App\Entity;
/**
* @ClassMeta(title="User Status", description="Defines user account statuses")
*/
class Status
{
/**
* @ConstantMeta(title="Active", description="User is active")
*/
const ACTIVE = 1;
/**
* @ConstantMeta(title="Suspended", description="User is suspended")
*/
const SUSPENDED = 2;
}
Access Metadata: Inject the service in a controller/service:
use AshleyDawson\ClassMetaBundle\ClassMeta\ClassMetaManager;
class StatusController
{
public function __construct(private ClassMetaManager $metaManager) {}
public function listStatuses()
{
$meta = $this->metaManager->getClassMeta(Status::class);
$constantsMeta = $this->metaManager->getClassConstantsMeta(Status::class);
return [
'class' => $meta,
'constants' => $constantsMeta,
];
}
}
Domain-Driven Design (DDD) Value Objects:
Attach metadata to value objects (e.g., Money, Email) for runtime validation or UI hints:
/**
* @ClassMeta(
* title="Monetary Value",
* validation="amount > 0",
* ui={ "type": "currency", "symbol": "$" }
* )
*/
class Money { ... }
Dynamic Forms/CRUD: Use metadata to generate forms dynamically:
$formFields = [];
foreach ($this->metaManager->getClassConstantsMeta(UserRole::class) as $constant => $meta) {
$formFields[] = [
'name' => $constant,
'label' => $meta['title'],
'type' => 'checkbox',
];
}
API Documentation:
Auto-generate Swagger/OpenAPI specs from @ClassMeta/@ConstantMeta:
$schema = [
'type' => 'object',
'properties' => [],
];
foreach ($this->metaManager->getClassConstantsMeta(OrderStatus::class) as $constant => $meta) {
$schema['properties'][$constant] = [
'type' => 'string',
'enum' => [$constant],
'description' => $meta['description'] ?? '',
];
}
Scoped Constants:
Use scoped annotation to group constants logically:
/**
* @ClassMeta(scoped=true)
*/
class Permission {
/**
* @ConstantMeta(scope="admin")
*/
const MANAGE_USERS = 'admin.manage_users';
/**
* @ConstantMeta(scope="user")
*/
const EDIT_PROFILE = 'user.edit_profile';
}
Access scoped metadata:
$adminPermissions = $this->metaManager->getScopedClassConstantsMeta(Permission::class, 'admin');
Dependency Injection Integration: Tag services to process metadata during compilation (Symfony 4+):
services:
App\Metadata\Processor\ClassMetaProcessor:
tags: [class_meta.processor]
Cache Invalidation:
php bin/console cache:clear
FilesystemCache or ApcuCache) with a reasonable TTL (e.g., 3600 seconds).Annotation Parsing:
src/ directory is in autoload-dev in composer.json).composer.json constraints).Scoped Constants:
scoped=true on the class and a scope value on each constant. Omitting either will exclude them from scoped queries.Performance:
private $cachedMeta = [];
public function getCachedClassMeta(string $class): array
{
return $this->cachedMeta[$class] ??= $this->metaManager->getClassMeta($class);
}
Symfony 5+ Deprecations:
symfony/property-info + custom annotations.symfony/dependency-injection).Verify Annotations:
Use Symfony’s AnnotationReader to debug:
$reader = new \Doctrine\Common\Annotations\AnnotationReader();
$reflectionClass = new \ReflectionClass(Status::class);
$classAnnotation = $reader->getClassAnnotation($reflectionClass, ClassMeta::class);
Cache Debugging:
Temporarily disable caching by configuring VoidCache:
ashley_dawson_class_meta:
cache_provider_service_id: doctrine.cache.null
Annotation Syntax:
@ClassMeta() for empty values (e.g., @ClassMeta() vs @ClassMeta(title=""))./** @ClassMeta({"title": "Multi-line", "description": "Works"}) */
Custom Annotation Parsers:
Extend AshleyDawson\ClassMeta\Annotation\ClassMetaReader to support custom annotation formats (e.g., PHPDoc tags).
Metadata Processors:
Implement AshleyDawson\ClassMetaBundle\DependencyInjection\Compiler\ClassMetaProcessorInterface to transform metadata during container compilation:
public function process(array $meta): array
{
$meta['processed'] = true;
return $meta;
}
Cache Providers:
Create a custom cache provider (e.g., Redis) by implementing Doctrine\Common\Cache\CacheProvider.
Integration with Doctrine: Use metadata to customize ORM behavior (e.g., dynamic enum types):
$platform = $this->metaManager->getClassMeta(OrderStatus::class)['platform'] ?? 'string';
$platform->registerDoctrineTypeMapping(OrderStatus::class, 'enum');
How can I help you explore Laravel packages today?