Doctrine反面的collection字段是随机填充的

Doctrine inverse side's collection field is randomly filled

我有以下两个实体 address 和 user 。 在我的一个控制器中我有这个功能:

public function initAddressAction($idUser)
{
    $em = $this->getDoctrine()->getManager();
    $address = new Address();

    /** @var User $user*/
    $user= $em->getRepository('AppBundle:User' )->find($idUser);
    if ($user!== null) {
        $address->setUser($user); 
            dump($user); // #1
            $addresses = $user->getAddresses()->toArray();
            dump($user);die; // #2
            ...} 

我的问题是为什么第一个转储在地址字段中打印了一个空数组的用户对象:

#collection: Doctrine\Common\Collections\ArrayCollection {#9487 ▼

  -elements: []`

鉴于第二个转储在地址字段中打印具有非空数组集合的用户对象(该数组中实际上有一个地址):

#collection: Doctrine\Common\Collections\ArrayCollection {#9487 ▼

  -elements: array:1 [▼

    0 => App\Entity\address{#81625 ▼`

用户:

/**
 * @ORM\OneToMany(targetEntity="App\Entity\Address", mappedBy="user")
 */
private $addresses;

/**
 * Set addresses
 *
 * @param Collection $addresses
 */
public function setAddresses($addresses)
{
    $this->addresses= $addresses;
}

/**
 * Get addresses
 *
 * @return \Doctrine\Common\Collections\Collection
 */
public function getAddresses()
{
    return $this->addresses;
}

/**
 * Add address
 *
 * @param Address $address
 * @return User
 */
public function addAddress(Address$address)
{
    if (!$this->addresses->contains($address)) {
        $this->addresses[] = $address;
    }

    return $this;
}

/**
 * Remove address
 *
 * @param Address $address
 */
public function removeAddress(Address $address)
{
    $this->addresses->removeElement($address);
}

地址:

/**
 * @ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="adresses")
 * @ORM\JoinColumn(name="id_user", referencedColumnName="id_user", nullable=false)
 */
private $user

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

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

我已经通过 Symfony 制造商包生成了一个用户和一个地址 class。

用户:

<?php

namespace App\Entity;

use App\Repository\UserRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass=UserRepository::class)
 * @ORM\Table(name="`user`")
 */
class User
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $name;

    /**
     * @ORM\OneToMany(targetEntity=Address::class, mappedBy="userField")
     */
    private $addresses;

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

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

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }

    /**
     * @return Collection<int, Address>
     */
    public function getAddresses(): Collection
    {
        return $this->addresses;
    }

    public function addAddress(Address $address): self
    {
        if (!$this->addresses->contains($address)) {
            $this->addresses[] = $address;
            $address->setUserField($this);
        }

        return $this;
    }

    public function removeAddress(Address $address): self
    {
        if ($this->addresses->removeElement($address)) {
            // set the owning side to null (unless already changed)
            if ($address->getUserField() === $this) {
                $address->setUserField(null);
            }
        }

        return $this;
    }
}

地址:

<?php

namespace App\Entity;

use App\Repository\AddressRepository;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass=AddressRepository::class)
 */
class Address
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity=User::class, inversedBy="addresses")
     * @ORM\JoinColumn(nullable=false)
     */
    private $userField;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $name;

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

    public function getUserField(): ?User
    {
        return $this->userField;
    }

    public function setUserField(?User $userField): self
    {
        $this->userField = $userField;

        return $this;
    }

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }
}

失败示例:

试试这个(例如在控制器中):

    // get address 1 (this is attached to user 1 in the database)
    $address1 = $addressRepo->findOneBy(['id' => 1]);
    dump($address1);

这是 Address 1,它的 User 对象还没有初始化,但这对这个例子来说并不重要。

    // get user 2 (user 2 only has address 2 in the database)
    $user2 = $userRepo->findOneBy(['id' => 2]);
    dump($user2);

这是用户 2,请注意地址数组尚未初始化(这很重要!)。

    // set user 2 onto address 1
    $address1->setUserField($user2);
    dump($address1);

现在地址 1 有用户 2。

    dump($user2);

但是用户 2 没有地址 1。

    // load user 2's addresses from the database
    $user2->getAddresses()->toArray();
    dump($user2);

用户 2 现在从数据库中加载了地址 2(尽管仍然没有地址 1)。

用$userField->addAddress($this)

如果添加 $userField->addAddress($this); 行:

public function setUserField(?User $userField): self
{
    $this->userField = $userField;
    $userField->addAddress($this);

    return $this;
}

并执行相同的步骤:

    // get address 1 (this is attached to user 1 in the database)
    $address1 = $addressRepo->findOneBy(['id' => 1]);
    dump($address1);

    // get user 2  (user 2 only has address 2 in the database)
    $user2 = $userRepo->findOneBy(['id' => 2]);
    dump($user2);

    // set user 2 onto address 1
    $address1->setUserField($user2);
    dump($address1);
    dump($user2);

用户 2 现在有地址 1。将地址添加到用户也会触发从数据库加载现有地址,因此它现在也有地址 2。

    // load user 2's addresses from the database
    $user2->getAddresses()->toArray();
    dump($user2);

(地址已经加载,没有变化。)

具有持久化和刷新

并且如果您 添加 $userField->addAddress($this); 行,但添加 persist & flush:

    // get address 1 (this is attached to user 1 in the database)
    $address1 = $addressRepo->findOneBy(['id' => 1]);
    dump($address1);

    // get user 2  (user 2 only has address 2 in the database)
    $user2 = $userRepo->findOneBy(['id' => 2]);
    dump($user2);

    // set user 2 onto address 1
    $address1->setUserField($user2);
    dump($address1);
    dump($user2);

用户 2 没有地址 1。

    // persist address 1
    $em->persist($address1);
    $em->flush($address1);

    // load user 2's addresses from the database
    $user2->getAddresses()->toArray();
    dump($user2);

现在用户 2 从数据库中加载了地址 1 和地址 2。