Explain These Services.xml File Terms In Shopware 6 - symfony

What does argument and argument type mean,
What does tag and tag name mean,
in Shopware 6 plugin services.xml file?
<?xml version="1.0" ?>
<services>
<service id="Winner\Service\ExampleService" />
<service id="Winner\Service\ExampleServiceDecorator" decorates="Winner\Service\ExampleService">
<tag name="kernel.event_listener" event="product.loaded" />
<argument type="service" id="Winner\Service\ExampleServiceDecorator.inner" />
</service>
</services>

The services.xml file is the configuration file for the symfony dependency injection container.
By default, Symfony allows autowiring of the config, but the Shopware default is to configure the DI container manually. Please refer to the Symfony docs for more information on manually configuring the DI.
And the Symfony docs also have more information on service tags.
So technically this is not a Shopware specific question, but rather a Symfony question. I hope my answer and the provided docs help you.

Related

Extends a KnpMenu from a Symfony Bundle

I am using Sylius as a shop Symfony bundle, and I would like to extend the KnpMenu used in "/admin" path of this bundle.
In Sylius, the menu is made from a service :
<service id="sylius.menu_builder.admin.main" class="Sylius\Bundle\AdminBundle\Menu\MainMenuBuilder"
parent="sylius.menu_builder" public="false">
</service>
<service id="sylius.menu.admin.main" class="Knp\Menu\MenuItem">
<factory service="sylius.menu_builder.admin.main" method="createMenu" />
<tag name="knp_menu.menu" alias="sylius.admin.main" />
</service>
Is there a way to add an entry in this menu from my own Bundle ?
Thanks for your help !
Yes, you have to create MenuListener, add child elements in there, and register it as a service. You have it explained in the documentation: http://docs.sylius.org/en/latest/customization/menu.html
Good luck!

symfony2 factory-service in yaml

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 ;)

how to user inject a different user manager to sonata user bundle

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.

Cannot load doctrine:config , services.xml

I am trying to build an eventlistener for my project and followed the instruction of this guide: How to register eventlisteners I edited my services.xml like in the tutorial , but i get this error message, when i want to test it:
InvalidArgumentException: There is no extension able to load the configuration for "doctrine:config" (in /var/www/symfony/src/Acme/AppBundle/DependencyInjection/../Resources/config/services.xml). Looked for namespace "http://symfony.com/schema/dic/doctrine", found none
My services.xml :
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:doctrine="http://symfony.com/schema/dic/doctrine"
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">
<doctrine:config>
<doctrine:dbal default-connection="default">
<doctrine:connection driver="pdo_sqlite" memory="true" />
</doctrine:dbal>
</doctrine:config>
<services>
<service id="my.listener" class="Acme\AppBundle\EventListener\Confirmer">
<tag name="doctrine.event_listener" event="preUpdate" />
</service>
</services>
</container>
Where is the problem?
You don't need the doctrine configuration in your service config.
The connection driver is being configured inside app/config/config.yml.
The listener configuration looks good though - just remove the doctrine:config stuff.
Have a look at the doctrine configuration reference.
Symfony will automatically use the default connection for your listener/subscriber if you don't specify one using the connectionoption. The documentation is a bit confusing here but the doctrine connection is just there to show how to use a different connection.

How to programically inject a dependency to N classes in Symfony2? Can I somehow inherit injected services?

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 ).

Resources