如何删除Symfony服务?(索纳塔分类)


How to remove a Symfony service? (Sonata Classification)

我将SonataAdminBundle与依赖ClassificationBundle的MediaBundle一起使用。默认情况下,ClassificationBundle为类别、标记、集合和上下文添加了后端管理,但由于我的应用程序不使用它们,我想将它们从菜单和管理面板中删除。

我以前从未删除过服务,所以我不知道该怎么做。

必须有一种方法从SonataClassificationBundle/Resources/config/admin.xml中删除这些服务,显然不需要修改文件本身,因为它是一个供应商文件。

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

或者有没有办法将它们从Sonata管理池中删除?既然他们被sonata.admin标记了?

编辑

使用Sonata Easy Extends,我扩展了捆绑包,并添加了编译器通行证:

class ApplicationSonataClassificationBundle extends Bundle
{
    /**
     * {@inheritdoc}
     */
    public function getParent()
    {
        return 'SonataClassificationBundle';
    }
    public function build(ContainerBuilder $container)
    {
        parent::build($container);
        $container->addCompilerPass(new CustomCompilerPass());
    }
}

编译器过程看起来像这个

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

但我得到

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

该文件正在导入整个sonata捆绑包的路由

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

我想,即使从容器中删除了该服务,sonata管理员也在使用该服务。我该怎么改?

EDIT2

我做到了!我不得不把编译器通行证放在Sonata Admin Extension(及其命名空间)中,而不是Sonata Media中。显然,还扩展了Admin捆绑包。之后工作得很好。

我真正不明白的是,当原始捆绑包在我的扩展捆绑包之后加载时,它为什么能工作:

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

这很奇怪。

因为我们谈论的是一个捆绑包,它是应用程序的一个依赖项,所以您无法控制它定义并注册到应用程序中的服务。

我相信使用ContainerBuilder::removeDefinition()可以删除它们。它也将适用于其他捆绑包中定义的服务,因此它将适用于Sonata捆绑包。

您可以在Symfony的文档中看到一个示例,说明将此代码放在何处以及如何访问ContainerBuilder对象。

但是,我建议你不要这样做。即使你不会使用某些服务,它们也不会打扰你,而且考虑到Symfony处理服务的方式,它们不会在生产中造成任何性能问题,我保证。

您需要创建一个编译器过程来删除定义。为了能够做到这一点,你必须确保你的捆绑包是在索纳塔一号之后声明的。如果您无法控制这一点,那么扩展Sonata捆绑包并在其中定义编译器传递。

半年后,我发现了一种更干净的方法,可以将服务保存在容器中(因为某个地方需要它们),但不会在管理面板上显示它们。

   $definitionsNames = array('sonata.media.admin.media', 'sonata.media.admin.gallery_has_media', 'sonata.media.admin.gallery',
        'sonata.classification.admin.category','sonata.classification.admin.tag','sonata.classification.admin.context','sonata.classification.admin.collection');
    foreach ($definitionsNames as $definitionName) {
        $definition = $container->getDefinition($definitionName);
        $tags = $definition->getTags();
        $tags['sonata.admin'][0]['show_in_dashboard'] = false;
        $definition->setTags($tags);
    }

不幸的是,管理路由仍然可用。这对我方来说不是问题,但我相信有办法消除它们。问题是media_widget包含一个指向管理媒体编辑路由的链接,因此需要覆盖该链接才能不再显示。然后需要覆盖Media、Gallery和GHM管理员,并覆盖configroutes()函数并删除所有路由。然后我相信你不能通过管理员访问任何东西,但应用程序仍然可以使用管理服务,如果在任何地方需要的话。

这样,我仍然遵循拉杜的建议,不要从容器中删除服务。

这个问题已经很老了,但我遇到了同样的问题,并使用编译器过程(在Symfony 4及以上版本中)删除了所需的服务。

创建一个编译器通行证,删除您的服务:

namespace App'DependencyInjection'CompilerPass;
use MyService;
use Symfony'Component'DependencyInjection'Compiler'CompilerPassInterface;
use Symfony'Component'DependencyInjection'ContainerBuilder;
class RemoveUnusedServices implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        $container->removeDefinition(MyService::class);
    }
}

并将该服务注册到App类:

namespace App;
use App'DependencyInjection'CompilerPass'RemoveUnusedServices;
use Symfony'Component'DependencyInjection'Compiler'PassConfig;
use Symfony'Component'DependencyInjection'ContainerBuilder;
use Symfony'Component'HttpKernel'Bundle'Bundle;
class App extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        $container->addCompilerPass(new RemoveUnusedServices(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -2000);
        // (add the desired priority, if needed)
    }
}

有时,要删除的服务也在另一个服务的定义中使用(例如,使用方法调用)。

如果是这种情况,您还必须更改受影响服务的定义(使用Definition)。

$affected = $container->getDefinition(MyDependentService::class);
// then, you have to alter the configuration, using Definition
// i.e.:
//$affected->replaceArgument(0, new Definition(AnotherService::class));

注意:更改应用程序行为的另一种方法是装饰您想要更改的服务,以提供所需的行为。这可能更容易。