Symfony - 启动时加载服务

Syfmony - load service on boot

我发布了另一个 question 试图找到一种在自定义 "helper" class.[=13= 控制器外部静态访问存储库 class 的方法]

到目前为止,我唯一想出的方法是使用下面的代码。如果有人想回答关于 "best practice" 或 "design patterns" 的其他问题,请回答。

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

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}

DeviceRepository

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

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

服务定义:

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

和自定义 class:

// 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;
    }

很高兴看到你 运行 不在了。

除非有人先从容器中取出存储库,然后初始化 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;
  }

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