fsi/metadata
DEPRECATED: do not use. FSi Metadata Component reads class configuration metadata from sources like annotations (currently PHP annotations only). Provides a ClassMetadata object and abstract drivers (e.g., annotation driver) to store class/property/method metadata.
Install Dependencies:
composer require doctrine/annotations symfony/yaml spatie/array-to-xml
(Note: fsi/metadata is deprecated; use this as a reference for similar functionality.)
Create a Custom Driver (Laravel-compatible):
// app/Metadata/AnnotationDriver.php
namespace App\Metadata;
use Doctrine\Common\Annotations\AnnotationReader;
use FSi\Component\Metadata\Driver\AbstractAnnotationDriver;
use FSi\Component\Metadata\ClassMetadataInterface;
class AnnotationDriver extends AbstractAnnotationDriver
{
public function __construct(private AnnotationReader $reader) {}
public function loadClassMetadata(ClassMetadataInterface $metadata): void
{
$reflection = $metadata->getClassReflection();
foreach ($reflection->getProperties() as $property) {
foreach ($this->reader->getPropertyAnnotations($property) as $annotation) {
$metadata->addPropertyMetadata(
$property->name,
$annotation->name,
$annotation->value
);
}
}
}
}
Register in Laravel:
// app/Providers/MetadataServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Metadata\AnnotationDriver;
use Doctrine\Common\Annotations\AnnotationReader;
use FSi\Component\Metadata\MetadataFactory;
use Illuminate\Contracts\Cache\Store;
class MetadataServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton(AnnotationReader::class, fn() => new AnnotationReader());
$this->app->singleton(AnnotationDriver::class, fn($app) =>
new AnnotationDriver($app->make(AnnotationReader::class))
);
$this->app->singleton(MetadataFactory::class, fn($app) =>
new MetadataFactory(
$app->make(AnnotationDriver::class),
$app->make(Store::class),
'laravel-metadata'
)
);
}
}
First Usage:
// In a controller or service
$metadata = app(MetadataFactory::class)->getClassMetadata(\App\Models\User::class);
$emailRule = $metadata->getPropertyMetadata('email', 'Field');
// app/Models/User.php
namespace App\Models;
use FSi\Component\Metadata\Annotation\Field;
class User
{
#[Field(name: "email", value: "required|email")]
public string $email;
}
// app/Providers/AppServiceProvider.php
public function boot()
{
$factory = app(MetadataFactory::class);
$userMetadata = $factory->getClassMetadata(User::class);
// Cache or process metadata globally.
}
# config/metadata/user.yml
App\Models\User:
properties:
email:
Field: { name: "email", value: "required|email" }
YamlDriver (extend AbstractDriver):
class YamlDriver extends AbstractDriver
{
public function loadClassMetadata(ClassMetadataInterface $metadata): void
{
$config = yaml_parse_file(config_path('metadata/' . $metadata->getClassName() . '.yml'));
// Parse YAML into metadata.
}
}
$cache = new LaravelCacheAdapter(app('cache.store'));
$factory = new MetadataFactory($driver, $cache, 'laravel-metadata');
updated: models event):
event(new ModelUpdated(User::class));
// In event listener:
app('cache')->forget('laravel-metadata-' . User::class);
ClassMetadataInterface for domain-specific data (e.g., CMS fields).
class CmsMetadata implements ClassMetadataInterface
{
public function addFieldMetadata(string $name, array $config): void
{
// Custom logic.
}
}
MetadataFactory:
$factory = new MetadataFactory($driver, $cache, 'prefix', CmsMetadata::class);
// app/Http/Resources/UserResource.php
public function toArray($request)
{
$metadata = app(MetadataFactory::class)->getClassMetadata(User::class);
$fields = $metadata->getPropertyMetadata('email', 'Field')['value'] ?? [];
return ['email' => $this->email, 'rules' => $fields];
}
// app/Http/Requests/StoreUserRequest.php
public function rules()
{
$metadata = app(MetadataFactory::class)->getClassMetadata(User::class);
$rules = [];
foreach ($metadata->getPropertyMetadata('email', 'Field') as $value) {
$rules['email'][] = $value;
}
return $rules;
}
Deprecation Risk:
Attribute + Reflection for long-term use.doctrine/annotations directly with a custom AnnotationReader wrapper.Symfony Dependencies:
AnnotationReader and Doctrine\Common\Cache are Symfony-centric. Laravel requires adapters:
// Adapter for Doctrine Cache
class LaravelCacheAdapter implements \Doctrine\Common\Cache\Cache
{
public function fetch($id) { return app('cache')->get($id); }
public function save($id, $data, $lifeTime = 0) { return app('cache')->put($id, $data, $lifeTime); }
// Implement remaining methods...
}
Performance Overhead:
$factory = new MetadataFactory($driver, app('cache.store'), 'laravel-metadata', null, 3600); // 1-hour TTL
Annotation Conflicts:
#[Attribute] (PHP 8+) and Symfony’s @Annotation may clash. Use namespaced annotations:
namespace App\Metadata\Annotation;
#[Attribute(Attribute::TARGET_PROPERTY)]
class Field {}
YAML/XML Parsing:
composer require symfony/yaml spatie/array-to-xml
AppServiceProvider) and store in Laravel’s cache.Metadata Not Loading?
loadClassMetadata is called. Add logging:
public function loadClassMetadata(ClassMetadataInterface $metadata): void
{
logger()->debug('Loading metadata for:', [$metadata->getClassName()]);
// ...
}
Cache Issues:
php artisan cache:clear
php artisan metadata:clear # Custom artisan command
Annotation Reader Errors:
#[Attribute] (PHP 8+) or @Annotation (legacy).$reader = new AnnotationReader();
$reader->addNamespace('App\Metadata\Annotation');
Add New Drivers:
AbstractDriver for JSON, TOML, or database-backed metadata:
class DatabaseDriver extends AbstractDriver
{
public function loadClassMetadata(ClassMetadataInterface $metadata): void
{
$config = DB::table('metadata')->where('class', $metadata->getClassName())->first();
// Parse $config into metadata.
}
}
Custom Metadata Storage:
ClassMetadataInterface to store metadata in a database or Redis:
class RedisMetadata implements ClassMetadataInterface
{
public function __construct(private \Illuminate\Redis\Connections\Connection $redis) {}
public function getProperty
How can I help you explore Laravel packages today?