RegistryBundle is a Symfony bundle that provides a convenient mechanism for working with registries. This package allows you to automatically register services with specific attributes and interfaces in registries.
To install this package, use Composer:
composer require highcore/registry-bundle
After installation, add RegistryBundle to your Symfony configuration file (config/bundles.php):
return [ // ... Highcore\Bundle\RegistryBundle\RegistryBundle::class => ['all' => true], ];
Registries are registered in the bundle class using a Compiler Pass. This allows services marked with attributes to be automatically registered in the appropriate registries during the container compilation phase.
Example of Registering a Registry
<?php declare(strict_types=1) namespace App\YourBundle; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; use Highcore\Bundle\RegistryBundle\Compiler\Pass\ServiceAttributeRegistryPass; use Highcore\Component\Criteria\Doctrine\Handler\CriteriaRepository; use Highcore\Bundle\RegistryBundle\Registry\IdentityServiceRegistry; use Highcore\Bundle\RegistryBundle\Registry\ServiceRegistry; class YourBundle extends Bundle { public function build(ContainerBuilder $container): void { parent::build($container); $container->addCompilerPass(new ServiceAttributeRegistryPass( definitionId: 'some.your.project.namespace.first.resource.registry', definitionClass: IdentityServiceRegistry::class, targetClassAttribute: \App\YourBundle\Attribute\AsYourResourceAttribute::class, // your attribute class interface: \App\YourBundle\YourServiceInterface::class, // your interface class (interface is optional, if passed, CompilerPass will check your service for an implementation of that interface) )); $container->addCompilerPass(new ServiceAttributeRegistryPass( definitionId: 'some.your.project.namespace.second.resource.registry', definitionClass: ServiceRegistry::class, targetClassAttribute: \App\YourBundle\Attribute\AsYourSecondResourceAttribute::class, // register registry without interface )); } }
In this example, two registries are registered using CompilerPass:
Registers all services marked with the \App\AsYourResourceAttribute attribute, and each service must implement the \App\YourServiceInterface interface.
- Example Interface for your first registry:
<?php declare(strict_types=1) namespace App\YourBundle; interface YourServiceInterface { public function yourMethod(): void; }
- Example Attribute for your first registry:
<?php declare(strict_types=1) namespace App\YourBundle\Attribute; use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; #[\Attribute(\Attribute::TARGET_CLASS)] #[NamedArgumentConstructor] class AsYourResourceAttribute implements IdentityServiceAttributeInterface { public function __construct(private readonly ?string $identifier = null) { } // If self::hasIdentifier() returns false, this method will not be called, // instead we will take the name of the class to which this attribute will be assigned as the identifier public function getIdentifier(): string { return $this->identifier; } public function hasIdentifier(): bool { return null !== $this->identifier; } }
Registers all services marked with the \App\AsYourSecondResourceAttribute attribute.
For example, for second registry we will create only attribute
<?php declare(strict_types=1) namespace App; use Highcore\Component\Registry\Attribute\ServiceAttributeInterface; #[\Attribute(\Attribute::TARGET_CLASS)] class AsYourSecondResourceAttribute implements ServiceAttributeInterface { }
Services can be automatically added to the registry using attributes. Simply add attributes to the service classes you want to register and register them in the symfony service container:
<?php declare(strict_types=1); namespace App\YourBundle\Service; use Highcore\JsonApi\Configurator\JsonApiResourceConfigurator; use App\YourBundle\Attribute\AsYourResourceAttribute; use App\YourBundle\YourServiceInterface; #[AsYourResourceAttribute('some_identifier')] class MyService implements YourServiceInterface { public function yourMethod(): void { // Implementation of the configurator } }
Register your service in the symfony container:
# src/YourBundle/Resources/config/services.php <?php declare(strict_types=1); use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; return static function (ContainerConfigurator $configurator): void { $services = $configurator->services(); $defaults = $services->defaults(); $defaults->autowire(); $defaults->autoconfigure(); // that's all you need to register your service in the registry $services->set(\App\YourBundle\Service\MyService::class); };
To get started, register your service and pass registry "some.your.project.namespace.first.resource.registry" to arguments Take the ServiceRegistry service identifier from the definitionId used earlier in \App\YourBundle::build()
# src/YourBundle/Resources/config/services.php <?php declare(strict_types=1); use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; return static function (ContainerConfigurator $configurator): void { // ... $services->set(\App\YourBundle\Service\SomeYourServiceUsingRegistry::class) ->args([service('some.your.project.namespace.first.resource.registry')]); };
Declare your service
<?php declare(strict_types=1); namespace App\YourBundle; use Highcore\Component\Registry\IdentityServiceRegistryInterface; final class SomeYourServiceUsingRegistry { public function __construct(private readonly Highcore\Component\Registry\IdentityServiceRegistryInterface $registry) { } public function someMethod() { // Retrieve all registered services $yourServices = $this->registry->all(); // Use the services }