【问题标题】:Doctrine2, pass Id or Object?Doctrine2,通过 Id 还是 Object?
【发布时间】:2012-07-10 14:46:17
【问题描述】:

我不明白为什么我可以使用一些 Entity 对象设置 Id,而对于其他对象我得到一个错误并告诉我 Id 不能为空,我必须传递一个对象。

例如:

$log = new Log();
$log->setTypeId(1);
$log->setUserId(1);
$entityManager->persist($log);
$entityManager->flush();

如果我尝试上面的代码,我会收到错误消息:违反完整性约束:1048 列 'user_id' 不能为空。我必须首先创建 Type Object 和 de User 对象并传递它们:

$log->setType($TypeObject)
$log->setUser($UserObject)

但是对于其他实体对象我直接赋值没有问题,这是为什么呢?

这是我的实体日志:

<?php
/**
 * @Entity
 * @Table(name="log")
 * @HasLifecycleCallbacks
 */
class Log
{
    /**
     * @var type 
     * @Id
     * @Column(type="integer")
     * @GeneratedValue
     */
    protected $id;

     /**
     *
     * @var type 
     * @Column(type="integer")
     */
    protected $user_id;

     /**
     *
     * @var type 
     * @Column(type="integer")
     */
    protected $type_id;

     /**
     *
     * @var type 
     * @Column(type="datetime")
     */
    protected $created;

    /**
     *
     * @var type 
     * @ManyToOne(targetEntity="User", inversedBy="logs")
     */
    protected $user;

    /**
     *
     * @ManyToOne(targetEntity="Type", inversedBy="logs")
     */
    protected $type;

    public function getId()
    {
        return $this->id;
    }

    public function getUserId()
    {
        return $this->user_id;
    }

    public function getTypeId()
    {
        return $this->type_id;
    }

    public function getCreated()
    {
        return $this->created;
    }

    public function setUserId($userId)
    {
        $this->user_id = $userId;
    }

    public function setTypeId($typeId)
    {
        $this->type_id = $typeId;
    }

    public function setCreated($created)
    {
        $this->created = $created;
    }

    public function setUser($user)
    {
        $this->user = $user;
    }

    public function setType($type)
    {
        $this->type = $type;
    }

    /**
     * @PrePersist
     */
    public function prePersist()
    {
        $this->setCreated(new DateTime());
    }

}
?>

【问题讨论】:

  • 很高兴有帮助。这使我进入了 1K 标记。呜呼。

标签: php orm doctrine-orm


【解决方案1】:

现有的答案从来没有让我满意。在许多有效的场景中,加载一个对象只是为了定义关系,而 FK 已经很方便了,但根本没有任何意义。

更好的解决方案是使用 Doctrine 的 EntityManager 的 getRefrence 方法。

Reference Proxies...

EntityManager#getReference($entityName, $identifier) 方法让 您获得对已知标识符的实体的引用, 无需从数据库中加载该实体。这很有用,因为 例如,作为性能增强,当您想建立一个 关联到您拥有标识符的实体。你可以 只需这样做:

<?php
  // $em instanceof EntityManager, $cart instanceof MyProject\Model\Cart
  // $itemId comes from somewhere, probably a request parameter
  $item = $em->getReference(\MyProject\Model\Item::class, $itemId);
  $cart->addItem($item);

这个问题首次发布时可能不可用 - 我不知道。

【讨论】:

  • 我不知道这种方法,它看起来正是原始海报所要求的。
  • 这是完美的。这是新功能吗?
  • 我试图确定这一点,但不确定何时可用(2.0 > ?)。去年添加的。
  • 最好在 Doctrine 2 标签 (stackoverflow.com/documentation/doctrine2) 下的新文档部分中添加有关此功能的注释
  • 严,谢谢你的建议。老实说,我不知道这个 SO 功能,我需要阅读。如果您愿意,请随意这样做。
【解决方案2】:

编辑

我在 Doctrine2 的网站上找到了这个声明。这是您在编写模型时可能需要遵循的最佳实践。

Doctrine2 Best Practices

25.9。不要将外键映射到实体中的字段

外键在对象模型中没有任何意义。外键是关系数据库建立关系的方式。您的对象模型通过对象引用建立关系。因此,将外键映射到对象字段会严重地将关系模型的细节泄漏到对象模型中,这是您真正不应该做的事情

编辑

Doctrine 将您的对象映射到它们各自的 Id。

你在这里做的有点多余。

你基本上已经两次告诉教义同样的事情了。

您已经告诉它它有一个“user_id”列并且它也有一个用户对象,它们是相同的。但是教义已经可以根据日志类里面有一个用户对象这一事实,猜到这个关系会有一个user_id列。

您应该简单地执行以下操作

<?php
/**
 * @Entity
 * @Table(name="log")
 * @HasLifecycleCallbacks
 */
class Log
{
    /**
     * @var type 
     * @Id
     * @Column(type="integer")
     * @GeneratedValue
     */
    protected $id;

     /**
     *
     * @var type 
     * @Column(type="datetime")
     */
    protected $created;

    /**
     *
     * @var type 
     * @ManyToOne(targetEntity="User", inversedBy="logs")
     */
    protected $user;

    /**
     *
     * @ManyToOne(targetEntity="Type", inversedBy="logs")
     */
    protected $type;

    public function getId()
    {
        return $this->id;
    }

    public function getCreated()
    {
        return $this->created;
    }

    public function setCreated($created)
    {
        $this->created = $created;
    }

    public function setUser($user)
    {
        $this->user = $user;
    }

    public function setType($type)
    {
        $this->type = $type;
    }

    /**
     * @PrePersist
     */
    public function prePersist()
    {
        $this->setCreated(new DateTime());
    }

}

Doctrine 会自己担心 user_id 和 type_id。你不必担心。通过这种方式,您可以使用完整的对象,使其更容易编程,而不必担心 id。教义会处理这个问题。

如果您只有一个 id,因为这是您在前端使用的,那么只需使用 Entitymanager 获取与该 id 关联的对象。

$user = $em->getEntity( 'User', $idFromWeb );
$log = new Log();
$log->setUser( $user );

【讨论】:

  • 从数据库中检索 $user 似乎是一种开销。如果日志条目只需要 UserId,为什么要检索整个用户记录。有什么办法可以避免吗?
  • Mubix,你是对的。但是在这个请求期间,您可能已经获取了用户实体以获取他们的偏好或验证密码信息或其他内容。 Doctrine2 也只获取一次实体。如果您多次调用 getEntity,Doctrine2 将继续返回相同的对象而不返回数据库。
  • 在你正在做一些批处理操作并加载所有引用的实体只是为了让 Doctrine 抓住 FK 的场景中?有没有办法解决这个问题?我觉得打所有这些额外的电话很脏。将编写原始 PDO 语句或不使用映射,只需从控制器代码通过存储库加载该实体。
  • Doctrine 是一个 ORM,在批处理操作的情况下它可能会更慢。它使用 UnitOfWork 模式来提高批量更新的效率,但如果您真的担心速度,通常最好自己编写语句,尽管这需要更多时间。这是 ORM 的缺点之一。
  • 我知道这是教条的处事方式。然而,这给我带来了噩梦,因为开发人员正在循环加载整个对象,而整个应用程序为了获得 FK 就超级慢。
猜你喜欢
  • 1970-01-01
  • 2016-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多