When configuring a service using XML, we can do the following:
<service id="foobar" class="App\Foobar" public="false" abstract="true">
<argument type="service" id="doctrine" />
<argument>null</argument>
<argument type="service" id="logger" on-invalid="ignore" />
<argument key="$bombastic" type="service"
id="bombastic.service" on-invalid="ignore" />
</service>
The first three arguments are positional (the first three arguments in the constructor), and the last one is keyed to the parameter name. Since the actual service has 5 arguments, the fourth argument is left undefined so it can be defined by a service that extends the foobar service.
Which is very nice.
In YAML the documentation shows how to use keyed arguments like this:
App\Updates\SiteUpdateManager:
arguments:
$someService: '#manager'
and positional arguments like this:
App\Updates\SiteUpdateManager:
arguments:
- '#manager'
But I'd like to do the same as the above XML configuration, but using YAML (because all the service configuration for this application is already in YAML, and I would not want to add a single XML configuration file just for this service).
How can I combine the two styles with YAML configuration?
Try combining indexed arguments with keyed ones, e.g:
App\Updates\SiteUpdateManager:
arguments:
0: '#doctrine'
1: null
2: '#?logger'
$bombastic: '#?bombastic.service'
Related
I am using symfony2 and its debug toolbar is great.
However, I've come to install som extra bundles which add some elements and it is now displayed on two levels.
How can I do to remove some elements from the toolbar ?
For example, I don't need info about my phpversion, not about the route, etc.
Elements of the toolbar are called DataCollectors, they are special services tagged with data_collector. In the following lines, i will take the Time Datacollector as example.
So in order to deactivate one of them, you first have to get his service id.
You can list all the DataCollectors by running the console command:
php console container:debug --show-private --tag='data_collector'
The output is:
[container] Public and private services with tag data_collector
Service ID template id priority Class name
9d48641ce55174a7d8ab08e99157426bc290884423a78a5821440d644f6a37df_5 #WebProfiler/Collector/time.html.twig time 300 Symfony\Component\HttpKernel\DataCollector\TimeDataCollector
So now you got the id of the service which is time, you have to build the name. Add data_collector. as a prefix of the id to get the name. The name of the service is data_collector.time.
Now as you want to deactivate it, you have to give it a Zero priority.
In your config.yml:
services:
data_collector.time:
class: "%data_collector.time.class%"
tags:
- {name: 'data_collector', priority: '0'}
Now the profiler doesn't have the time no more.
This is a way to unable some profiler items properly. ( A.K.A: A symfony update won't affect it, unless they change the name of the DataCollectors )
The shortiest way is to direcly put the zero priority in vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="data_collector.config.class">Symfony\Component\HttpKernel\DataCollector\ConfigDataCollector</parameter>
<parameter key="data_collector.request.class">Symfony\Component\HttpKernel\DataCollector\RequestDataCollector</parameter>
<parameter key="data_collector.exception.class">Symfony\Component\HttpKernel\DataCollector\ExceptionDataCollector</parameter>
<parameter key="data_collector.events.class">Symfony\Component\HttpKernel\DataCollector\EventDataCollector</parameter>
<parameter key="data_collector.logger.class">Symfony\Component\HttpKernel\DataCollector\LoggerDataCollector</parameter>
<parameter key="data_collector.time.class">Symfony\Component\HttpKernel\DataCollector\TimeDataCollector</parameter>
<parameter key="data_collector.memory.class">Symfony\Component\HttpKernel\DataCollector\MemoryDataCollector</parameter>
<parameter key="data_collector.router.class">Symfony\Bundle\FrameworkBundle\DataCollector\RouterDataCollector</parameter>
</parameters>
<services>
[...]
<service id="data_collector.time" class="%data_collector.time.class%" public="false">
<tag name="data_collector" template="#WebProfiler/Collector/time.html.twig" id="time" priority="0" />
<argument type="service" id="kernel" on-invalid="ignore" />
<argument type="service" id="debug.stopwatch" on-invalid="ignore" />
</service>
[..]
</services>
</container>
All the DataCollectors are not defined in the same file. But here is a quick list of some of them:
data_collector.config:
data_collector.request:
data_collector.router:
data_collector.security:
data_collector.logger:
data_collector.memory:
data_collector.exception:
data_collector.events:
swiftmailer.data_collector:
I had issue with php-cache. It had CacheDataCollector which could crash symfony2 in some cases - https://github.com/php-cache/issues/issues/112
I tried solution above (with priority 0) - and it doesnt worked, data-collector still crashed symfony2
I dont have any ideas why priority = 0 should disable anything.
You could check symfony2 ProfilerPass yourself, which process tag data_collector in
https://github.com/avorobiev/symfony2/blob/master/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ProfilerPass.php
So, I have used pretty dirty patch intead - in application config I add service with the same name, but without tag data_collector.
Like
services:
cache.data_collector:
class: Cache\CacheBundle\DataCollector\CacheDataCollector
More clean and more stable way would be adding CompilerPass, which will remove tag - https://blog.liplex.de/disable-elements-in-the-symfony-developer-toolbar-with-compilerpass/
In Symfony2, is it possible to define a Service in the Service Container that has the Service Container as a dependance? I'm trying to build a CommandDispatcher which will pass a Command to the appropriate CommandHandler. To be able to do this, the CommandDispatcher needs the Container to query it for the appropriate CommandHandler.
Example service XML:
<service id="command_dispatcher" class="CommandDispatcher">
<argument type="service" id="Container"/>
</service>
The id for the argument should be 'service_container'
Do
<service id="command_dispatcher" class="CommandDispatcher">
<argument type="service" id="service_container"/>
</service>
Looking at the code of the Sylius Bundle for Symfony I noticed the Resource Bundle has an interesting way of defining resource controllers as services.
Here is the cart item controller service configuration in XML
<service id="sylius.controller.cart_item" class="%sylius.controller.cart_item.class%">
<argument type="service">
<service factory-service="sylius.controller.configuration_factory" factory-method="createConfiguration" class="Sylius\Bundle\ResourceBundle\Controller\Configuration">
<argument>sylius</argument>
<argument>cart_item</argument>
<argument>SyliusCartBundle:CartItem</argument>
</service>
</argument>
<call method="setContainer">
<argument type="service" id="service_container" />
</call>
</service>
If I understand it correctly this code instantiates the controller class and passes as the constructor argument the result of a call to the factory-method "createConfiguration" in the factory-service class. Arguments are specified, so everything is fine.
My question is twofold:
1) Where is this documented? I could not find one example of this kind of arguments-as-a factory-callable in the docs.
2) What would be the YAML version of this?
Thanks...
Here is the way:
<service id="sylius.controller.cart_item" class="%sylius.controller.cart_item.class%">
<argument type="service">
<service factory-service="sylius.controller.configuration_factory" factory-method="createConfiguration" class="Sylius\Bundle\ResourceBundle\Controller\Configuration">
<argument>sylius</argument>
<argument>cart_item</argument>
<argument>SyliusCartBundle:CartItem</argument>
</service>
</argument>
<call method="setContainer">
<argument type="service" id="service_container" />
</call>
</service>
Can be written as the following in yml
sylius.controller.cart_item:
class: %sylius.controller.cart_item.class%
arguments:
- "#=service('sylius.controller.configuration_factory').createConfiguration('sylius', 'cart_item', 'SyliusCartBundle:CartItem')"
calls:
- [setContainer, ["#service_container"]]
You can find the answer to both of your questions in the dependency injection docs.
As far as defining a service nested under another service in YAML, it doesn't seem the parser that ships with Symfony can handle that, but I did find someone's pet project that seems to aim for this functionality: https://gist.github.com/Mikulas/8004470
I was trying to override the CartItemController and came across this, because I thought i needed to do it this way. But it's not the way to go. Anyways, to answer your question. Here is how the xml transforms into yaml
(because the solution suggested by Alexei Tenitski didn't work for me, I did it like so)
sylius.controller.cart_item:
class: Sylius\Bundle\ResourceBundle\Controller\ResourceController
arguments: ["#sylius.cart_item.config_factory"]
calls:
- [setContainer, ["#service_container"]]
sylius.cart_item.config_factory:
class: Sylius\Bundle\ResourceBundle\Controller\Configuration
factory_class: Sylius\Bundle\ResourceBundle\Controller\ConfigurationFactory
factory_method: createConfiguration
arguments: ["sylius", "cart_item", "SyliusCartBundle:CartItem"]
But I'm guessing you were trying to override the CartItem controller, right? :) that's what I was trying to do anyways.
In the Sylius Docs is explained how you would go about doing that. Like this :
location : yourbundle/resources/config/config.yml
sylius_cart:
classes:
item:
controller: YourBundle\Controller\CartItemController
Also, make sure that if you configure the route to your new controller action, you use the controller service instead of the normal approach.
location : yourbundle/resources/config/routing.yml
mybundle_ajaxcart_add:
path: /ajax/cart/add
defaults: { _controller: sylius.controller.cart_item:addAjaxAction }
I wanted to post it here, because I was looking for this for about half a day and probably someone is going to be looking for the same solution. And I like to save that person the headache ;)
I try to use sonata admin bundle and sonata bundle. I have created the ApplicationSontaUserBundle and everything works fine if I use fosUserInterface. But i have another user Bundle, named Sso for example. So in my app i need to use Sso userManger instead of FosUserManager. The sonata user bundle contains a "admin_orm.xml", and in this services configuration file, I have this line in file:(in sonata user bundle)
<service id="sonata.user.admin.user" class="%sonata.user.admin.user.class%">
<tag name="sonata.admin" manager_type="orm" label="users" label_catalogue="SonataUserBundle" label_translator_strategy="sonata.admin.label.strategy.underscore" />
<argument />
<argument>%sonata.user.admin.user.entity%</argument>
<argument>%sonata.user.admin.user.controller%</argument>
<call method="setUserManager">
<argument type="service" id="fos_user.user_manager" />
</call>
<call method="setTranslationDomain">
<argument>%sonata.user.admin.user.translation_domain%</argument>
</call>
</service>
And in Application/Sonata/UserBundle/Resources/config/admin_orm.xml, I have this one:
<call method="setUserManager">
<argument type="service" id="sso.user_manager" />
</call>
Because i defined another userAdmin in Application/Sonata/Admin/Model/UserAdmin.php
use xxx\SsoBundle\Entity\UserManager;
....
/**
* #param UserManager $userManager
*/
public function setUserManager(UserManager $userManager)
{
$this->userManager = $userManager;
}
Instead of the original FosUserInterface. But, it never take my settings in
Application/Sonata/UserBundle/Resources/config/admin_orm.xml
It always complain the user manager is a FosUserInterface object and I need my own user manager. If i change the
/vendor/sonata-project/user-bundle/Sonata/UserBundle/Resources/config/admin_orm.xml
and set my own user manager, everything works fine. Anyone can tell me how to override this file?
well, after 2 years, I found the solution. And it is so stupid.
I need to define the dependencyInjection class to load the xml and the service will be loaded. The child bundle's configuration file will not be loaded if u do not write the dependencyInjection class. Just as a normal bundle. The configuration file can not be "overwrite", every bundle's configuration file will be red and the last one will win.
In my app I am generating n number of classes. They all have the same skeleton and serve a similar purpose. They also share dependencies.
Instead of adding n entries in services.xml like so:
<service id="acme.security.first_voter" class="Acme\SecurityBundle\Security\Authorization\Voter\FirstVoter" public="false">
<tag name="security.voter" />
<argument type="service" id="logger" />
</service>
<service id="acme.security.second_voter" class="Acme\SecurityBundle\Security\Authorization\Voter\SecondVoter" public="false">
<tag name="security.voter" />
<argument type="service" id="logger" />
</service>
I'd like to simply add one entry like this:
<service id="acme.security.base_voter" class="Acme\SecurityBundle\Security\Authorization\Voter\BaseVoter" public="false">
<tag name="security.voter" />
<argument type="service" id="logger" />
</service>
and in each Voter simply add
use Acme\SecurityBundle\Security\Authorization\Voter\BaseVoter;
class FirstVoter extends BaseVoter
But that does not work.
I've seen Managing Common Dependencies with Parent Services, but it does not solve my issue, becouse it requires I add a
<service id="acme.security.first_voter" class="Acme\SecurityBundle\Security\Authorization\Voter\FirstVoter" parent="base_voter"/>
<service id="acme.security.second_voter" class="Acme\SecurityBundle\Security\Authorization\Voter\SecondVoter" parent="base_voter"/>
for each voter... but thats exacly what I'm trying to avoid, becouse n can be 5 or.. 500.
I've read some old Richard Miller blog posts about injecting a dependency into an interface, and all classes implementing that interface would "inherit injected dependencies" (also be injected that service). Thats exacly what I need! Unfortunately, this has been dropped for some reason and it does not work for Symfony2.3.
Is there any solution to my problem?
You can well use parent services for this purpose.
You just have to register them all using a CompilerPass instead of adding each one manually.
Use the Finder component to search all bundle's i.e. Voter folder for classes extending your base voter - then register them in the CompilerPass.
Improve by caching your results for performance reasons :)
Or you use JMSDiExtraBundle
use JMS\DiExtraBundle\Annotation\Service;
/**
* #Service("some.service.id", parent="another.service.id", public=false)
*/
class Voter extends BaseVoter
{
}
It basically does exactly that ( using a compilerpass ).