Doctrine:在持久/刷新之前获取自动增量ID

时间:2014-07-15 14:39:52

标签: symfony orm doctrine fixtures

我在Doctrine-Fixtures遇到了麻烦。我想在另一个实体中添加用户和电子邮件,但与用户有关。所以这是我的过程:

    // Create user
    $user1 = new User();

    // Create user email and add the foreign key to the user
    $user1Mail = new UserEmail();
    $user1Mail->setEmail('test@example.com');
    $user1Mail->setUser($user1);

    // Add attributes
    $user1->setEmail($user1Mail);
    // ...

    $manager->persist($user1Mail);
    $manager->persist($user1);
    $manager->flush();

我在保留前添加了$user1Mail->setUser($user1);中的电子邮件用户,但问题是,用户没有主键 - > ID(自动增量)。因此,要创建电子邮件和用户之间的关系,用户需要具有要引用的主键。

我知道之前创建唯一令牌的解决方案并将其设置为用户的ID,但我认为这是一种令人不舒服的方式,因为我需要检查用户ID是否已被使用。

有没有一种好方法可以解决这个问题?

//编辑: 这是必要的实体关系:

用户:

class User implements UserInterface, \Serializable
{
    // ...

    /**
     * @var Application\CoreBundle\Entity\UserEmail
     * 
     * @ORM\OneToOne(
     *  targetEntity="UserEmail",
     *  cascade={"persist"}
     * )
     * @ORM\JoinColumn(
     *  name="primaryEmail",
     *  referencedColumnName="id",
     *  nullable=false,
     *  onDelete="restrict"
     * )
     */
    private $email;

    // ...

}

USEREMAIL:

class UserEmail
{

    // ...

    /**
     * @var Application\CoreBundle\Entity\User
     * @ORM\ManyToOne(
     *  targetEntity="User",
     *  cascade={"persist", "remove"}
     * )
     * @ORM\JoinColumn(
     *  name="userID",
     *  referencedColumnName="id",
     *  nullable=false
     * )
     */
    private $user;


    // ...

}

如您所见,如果添加用户,您还必须添加UserEmail。但UserEmail要求已设置userID,但仅在将用户持久保存到数据库时才设置。我怎样才能实现它的修复?

2 个答案:

答案 0 :(得分:1)

我发现您的UserUserEmail之间存在OneToOne关联,而UserEmailUser之间存在ManyToOne关联,我觉得很奇怪,这些是2个单独的关联

我认为您宁愿拥有单一的双向OneToMany关联:

class User implements UserInterface, \Serializable
{
    // ...

    /**
     * @var ArrayCollection
     * 
     * @ORM\OneToMany(targetEntity="UserEmail", mappedBy="user", cascade={"persist", "remove"})
     */
    private $emails;

    public function __construct()
    {
        $this->emails = new ArrayCollection();
    }

    /**
     * @param UserEmail $email
     */
    public function addEmail(UserEmail $email)
    {
        $this->emails->add($email);
        $email->setUser($this);
    }

    /**
     * @param UserEmail $email
     */
    public function removeEmail(UserEmail $email)
    {
        $this->emails->removeElement($email);
        $email->setUser(null);
    }

    /**
     * @return UserEmail[]
     */
    public function getEmails()
    {
        return $this->emails->toArray();
    }

    // ...
}

class UserEmail
{
    // ...

    /**
     * @var User
     *
     * @ORM\ManyToOne(targetEntity="User", inversedBy="emails")
     * @ORM\JoinColumn(name="userID", referencedColumnName="id", nullable=FALSE)
     */
    private $user;

    /**
     * @param User $user
     */
    public setUser(User $user = null)
    {
        $this->user = $user;
    }

    /**
     * @return User[]
     */
    public function getUser()
    {
        return $this->user;
    }

    // ...
}

我在cascade上加了User::$emails,因此对User的任何更改都会向UserEmail级联。这将使他们更容易管理。

使用它看起来像这样:

$email = new UserEmail();

$user = new User();
$user->addEmail($email);

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

关于外键

Doctrine将为您管理实体的外键。使用关联时,无需在实体上手动设置它们。

主电子邮件

我个人会向UserEmail添加一个属性以将其标记为主要属性。您需要在实体中使用更多逻辑,但管理它们将变得毫不费力。

以下是您需要的其他代码:

class User
{

    // ...

    /**
     * @param UserEmail $email
     */
    public function addEmail(UserEmail $email)
    {
        $this->emails->add($email);
        $email->setUser($this);

        $this->safeguardPrimaryEmail();
    }

    /**
     * @param UserEmail $email
     */
    public function removeEmail(UserEmail $email)
    {
        $this->emails->removeElement($email);
        $email->setUser(null);

        $this->safeguardPrimaryEmail();
    }

    /**
     * @param UserEmail $email
     */
    public function setPrimaryEmail(UserEmail $newPrimaryEmail)
    {
        if (!$this->emails->contains($newPrimaryEmail)) {
            throw new \InvalidArgumentException('Unknown email given');
        }

        foreach ($this->emails as $email) {
            if ($email === $newPrimaryEmail) {
                $email->setPrimary(true);
            } else {
                $email->setPrimary(false);
            }
        }
    }

    /**
     * @return UserEmail|null
     */
    public function getPrimaryEmail()
    {
        foreach ($this->emails as $email) {
            if ($email->isPrimary()) {
                return $email;
            }
        }

        return null;
    }

    /**
     * Make sure there's 1 and only 1 primary email (if there are any emails)
     */
    private function safeguardPrimaryEmail()
    {
        $primaryFound = false;

        foreach ($this->emails as $email) {
            if ($email->isPrimary()) {
                if ($primaryFound) {
                    // make sure there's no more than 1 primary email
                    $email->setPrimary(false);
                } else {
                    $primaryFound = true;
                }
            }
        }

        if (!$primaryFound and !$this->emails->empty()) {
            // make sure there's at least 1 primary email
            $this->emails->first()->setPrimary(true);
        }
    }

    // ...
}

class UserEmail
{
    // ...

    /**
     * @var boolean
     *
     * @ORM\Column(type="boolean")
     */
    private $isPrimary = false;

    /**
     * @internal Use 
     * @param bool $isPrimary
     */
    public function setPrimary($isPrimary)
    {
        $this->isPrimary = (bool)$isPrimary;
    }

    /**
     * @return bool
     */
    public function isPrimary()
    {
        return $this->isPrimary;
    }

    // ...
}

您可能会注意到safeguardPrimaryEmail()。这将确保在添加/删除电子邮件时主标记保持一致。

使用它非常简单:

  • 默认情况下,创建的电子邮件不是主电子邮件。
  • 如果是添加给用户的第一封电子邮件,它将自动成为主要电子邮件。
  • 此外,添加的电子邮件将主要。
  • 删除主电子邮件后,剩下的第一封电子邮件将成为主电子邮件。
  • 您可以致电User::setPrimaryEmail()
  • 手动设置其他主电子邮件

这个概念可能有很多变化,所以只需将其视为一个示例,并根据您的需要进行优化。

答案 1 :(得分:0)

这是因为Doctrine会在插入数据库时​​生成实体ID。 您可以使用额外的flush()

来完成此操作
$user1 = new User();
$manager->persist($user1);
$manager->flush();

// Create user email and add the foreign key to the user
$user1Mail = new UserEmail();
$user1Mail->setEmail('test@example.com');
$user1Mail->setUser($user1);

// Add attributes
$user1->setEmail($user1Mail);
// ...

$manager->persist($user1Mail);
$manager->persist($user1);
$manager->flush();

或者您可以将User课程中的电子邮件映射设置为级联持久。 这意味着如果将一个新的非持久化对象添加到该对象,那么新对象也将被保存。

我不知道entites的确切结构,但它看起来像

/**
 * @ORM\OneToOne(targetEntity="user", cascade={"persist"})
 * @ORM\JoinColumn(name="user_email_id", referencedColumnName="id")
 */
private $userEmail

因此,如果您向用户设置新电子邮件,则如果您保留User实体,则会自动保留该电子邮件。

如果有效,我更喜欢第二种方法。我希望它会有所帮助。

Doctrine reference: Transitive persistence / Cascade Operations