【问题标题】:Syfmony - load service on bootSymfony - 在启动时加载服务
【发布时间】:2015-10-14 19:06:34
【问题描述】:

我发布了另一个 question 试图找到一种方法来静态访问自定义“帮助器”类中控制器之外的存储库类。

到目前为止,我想出如何实现这一点的唯一方法是使用下面的代码。如果有人想提出关于“最佳实践”或“设计模式”的其他问题,请这样做。

我打开这个问题是为了寻找在 symfony 启动时加载单例服务(?)的最佳方法,以便其他类可以静态访问它而无需任何依赖注入。我没有找到任何官方文档或常见做法的运气。我知道单例是反实践的,但是下面的方法是最好的方法,还是有更理想的解决方案?

services.yml

parameters:
    entity.device: Asterisk\DbBundle\Entity\Device
services:
    asterisk.repository.device:
    class: Asterisk\DbBundle\Entity\Repositories\DeviceRepository
    factory: ["@doctrine.orm.asterisk_entity_manager", getRepository]
    arguments:
        - %entity.device%
    tags:
        - {name: kernel.event_listener, event: kernel.request, method: onKernelRequest}

设备存储库

class DeviceRepository extends \Doctrine\ORM\EntityRepository
{
    /** @var  ExtendedEntityRepository */
    protected static $instance;

    public function __construct(EntityManager $entityManager, ClassMetadata $class)
    {
        parent::__construct($entityManager, $class);

        if(static::$instance instanceof static == false)
            static::$instance = $this;
    }

    public static function getInstance()
    {
        return static::$instance;
    }

    public function onKernelRequest($event)
    {
        return;
    }
}

【问题讨论】:

    标签: php symfony inversion-of-control


    【解决方案1】:

    很高兴看到你不再到处乱跑了。

    除非有人先将存储库从容器中取出,否则您的方法不会奏效,因此 self::$instance 被初始化。但无论如何,你真的不想这样做。超级hacky。

    您希望将存储库服务注入您的内核侦听器。试图使存储库充当内核侦听器并不是一个好的设计。因此,只需为您的存储库创建一个服务,然后为侦听器创建第二个服务。乍一看可能有点奇怪,但它在实践中确实运作良好,这就是 S2 的设计方式。

    如果由于某种原因您坚持必须能够全局访问容器的概念,那么请注意您的内核是全局定义的(查看 app.php)并且它有一个 getContainer 方法.

    $repo = $_GLOBAL['kernel']->getContainer()->get('asterisk.repository.device');
    

    但同样,应该没有必要这样做。

    ================================

    更新 - 您似乎正在尝试使用侦听器功能来设置单例。你应该尽量避免单例,但如果你真的认为你需要它们,那么可以使用对内核的全局访问:

    class DeviceRepository extends \Doctrine\ORM\EntityRepository
    {
      /** @var  ExtendedEntityRepository */
      protected static $instance;
    
      public static function getInstance()
      {
        if (!static::$instance) {
            static::$instance = $_GLOBAL['kernel']->getContainer()->get('asterisk.repository.device');
        }
        return static::$instance;
      }
    

    糟糕的设计,但至少它摆脱了监听器黑客攻击,并且在实际需要之前避免创建存储库。这也意味着您可以通过命令访问存储库(调用命令时不会设置侦听器)。

    【讨论】:

    • 您能否详细说明将 repo 服务注入内核侦听器?你是说我正在做的事情很好,但我应该注入一个内核监听器而不是每个 repo 都是一个内核监听器(就像我上面的示例代码)?
    • 您是否将侦听器功能用作设置存储库单例实例的一种手段?
    • 是的,因为我知道在 symfony 启动时没有其他方法可以加载它
    【解决方案2】:

    我不明白这种方法的利润是多少。 servicecontainer 的想法是只为每个类创建一个实例,并为要求使用同一实例的任何方法提供引用(或指针,如果您愿意)。让我证明一下:

    服务定义:

    // app/config.yml
    services:
        app.test:
            class: Vendor\AppBundle\Service\Test
    

    和一个自定义类:

    // src/AppBundle/Service/Test.php
    namespace AppBundle/Service;
    
    class Test {
        public $test = 0;
    }
    

    和一个控制器:

    // src/AppBundle/Controller/DefaultController
    namespace AppBundle/Controller;
    
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
    use Symfony\Bundle\FrameworkBundle\Controller\Controller;
    use Symfony\Component\HttpFoundation\Request;
    
    class DefaultController extends Controller
    {
        /**
         * @Route("/", name="homepage")
         */
        public function indexAction()
        {
            $instance1 = $this->get('app.test');
            $instance2 = $this->get('app.test');
    
            $instance1->test = 1;
    
            echo $instance2->test; // RETURNS 1 !!!
            exit;
        }
    

    【讨论】:

    • 正确:但如果您需要访问控制器之外的服务(请参阅相关链接的其他问题),我需要能够在控制器之外调用 Test::someMethod()
    猜你喜欢
    • 1970-01-01
    • 2013-03-27
    • 2019-12-31
    • 2021-08-22
    • 1970-01-01
    • 2017-01-24
    • 2012-06-05
    • 1970-01-01
    相关资源
    最近更新 更多