在Doctrine中,如何为实体

时间:2016-02-12 13:33:26

标签: php doctrine-orm doctrine

假设我有一个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...
}

现在,我想管理UserGroup的关系,与某些条件,例如:

  1. 如果NULL的{​​{1}}不存在,则返回\AppBundle\Entity\Group(或Group的某种骨架/模板,其中包含未从数据库加载的固定值),即使它设置为一个值(数据库中没有设置密钥限制以防止此行为),因此需要进行某种验证/检查
  2. 在致电users.group_id 时,
  3. 延迟加载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 ObjectsInheritance 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在我的数据库中都可用Usershttp://oi64.tinypic.com/1sfno5.jpg

4 个答案:

答案 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就是您所需要的。这里的想法是:

  1. group属性添加到User,并使用相应的typehinted getter和setter
  2. 创建名为MyBundle\Entity\EventSubscriber\UserSubscriber
  3. 的自定义Doctrine订阅者
  4. 使用Doctrine实体管理器服务
  5. 注入它
  6. 像这样创建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
        }
    }
    
  7. 只要从管理器加载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