Создаем CompilerPass в котором получаем список сервисов помеченых кастомным тегом acme.filter_provider
и регистрируем их в registry сервисе:
<?php
namespace Acme\Bundle\AcmeBundle\DependencyInjection\CompilerPass;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Reference;
/**
* Class FilterProviderRegistryCompilerPass.
*
* @package Acme\Bundle\AcmeBundle\DependencyInjection\CompilerPass
*/
class FilterProviderRegistryCompilerPass implements CompilerPassInterface
{
public const PROVIDER_REGISTRY_SERVICE = 'acme.registry.filter_registry';
public const PROVIDER_TAG = 'acme.filter_provider';
/**
* {@inheritDoc}
*/
public function process(ContainerBuilder $container): void
{
if (!$container->hasDefinition(self::PROVIDER_REGISTRY_SERVICE)) {
return;
}
$registryDefinition = $container->getDefinition(self::PROVIDER_REGISTRY_SERVICE);
foreach ($this->getTaggedServiceIds($container) as $providerId => $providerTags) {
foreach ($providerTags as $providerTag) {
$registryDefinition->addMethodCall(
'registerProvider',
[new Reference($providerId), $providerTag['entity'], $providerTag['code']]
);
}
}
}
/**
* Gets all services tagged with "acme.filter_provider".
*
* @param ContainerBuilder $container The container.
*
* @return array An array contains services.
*/
protected function getTaggedServiceIds(ContainerBuilder $container): array
{
$providers = $container->findTaggedServiceIds(self::PROVIDER_TAG);
foreach ($providers as $serviceId => $tags) {
foreach ($tags as $tag) {
$this->assertTagHasAttributes($serviceId, self::PROVIDER_TAG, $tag, ['entity', 'code']);
}
}
return $providers;
}
/**
* Validates tag attributes for the given service ID.
*
* @param string $serviceId The service ID.
* @param string $tagName The tag name to validate.
* @param array $tagAttributes The list of attributes of the given tag.
* @param array $requiredAttributes The list of required attributes of the given tag.
*
* @throws LogicException
*/
protected function assertTagHasAttributes(
string $serviceId,
string $tagName,
array $tagAttributes,
array $requiredAttributes
): void {
foreach ($requiredAttributes as $attribute) {
if (!empty($tagAttributes[$attribute])) {
continue;
}
throw new LogicException(
sprintf('Tag "%s" for service "%s" must have attribute "%s"', $tagName, $serviceId, $attribute)
);
}
}
}
Далее добавляем созданный CompilerPass в контейнер:
<?php
namespace Acme\Bundle\AcmeBundle;
use Acme\Bundle\AcmeBundle\DependencyInjection\CompilerPass\FilterProviderRegistryCompilerPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
/**
* Class AcmeBundle.
*
* @package Acme\Bundle\AcmeBundle
*/
class AcmeBundle extends Bundle
{
/**
* {@inheritdoc}
*/
public function build(ContainerBuilder $container): void
{
parent::build($container);
$container->addCompilerPass(new FilterProviderRegistryCompilerPass());
}
}
Создаем класс для registry сервиса:
<?php
namespace Acme\Bundle\AcmeBundle\Registry;
use Acme\Bundle\AcmeBundle\Provider\FilterProviderInterface;
use LogicException;
use UnexpectedValueException;
/**
* Class FilterProviderRegistry.
*
* @package Acme\Bundle\AcmeBundle\Registry
*/
class FilterProviderRegistry
{
/**
* Providers storage format:
* [
* '<entityClass>' => <providerObject>,
* ]
*
* @var FilterProviderInterface[]
*/
protected $providers = [];
/**
* Registers a given provider.
*
* @param FilterProviderInterface $provider The filter provider.
* @param string $entityClass The entity class.
* @param string $code The filter code.
*
* @throws LogicException
*/
public function registerProvider(
FilterProviderInterface $provider,
string $entityClass,
string $code
): void {
if (!empty($this->providers[$entityClass])) {
throw new LogicException(sprintf('Provider with entity class "%s" already exists', $entityClass));
}
$provider->setEntityClass($entityClass);
$provider->setCode($code);
$this->providers[$entityClass] = $provider;
}
/**
* Checks if a provider is registered for the given entity class.
*
* @param string $entityClass The entity class.
*
* @return bool True if registered.
*/
public function hasProvider(string $entityClass): bool
{
return !empty($this->providers[$entityClass]);
}
/**
* Gets a provider by the entity class.
*
* @param string $entityClass The entity class.
*
* @return FilterProviderInterface The filter provider.
*
* @throws UnexpectedValueException
*/
public function getProvider(string $entityClass): FilterProviderInterface
{
if (!$this->hasProvider($entityClass)) {
throw new UnexpectedValueException(sprintf('Provider with entity class "%s" is not exist', $entityClass));
}
return $this->providers[$entityClass];
}
/**
* Gets a list of providers.
*
* @return FilterProviderInterface[] The list of registered providers.
*/
public function getProviders(): array
{
return $this->providers;
}
}
В services.yml добавляем registry сервис:
acme.registry.filter_registry:
class: Acme\Bundle\AcmeBundle\Registry\FilterProviderRegistry
public: false
Создаем интерфейс для сервисов, которые будут добавляться в registry сервис:
<?php
namespace Acme\Bundle\AcmeBundle\Provider;
/**
* Interface FilterProviderInterface.
*
* @package Acme\Bundle\AcmeBundle\Provider
*/
interface FilterProviderInterface
{
/**
* Sets an entity class for this provider.
*
* @param string $entityClass The entity class.
*/
public function setEntityClass(string $entityClass): void;
/**
* Gets an entity class of this provider.
*
* @return string The entity class.
*/
public function getEntityClass(): string;
/**
* Sets a code for this provider.
*
* @param string $code The code.
*/
public function setCode(string $code): void;
/**
* Gets a code of this provider.
*
* @return string The code.
*/
public function getCode(): string;
/**
* Outputs concatenated class and code.
*/
public function echo(): void;
}
Создаем класс для провайдера:
<?php
namespace Acme\Bundle\AcmeBundle\Provider;
/**
* Class ApplicationFilterProvider.
*
* @package Acme\Bundle\AcmeBundle\Provider
*/
class ApplicationFilterProvider implements FilterProviderInterface
{
/**
* @var string The entity class.
*/
protected $entityClass;
/**
* @var string The code.
*/
protected $code;
/**
* {@inheritDoc}
*/
public function setEntityClass(string $entityClass): void
{
$this->entityClass = $entityClass;
}
/**
* {@inheritDoc}
*/
public function getEntityClass(): string
{
return $this->entityClass;
}
/**
* {@inheritDoc}
*/
public function setCode(string $code): void
{
$this->code = $code;
}
/**
* {@inheritDoc}
*/
public function getCode(): string
{
return $this->code;
}
/**
* {@inheritDoc}
*/
public function echo(): void
{
echo $this->getEntityClass() . ':' . $this->getCode();
}
}
В services.yml добавляем созданный провайдер:
acme.provider.application_filter:
class: Acme\Bundle\AcmeBundle\Provider\ApplicationFilterProvider
public: false
tags:
- name: acme.filter_provider
entity: Acme\Bundle\AcmeBundle\Entity\Application
code: applications
Использование:
$registry = $container->get('acme.registry.filter_registry');
$registry->getProvider('Acme\Bundle\AcmeBundle\Entity\Application')->echo();