4

我在一对一关联中有 2 个实体。第一个,Person存储在 MySQL 数据库中并由 Doctrine 处理。第二个,AdUserRecord描述 ActiveDirectory 用户记录。它是只读的。它不需要知道Person。此外, AdUserRecord出于隐私原因,属性不应存储在 MySQL 数据库中。

AdUserRecord使用服务检索An AdSearcher,该服务可以通过samaccountname或进行搜索objectGUID。每当搜索成功时,服务都会检查是否有相应的Person记录,如果没有则创建一个。这很好用。

当我从一个Person对象开始时会出现我的问题。大多数情况下,我不需要访问Person's AdUserRecord,因此除非需要,否则我不想查询 Active Directory。我认为,这意味着Person::getAdrecord()需要访问该AdSearcher服务。像这样的东西:

public function getAdrecord(){
   if($this->adrecord) return $this->adrecord; 

   $searcher = ???; //get AdSearcher service somehow
   $record = $search->getRecordByUserGuid($this->ad_guid);
   if(!$record) throw new \Exception('this person no longer exists');
   
   $this->adrecord = $record;
   return $this->adrecord;
}

我一直在非常勤奋地阅读 Symfony 文档,但我仍然感到困惑。

问题

  • 如何将服务获取到实体中?它应该通过构造函数注入,还是仅仅在 getter 中需要的地方注入?如果它只出现在 getter 中,我必须注入它还是有办法导入它?
  • 向实体添加服务是处理这些类型情况的规范方法吗?为AdUserRecords 建立一个实体管理器会更好吗?
  • 如果我必须构建实体管理器,我需要实现哪些接口?

Person班级

namespace ACRD\DefaultBundle\Entity;

use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use ACRD\DefaultBundle\Entity\AdUserRecord;

/**
 * @ORM\Entity
 * @Orm\Table(name="person")
 *
 */
class Person {
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\Column(name="AD_guid", type="string", length=36, unique=true)
     */
    protected $ad_guid;

    /**
     * @var AdUserRecord
     */
    protected $adrecord;

     //usual getters and setters
}
4

2 回答 2

1

看起来 Doctrine 的postLoad事件是最好的解决方案。

// src/Acme/DemoBundle/EventListener/ActiveDirectorySubscriber.php
namespace Acme\DemoBundle\EventListener;

use Acme\DemoBundle\Model\AdAwareInterface;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\LifecycleEventArgs;
// for doctrine 2.4: Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use Symfony\Component\DependencyInjection\ContainerAware

class ActiveDirectorySubscriber extends ContainerAware implements EventSubscriber
{
    public function getSubscribedEvents()
    {
        return array(
            'postLoad',
        );
    }

    public function postLoad(LifecycleEventArgs $args)
    {
        $entity = $args->getEntity();

        if (!($entity instanceof AdAwareInterface)) {
            return:
        }

        $adSearcher = $this->getContainer()->get('acme_demo.ad_searcher');

        if ($adPerson = $adSearcher->find($entity->getAdGuid())) {
            $entity->setAdPerson($adPerson);
        }
    }
}

您还提到大多数时候您不需要使用活动目录的东西。在优化之前,我强烈建议您实际衡量一下对性能的影响有多大。但是,如果您确实注意到了性能问题,请考虑使用代理对象将AdPerson搜索权减少到您实际需要从中获取某些内容的程度。

public function postLoad(LifecycleEventArgs $args)
{
    $entity = $args->getEntity();

    if (!($entity instanceof AdAwareInterface)) {
        return:
    }

    $adSearcher = $this->getContainer()->get('acme_demo.ad_searcher');

    $entity->setAdPerson(new AdPersonProxy($adSearcher));
}

AdPersonProxy基本上会从您的类扩展,AdPerson用调用来包装每个公共方法以加载实际AdPerson对象,然后充当两者之间的外观。不过,在开始编码之前请考虑以下含义:

  • 它增加了代码库的复杂性(代码越多,需要维护的就越多);
  • 调试会很痛苦-例如,您可能会在模板中遇到异常,这会让您长时间摸不着头脑(去过那里,做到了);

底线是理论上服务不应该(大部分)注入实体内部。

于 2013-08-01T02:38:50.057 回答
0

关于你的第三个问题:

EntityManagers 实现Doctrine/Common/Persistence/ObjectManager- 看看github 上的接口

更远:

一个有点干净的实现类似于gedmo/doctrine-extensions提供的 Document<->Entity 映射(称为引用)。

查看文档以了解它在此处此处的工作原理。

如果这就是您想要开始深入研究ReferenceListener的代码:)

于 2013-08-01T18:19:07.790 回答