When developing an external component which can be used in other projects, we would want to make it extensible so other developers can add their own capabilities.
In this post I would like to share how to drive it when developing asymfony third party bundle. This is a short example and the idea can be extrapolated to other languages or frameworks.
Let's imagine we are developing a bundle which allows us to send notifications by using three channels:Email,Push notification andsms. Our bundle define an interface which looks like this:
interfaceNotificationInterface{publicfunctionsendNotification(array$data):bool;publicfunctiongetName():string;}
Each of our notifications handlers will look like this:
classEmailNotificationHandlerimplementsNotificationInterface{publicfunctionsendNotification(array$data):void{/* handler email notification. Throw an exception in case on error */}publicfunctiongetName():string{return'email';}}
The other ones (sms and push) will look as email one but itssendNotification implementation would be different.
Now, It's time to tell our bundle to tag all services which implementsNofiticationInterface properly. To do it, we need to register it in our extension class (underDependencyInjection folder)
classMyBundleExtensionextendsExtension{/** * @throws \Exception */publicfunctionload(array$configs,ContainerBuilder$container){// ... load services from file (services.xml, services.yaml or services.php)$container->registerForAutoconfiguration(NotificationInterface::class)->addTag('my_bundle.notification');// other loads ....}}
Now, in our project, we can use symfonytagged_iterator shortcut to get aniterable holding all notification handlers. We can do it simply by binding a variable in ourservices.yaml file:
services:# default configuration for services in *this* file_defaults:autowire:true# Automatically injects dependencies in your services.autoconfigure:true# Automatically registers your services as commands, event subscribers, etc.bind:$notificationHandlers:!tagged_iteratormy_bundle.notification
Now, if we inject$notificationHandlers in any of our services we will have access to all notification handlers. If we would create a new notification handler, for instance,TelegramNotificationHandler, it would be tagged as the other ones since it would implementNotificationInterface and it would be available in$notificationHandlers iterable
With this, we follow theopen-closed principle since we can add other notification handlers without modifying the existing ones.
Tagged iterators are really useful to create central repositories of services which are easily accesible. In my recently published book, I use them to create an accesible collection of api operations. If you want to know more, you can find the bookhere
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse