gpt4 book ai didi

php - doctrine2 额外延迟获取关联

转载 作者:塔克拉玛干 更新时间:2023-11-03 06:05:17 26 4
gpt4 key购买 nike

我有一个 Thread 实体,它与 Message 实体有一个 OneToMany 关联。我正在使用 DQL 查询获取线程,我想将其消息数量限制为 10。因此我将获取模式设置为 EXTRA_LAZY,如下所示。

class Thread
{
// ...

/**
* @var ArrayCollection
* @ORM\OneToMany(targetEntity="Profile\Entity\Message", mappedBy="thread", fetch="EXTRA_LAZY")
* @ORM\OrderBy({"timeSent" = "ASC"})
*/
protected $messages;
}

这允许我使用 slice 方法向数据库发出 LIMIT SQL 查询。到目前为止一切都很好。因为我的消息是加密的,所以在将线程对象处理到 Controller (并最终查看)之前,我需要在我的服务层中解密它们。为实现这一目标,我在服务中做了以下工作:

foreach ($thread->getMessages()->slice(0, 10) as $message) {
// Decrypt message
}

slice 的调用触发了一个获取 10 条消息的 SQL 查询。在我看来,我正在执行以下操作来呈现线程的消息:

$this->partialLoop()->setObjectKey('message');
echo $this->partialLoop('partial/thread/message.phtml', $thread->getMessages());

问题是这会从数据库中获取整个消息集合。如果我像在我的服务中那样调用 slice,则会向数据库发出具有 LIMIT 10 的相同 SQL 查询,这是不可取的。

如何在不在我的 View 中发出另一个 SQL 查询的情况下,在我的服务层中处理有限的消息集合?也就是说,要让 doctrine 创建一个 SQL 查询,而不是两个。在我看来,我可以简单地解密我的消息,但在这种情况下,这种做法违背了拥有服务层的目的。我当然可以“手动”获取消息并将它们添加到线程对象中,但如果我可以通过关联自动执行此操作,那就更好了。

提前致谢!

最佳答案

与大多数人建议的方法略有不同:

切片

Thread 实体中,有一个专门用于返回消息片段的方法:

class Thread
{
// ...

/**
* @param int $offset
* @param int|null $length
* @return array
*/
public function getSliceOfMessages($offset, $length = null)
{
return $this->messages->slice($offset, $length);
}
}

这将负责轻松检索 View 中的切片,而没有获取整个集合的风险。

解密消息内容

接下来您需要消息的解密内容。

我建议您创建一个可以处理加密/解密的服务,并让 Message 实体依赖于它。

class Message
{
// ...

/**
* @var CryptService
*/
protected $crypt;

/**
* @param CryptService $crypt
*/
public function __construct(CryptService $crypt)
{
$this->crypt = $crypt;
}
}

现在您必须通过将 CryptService 传递给它来创建 Message 实体。您可以在创建 Message 实体的服务中管理它。

但是这只会处理 you 实例化的 Message 实体,而不是 Doctrine 实例化的实体。为此,您可以使用 PostLoad 事件。

创建一个事件监听器:

class SetCryptServiceOnMessageListener
{
/**
* @var CryptService
*/
protected $crypt;

/**
* @param CryptService $crypt
*/
public function __construct(CryptService $crypt)
{
$this->crypt = $crypt;
}

/**
* @param LifecycleEventArgs $event
*/
public function postLoad(LifecycleEventArgs $event)
{
$entity = $args->getObject();

if ($entity instanceof Message) {
$message->setCryptService($this->crypt);
}
}
}

每当 Doctrine 加载一个时,这个事件监听器将注入(inject)一个 CryptServiceMessage 实体中。

在应用程序的引导/配置阶段注册事件监听器:

$eventListener = new SetCryptServiceOnMessageListener($crypt);
$eventManager = $entityManager->getEventManager();
$eventManager->addEventListener(array(Events::postLoad), $eventListener);

将 setter 添加到 Message 实体:

class Message
{
// ...

/**
* @param CryptService $crypt
*/
public function setCryptService(CryptService $crypt)
{
if ($this->crypt !== null) {
throw new \RuntimeException('We already have a crypt service, you cannot swap it.');
}

$this->crypt = $crypt;
}
}

如您所见,setter 防止换出 CryptService(您只需要在不存在时设置它)。

现在 Message 实体总是有一个 CryptService 作为依赖,无论是你还是 Doctrine 实例化它!

最后我们可以使用CryptService来加密和解密内容了:

class Message
{
// ...

/**
* @param string $content
*/
public function setContent($content)
{
$this->content = $this->crypt->encrypt($content);
}

/**
* @return string
*/
public function getContent()
{
return $this->crypt->decrypt($this->content);
}
}

用法

在 View 中你可以做这样的事情:

foreach ($thread->getSliceOfMessages(0, 10) as $message) {
echo $message->getContent();
}

如您所见,这非常简单!

另一个优点是内容只能以加密形式存在于 Message 实体中。您永远不会不小心将未加密的内容存储在数据库中。

关于php - doctrine2 额外延迟获取关联,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24515984/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com