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,并且在实际需要之前避免创建存储库。它还意味着您可以从命令访问存储库(调用命令时不会设置侦听器)。
我发布了另一个 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,并且在实际需要之前避免创建存储库。它还意味着您可以从命令访问存储库(调用命令时不会设置侦听器)。