Doctrine OneToOne身份通过外部实体异常刷新

Mac*_*ada 5 php doctrine symfony doctrine-orm

UserUserProfileOneToOne相关的Doctrine ORM实体.它们应该总是存在一对,应该没有User没有UserProfile.

用户应该从autoincrement获取其id,而UserProfile应该具有User的id.所以它们都应该具有相同的id,并且没有其他列来建立关系(Doctrine docs:Identity to foreign Entities).

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

我设法设置它,但它要求首先保存用户,并且只有在以后创建UserProfile并在单独的步骤中保存.

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

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评论显示了我想要实现的目标.

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();
Run Code Online (Sandbox Code Playgroud)

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);
    }   
}
Run Code Online (Sandbox Code Playgroud)

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;
    }    
}
Run Code Online (Sandbox Code Playgroud)

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

Arm*_*and 1

请记住,实际对象需要由 EntityManager 保存。仅仅将该类作为对另一个类的引用并不能让实体管理器知道这两个类都存在的事实。

您应该将实际的用户配置文件保存到 EntityManager 中以便能够保存关系。

由于负面评论而更新:

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

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

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

$em->persist($user);
$em->persist($myFirstComment);
$em->flush();
Run Code Online (Sandbox Code Playgroud)

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

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

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

$user = new User();
$profile = $user->getProfile();
$objectManager->persist($user);
$objectManager->persist($profile);
$objectManager->flush();
Run Code Online (Sandbox Code Playgroud)

但是,您应该考虑创建一个包含实体管理器的 userService ,并使其负责创建、链接和保留用户 + userProfile 实体。