George Irimiciuc George Irimiciuc - 6 months ago 203
PHP Question

How to remove a Symfony service? (Sonata Classification)

I'm using SonataAdminBundle with MediaBundle that has a dependency on ClassificationBundle. By default, ClassificationBundle adds in backend admin management for categories, tags, collections and contexts, but since my application doesn't use them I want to remove them from the menu and admin panel.

I've never removed a service before, so I don't know how to do it.

There has to be a way to remove these services from

SonataClassificationBundle/Resources/config/admin.xml
, obviously without modifying the file itself because it is a vendor file.

<services>
<service id="sonata.classification.admin.category" class="%sonata.classification.admin.category.class%">
<tag name="sonata.admin" manager_type="orm" group="sonata_classification" label="label_categories" label_catalogue="%sonata.classification.admin.category.translation_domain%" label_translator_strategy="sonata.admin.label.strategy.underscore" />
<argument />
<argument>%sonata.classification.admin.category.entity%</argument>
<argument>%sonata.classification.admin.category.controller%</argument>
<argument type="service" id="sonata.classification.manager.context" />

<call method="setTranslationDomain">
<argument>%sonata.classification.admin.category.translation_domain%</argument>
</call>

<call method="setTemplates">
<argument type="collection">
<argument key="list">SonataClassificationBundle:CategoryAdmin:list.html.twig</argument>
</argument>
</call>
</service>

<service id="sonata.classification.admin.tag" class="%sonata.classification.admin.tag.class%">
<tag name="sonata.admin" manager_type="orm" group="sonata_classification" label="label_tags" label_catalogue="%sonata.classification.admin.tag.translation_domain%" label_translator_strategy="sonata.admin.label.strategy.underscore" />
<argument />
<argument>%sonata.classification.admin.tag.entity%</argument>
<argument>%sonata.classification.admin.tag.controller%</argument>

<call method="setTranslationDomain">
<argument>%sonata.classification.admin.tag.translation_domain%</argument>
</call>
</service>

<service id="sonata.classification.admin.collection" class="%sonata.classification.admin.collection.class%">
<tag name="sonata.admin" manager_type="orm" group="sonata_classification" label="label_collections" label_catalogue="%sonata.classification.admin.collection.translation_domain%" label_translator_strategy="sonata.admin.label.strategy.underscore" />
<argument />
<argument>%sonata.classification.admin.collection.entity%</argument>
<argument>%sonata.classification.admin.collection.controller%</argument>

<call method="setTranslationDomain">
<argument>%sonata.classification.admin.collection.translation_domain%</argument>
</call>
</service>

<service id="sonata.classification.admin.context" class="%sonata.classification.admin.context.class%">
<tag name="sonata.admin" manager_type="orm" group="sonata_classification" label="label_contexts" label_catalogue="%sonata.classification.admin.context.translation_domain%" label_translator_strategy="sonata.admin.label.strategy.underscore" />
<argument />
<argument>%sonata.classification.admin.context.entity%</argument>
<argument>%sonata.classification.admin.context.controller%</argument>

<call method="setTranslationDomain">
<argument>%sonata.classification.admin.context.translation_domain%</argument>
</call>
</service>
</services>


Or maybe is there a way to remove them from the Sonata admin pool? Since they are tagged with
sonata.admin
?

EDIT

Using Sonata Easy Extends I've extended the bundle and added a Compiler Pass:

class ApplicationSonataClassificationBundle extends Bundle
{
/**
* {@inheritdoc}
*/
public function getParent()
{
return 'SonataClassificationBundle';
}

public function build(ContainerBuilder $container)
{
parent::build($container);

$container->addCompilerPass(new CustomCompilerPass());
}

}


The compiler pass looks like this

class CustomCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$container->removeDefinition('sonata.classification.admin.category');
}
}


But I'm getting

You have requested a non-existent service "sonata.classification.admin.cate
gory" in . (which is being imported from "E:\svn\parkresort\app/config\rout
ing/sonata.yml").


That file is importing the routing for the whole sonata bundle

admin:
resource: '@SonataAdminBundle/Resources/config/routing/sonata_admin.xml'
prefix: /admin

_sonata_admin:
resource: .
type: sonata_admin
prefix: /admin

#sonata media
media:
resource: '@SonataMediaBundle/Resources/config/routing/media.xml'
prefix: /media


I guess the service is being used by sonata admin even after being removed from the container. How can I change that?

EDIT2

I did it! I had to put the Compiler Pass in Sonata Admin Extension(with its namespace) and not in Sonata Media. Also extending the Admin bundle, obviously. Worked just fine afterwards.

What I don't really understand is why it works when the original bundle is loaded after my extended bundle:

//AppKernel.php
new ApplicationSonataAdminBundle(),//extended
new Sonata\AdminBundle\SonataAdminBundle(),


That is strange.

Answer

Since we're talking about a bundle which is a dependency of your application, you have no control over what services it defines and registers into your application.

I believe it's possible to remove them using ContainerBuilder::removeDefinition(). It will work for services defined in other bundles as well, therefore it will work on the Sonata bundle.

You can see an example in Symfony's documentation on exactly where to put this code and how to gain access to the ContainerBuilder object.

However, I advise you to not do this. Even though you won't use some services, they won't bother you and, given how Symfony handles services, they won't cause any performance problems in production, I promise.