【问题标题】:Doctrine OneToOne identity through foreign entity exception on flushDoctrine OneToOne 身份通过刷新时的外国实体异常
【发布时间】:2015-05-22 17:02:00
【问题描述】:

我有 UserUserProfile OneToOne 相关的 Doctrine ORM 实体。它们应该始终成对存在,没有UserProfile,就不应该有User

用户应该从自动增量获取它的 id,而 UserProfile 应该有用户的 id。所以他们都应该有相同的 id 并且没有其他列来建立关系 (Doctrine docs: Identity through foreign Entities)。

UserProfile 的 id 同时是主键 (PK) 和外键 (FK)。

我设法设置了它,但它要求先保存用户,然后才在单独的步骤中创建和保存 UserProfile。

我想要的是在构造函数中始终使用 User 创建 UserProfile,但如果我这样做,我会得到这个异常:

Doctrine\ORM\ORMInvalidArgumentException: The given entity of type 'AppBundle\Entity\UserProfile' (AppBundle\Entity\UserProfile@0000000052e1b1eb00000000409c6f2c) has no identity/no id values set. It cannot be added to the identity map.

请看下面的代码——它可以工作,但不是我想要的方式。 php cmets 显示了我想要实现的目标。

Test.php:

/**
 * It works, both saving and loading.
 * BUT, it requires that I create and save UserProfile 
 * in a separate step than saving User step.
 */

// create and save User
$user = new User();
$objectManager->persist($user);
$objectManager->flush();

// create and save UserProfile (this should be unnecessary)
$user->createProfile()
$objectManager->flush();

User.php:

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="AppBundle\Entity\UserRepository")
 * @ORM\Table(name="users")
 */
class User
{
    /**
     * @var int
     *
     * @ORM\Column(name="uid", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * It's NULL at first, I create it later (after saving User).
     * 
     * @var UserProfile|null
     *
     * @ORM\OneToOne(targetEntity="UserProfile", mappedBy="user", cascade="persist")
     */
    private $profile = null;

    public function __construct()
    {
        // I want to create UserProfile inside User's constructor,
        // so that it is always present (never NULL):
        //$this->profile = new UserProfile($this);

        // but this would give me error:
        //
        // Doctrine\ORM\ORMInvalidArgumentException: 
        // The given entity of type 'AppBundle\Entity\UserProfile' 
        // (AppBundle\Entity\UserProfile@0000000058af220a0000000079dc875a)
        // has no identity/no id values set. It cannot be added to the identity map.
    }

    public function createProfile()
    {
        $this->profile = new UserProfile($this);
    }   
}

UserProfile.php:

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="profiles")
 */
class UserProfile
{
    /**
     * – UserProfile's "uid" column points to User's "uid" column
     * – it is PK (primary key)
     * - it is FK (foreign key) as well
     * – "owning side"
     *
     * @var User
     *
     * @ORM\Id
     * @ORM\OneToOne(targetEntity="User", inversedBy="profile")
     * @ORM\JoinColumn(name="uid", referencedColumnName="uid", nullable=false)
    */
    private $user;

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

测试应用:https://github.com/MacDada/DoctrineOneToOneTest

【问题讨论】:

  • Id 是一个数据库的东西,不应该成为模型中的一个问题。放弃他们需要相同主键值的想法。或者如果做不到这一点,请放弃一个需要是自动的而另一个不需要的概念。
  • @Cerad 我正在构建一个遗留应用程序——数据库就是它的样子。我需要将 Doctrine 调整为当前模式,反之亦然……另外,上面的代码有效。 Doctrine 可以处理作为主键的外键。它只是不能一次刷新实体。或者至少我正在寻找答案,如何配置 Doctrine 以便它可以。
  • 很公平。如果您进行搜索,那么您会发现同一问题的数百个变体。答案都一样。
  • 看来您需要更改关系的拥有方。

标签: php symfony doctrine-orm doctrine


【解决方案1】:

请记住,实际对象需要由 EntityManager 保存。 仅仅将类作为对另一个类的引用不会使 entityManager 意识到这两个类都存在。

您应该将实际的 userProfile 持久化到 EntityManager 以便能够保存关系。

因为负面评论而更新:

请阅读 Doctrine 文档...你应该坚持下去!

以下示例是本章用户评论示例的扩展。假设在我们的应用程序中,每当他写下他的第一条评论时都会创建一个用户。在这种情况下,我们将使用以下代码:

<?php
$user = new User();
$myFirstComment = new Comment();
$user->addComment($myFirstComment);

$em->persist($user);
$em->persist($myFirstComment);
$em->flush();

即使您保留一个包含我们的新评论的新用户,如果您删除对 EntityManager#persist($myFirstComment) 的调用,此代码也会失败。原则 2 不会将持久化操作级联到所有新的嵌套实体。

更新 2: 我了解您希望完成什么,但根据设计,您不应该在您的实体中移动此逻辑。实体应该代表尽可能少的逻辑,因为它们代表您的模式。

话虽如此,我相信你可以像这样完成你想做的事情:

$user = new User();
$profile = $user->getProfile();
$objectManager->persist($user);
$objectManager->persist($profile);
$objectManager->flush();

但是,您应该考虑创建一个包含 entitymanager 的 userService,并让其负责创建、链接和持久化 user + userProfile 实体。

【讨论】:

  • 不,由于cascade="persist",配置文件匹配到持久化。如果我将其删除并“手动”保留配置文件,问题仍然存在。
  • 它没有帮助,我已经尝试过了(现在还要再尝试一次,以完全确定)。同样的错误。
  • ./app/console cache:clear &amp;&amp; sudo /etc/init.d/nginx restart &amp;&amp; sudo /etc/init.d/php5-fpm restart &amp;&amp; sudo /etc/init.d/memcached restart &amp;&amp; ./app/console cache:clear
  • 您能分享一下您是如何尝试进行单独持久化的吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-25
  • 1970-01-01
  • 1970-01-01
  • 2020-08-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多