Installation:
composer require xphere/tag-bundle
Add to config/bundles.php:
return [
// ...
xPheRe\Bundle\TagBundle\TagBundle::class => ['all' => true],
];
First Use Case:
Define a service with a tag in config/services.yaml:
services:
my_service:
class: App\Service\MyService
tags: ['name: my_tag']
Inject tagged services into another service:
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
class MyConsumerService
{
public function __construct(
private iterable $myTaggedServices
) {}
public function doSomething()
{
foreach ($this->myTaggedServices as $service) {
$service->someMethod();
}
}
}
Key: Use iterable type-hint to automatically inject all services tagged with my_tag.
Basic Tagging:
services:
service_one:
class: App\Service\One
tags: ['name: my_tag']
service_two:
class: App\Service\Two
tags: ['name: my_tag', 'priority: 10']
Use priority to sort services (higher priority first).
Dynamic Tagging:
Use autoconfigure_tags to tag services automatically via attributes:
#[AutoconfigureTag('name: my_tag')]
class MyService {}
Constructor Injection:
class ConsumerService {
public function __construct(
private iterable $myTaggedServices
) {}
}
Method Injection:
class ConsumerService {
public function setTaggedServices(iterable $services) {
$this->services = $services;
}
}
Configure in YAML:
services:
consumer_service:
class: App\Service\ConsumerService
calls:
- [setTaggedServices, ['@my_tag']]
Lazy Loading:
Use TaggedIterator for on-demand access:
use xPheRe\Bundle\TagBundle\TaggedIterator;
class ConsumerService {
public function __construct(
private TaggedIterator $myTaggedServices
) {}
public function getService(string $id) {
return $this->myTaggedServices->get($id);
}
}
Combining with Other Bundles:
Works seamlessly with Symfony’s built-in tagging system. No need for CompilerPass unless extending functionality.
Custom Tag Attributes: Access tag attributes in consumers:
foreach ($myTaggedServices as $id => $attributes) {
$priority = $attributes['priority'] ?? 0;
}
Namespace Change:
Pre-0.4.0 used Berny\Bundle\TagBundle. Post-0.4.0 uses xPheRe\Bundle\TagBundle. Ensure your use statements and bundles.php reflect this.
Circular Dependencies: Avoid tagging services that depend on the consumer service. This can cause circular references during container compilation.
TaggedIterator Behavior:
TaggedIterator returns services in registration order (not alphabetical or by priority). Use usort if custom ordering is needed:
usort($services, fn($a, $b) => ($b['priority'] ?? 0) <=> ($a['priority'] ?? 0));
Missing Services: Verify tags are correctly defined in YAML/XML/PHP. Use:
php bin/console debug:container --tag=my_tag
to list tagged services.
Injection Failures:
If iterable injection fails, ensure:
my_tag.Custom Compiler Passes:
Extend the bundle by creating a CompilerPass to modify tagged services before injection:
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class MyTagCompilerPass implements CompilerPassInterface {
public function process(ContainerBuilder $container) {
$tagged = $container->findTaggedServiceIds('my_tag');
foreach ($tagged as $id => $tags) {
$definition = $container->getDefinition($id);
$definition->addMethodCall('customMethod');
}
}
}
Register the pass in services.yaml:
services:
my_tag_compiler_pass:
class: App\Compiler\MyTagCompilerPass
tags: [compiler_pass]
Override Default Behavior:
Replace the bundle’s TaggedIterator by binding your own implementation:
services:
xphere.tag_bundle.tagged_iterator:
class: App\Service\CustomTaggedIterator
public: true
Avoid Over-Tagging: Tag only services that are actually needed for injection. Excessive tags bloat the container.
Use lazy for Large Collections:
For services with many tagged dependencies, use lazy loading:
#[Autowire(service: 'my_tag', lazy: true)]
private iterable $myTaggedServices;
How can I help you explore Laravel packages today?