假设我有一个Doctrine(版本2)实体,如下所示:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* User
*
* @ORM\Table(name="users")
* @ORM\Entity
*/
class User {
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=50, nullable=false)
*/
private $name;
/**
* @var string
*
* @ORM\Column(name="group_id", type="string", length=6, nullable=true)
*/
private $groupId;
// Getters and setters...
}
现在,我想管理User
与Group
的关系,但与某些条件,例如:
NULL
的{{1}}不存在,则返回\AppBundle\Entity\Group
(或Group
的某种骨架/模板,其中包含未从数据库加载的固定值),即使它设置为一个值(数据库中没有设置密钥限制以防止此行为),因此需要进行某种验证/检查users.group_id
时,Group
醇>
我正在阅读Google,我对如何实现正确感到困惑(与Doctrine / Symfony方式一致)。
我可以将 ManyToOne 添加到实体的类关系中,如下所示:
$user->getGroup()
但是如何防止由不存在的外键引起的异常呢?即我希望检索用户的组详细信息,当它在我的数据库中可用时,但是当它不是时,我不希望 Doctrine 引发异常,导致我的应用程序崩溃。
人们说在/**
* @var \AppBundle\Entity\Group
*
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\Group")
* @ORM\JoinColumns({
* @ORM\JoinColumn(name="group_id", referencedColumnName="id")
* })
*/
private $group;
内使用实体管理器是very bad practice,我同意。但我很担心为此目的使用Proxy Objects或Inheritance Mapping。
似乎使用Entity
可能就是这样,但我找不到任何有关如何在 Doctrine2 中正确实现它们的强大文档。
如果可以,请帮忙。在 Kohana 中它是如此,如此简单(但这种方式还不成熟)。
修改
@Massimiliano Fedel建议尝试在Models
方法中捕获异常,最终使不存在的组返回User::getGroup()
。
所以我提交了这段代码:
NULL
不幸的是,似乎异常无法通过这种方式获取,因为框架以/**
* Get group
*
* @return \AppBundle\Entity\Group
*/
public function getGroup() {
try {
$group = $this->group;
} catch (\Doctrine\ORM\EntityNotFoundException $e) {
$group = null;
}
return $group;
}
退出:
Doctrine\ORM\EntityNotFoundException
编辑2:
下面您可以找到Entity of type 'AppBundle\Entity\Group' for IDs id(999) was not found
流的一些基础架构,即为什么我无法确保所有User
在我的数据库中都可用Users
。
http://oi64.tinypic.com/1sfno5.jpg
答案 0 :(得分:1)
1)您是否尝试在“组”的getter方法中捕获异常?这样你就可以捕获异常,并在发生异常时返回“null”。
2)从doctrine 2.1文档开始:“默认情况下,关联被标记为Lazy,这意味着关联的整个集合对象在第一次访问时就被填充了。” http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/tutorials/extra-lazy-associations.html
答案 1 :(得分:1)
那么,如果要创建一个服务,那就是将你的CRUD操作包装在User
实体上,让我们说UserService
?这样,您可以保留group_id
,并添加未管理的group
字段(无注释,不会保留到数据库)。
UserService
将采用getGroup
方法,将User
作为参数,然后检索他的group_id
并使用EntityManager
(或确实{{} 1}})获取GroupService
实体,如果没有找到,您将返回Group
,否则您将返回的null
设置为实体的非托管字段,因此您不要下次不得不再次拿到它。
答案 2 :(得分:0)
好的,首先,您的User
- Group
关系未正确定义。您绝对需要添加 ManyToOne 关系:
/**
* @var \AppBundle\Entity\Group
*
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\Group")
* @ORM\JoinColumns({
* @ORM\JoinColumn(name="group_id", referencedColumnName="id")
* })
*/
private $group;
有关此事的一些说法。在JoinColumns
子句中,默认情况下,关系是可空的,这意味着您可以在生成的NULL
列(Doctrine将为您创建的连接列)中使用group_id
而不是外键)。
如果你想拥有一个不可为空的关系,那么定义必须改为:
/**
* @var \AppBundle\Entity\Group
*
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\Group")
* @ORM\JoinColumns({
* @ORM\JoinColumn(name="group_id", referencedColumnName="id", nullable=false)
* })
*/
private $group;
因此,默认情况下,您可以为NULL
设置User
组。一旦正确定义,就您的问题而言:
1
如果您的应用程序已正确创建,那么您应该永远不会得到与尚未提交到数据库的User
相关的Group
。在保持Group
本身之前,必须以某种方式从Doctrine获取与User
绑定的每个User
。无论是在应用程序内还是从表单中。如果您需要在持久化Group
之前验证User
是否存在,则必须通过100%确定您使用的任何Group
来自数据库来解决上游问题
在这个特定的上下文中,setGroup
setter应该已经确保提供的内容确实是Group
实体(如果不是你应该添加一个typehint),并且Doctrine已经确保该实体已经存在在数据库中。我同意Doctrine错误可能会变得很难看,但是交付的产品不应该包括任何错误。
了解Symfony验证通常与表单一起使用,目的是验证最终用户引入的潜在违规,而不是开发人员(通常)。
2
如上所示,通过正确定义的关系,Doctrine会自动为您处理此问题。默认情况下,所有Doctrine关系都是懒惰加载的。
此外:
/**
* Get group
*
* @return \AppBundle\Entity\Group
*/
public function getGroup() {
try {
$group = $this->group;
} catch (\Doctrine\ORM\EntityNotFoundException $e) {
$group = null;
}
return $group;
}
你不需要这样做。删除它。
答案 3 :(得分:0)
考虑到您的具体用例,最好的选择是Doctrine event subscribers。
请注意,因为它们与常规的Symfony event subscribers是分开的。后者与通用的Symfony工作流有关,前者与Doctrine特有。
具体来说,postLoad event就是您所需要的。这里的想法是:
group
属性添加到User
,并使用相应的typehinted getter和setter MyBundle\Entity\EventSubscriber\UserSubscriber
像这样创建postLoad
方法:
public function postLoad(LifecycleEventArgs $args)
{
$user = $args->getEntity();
if ($user instanceof User && $user->getGroupId()) {
$r = $this->em->getRepository('MyBundle:Group');
$group = $r->find($user->getGroupId());
if ($group instanceof Group) {
$user->setGroup($group);
}
// If not an instance of Group, no group can be found: do nothing and leave it NULL
}
}
只要从管理器加载Doctrine实体,就会调用postLoad
方法,这意味着通过Doctrine找到的所有实体都将尽可能填充其组。
如果你想延迟加载......首先,你确定要这个吗? User
组通常用于Symfony应用程序的大多数页面 - 例如,在授权检查中。如果在每个页面都加载了组,那么它确实是不值得的。
如果你绝对需要这个,你可以使用Doctrine代理工厂服务并使用代理而不是实际实体填充你的User
(不确定这是如何工作的):
$group = $this->em->getProxyFactory()->getProxy('MyBundle:Group', $user->getGroupId());
$user->setGroup($group);
在这种情况下,你必须保留以下内容,因为如果id不存在,某些加载尝试将失败:
/**
* Get group
*
* @return \AppBundle\Entity\Group
*/
public function getGroup()
{
static $loaded = false;
if (!$loaded) {
try {
$this->group;
} catch (\Doctrine\ORM\EntityNotFoundException $e) {
$this->group = null;
}
$loaded = true;
}
return $this->group;
}
我调整了Jan Mares的原始代码,因为每次调用getGroup()
Doctrine都会尝试加载Group
。