ManyToMany 关系中的多个连接
Multiple joins in a ManyToMany relation
我有 3 个实体,User、Parking 和 Voiture。
User 与 parking 有 ManyToMany 关系,而 Parking 与 voiture 有 OneToMany reamtion。
我想做什么:
获取属于当前用户相关的所有停车场的汽车(voitures)
我是怎么做的:
正在使用 querybuilder,但我仍然不知道如何让它工作
这是我的实体
实体用户:
<?php
/**
* @ORM\Entity
* @UniqueEntity(fields="username", message="Username already taken")
*/
class User implements UserInterface
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
public function getId()
{
return $this->id;
}
/**
* @ORM\Column(type="string", length=191, unique=true)
* @Assert\NotBlank()
*/
private $username;
/**
* @ORM\ManyToMany(targetEntity="App\Entity\Parking", mappedBy="agents")
*/
private $parkings;
public function __construct()
{
$this->parkings = new ArrayCollection();
}
/**
* @return Collection|Parking[]
*/
public function getParkings(): Collection
{
return $this->parkings;
}
public function addParking(Parking $parking): self
{
if (!$this->parkings->contains($parking)) {
$this->parkings[] = $parking;
$parking->addAgent($this);
return $this;
}
return $this;
}
public function removeParking(Parking $parking): self
{
if ($this->parkings->contains($parking)) {
$this->parkings->removeElement($parking);
$parking->removeAgent($this);
}
return $this;
}
}
实体停车场:
<?php
/**
* @ORM\Entity(repositoryClass="App\Repository\ParkingRepository")
*/
class Parking
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=55)
*/
private $libelle;
/**
* @ORM\ManyToMany(targetEntity="App\Entity\user", inversedBy="parkings")
*/
private $agents;
/**
* @ORM\OneToMany(targetEntity="App\Entity\Voiture", mappedBy="parking", orphanRemoval=true)
*/
private $voitures;
public function __construct()
{
$this->agents = new ArrayCollection();
$this->voitures = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
/**
* @return Collection|user[]
*/
public function getAgents(): Collection
{
return $this->agents;
}
public function addAgent(user $agent): self
{
if (!$this->agents->contains($agent)) {
$this->agents[] = $agent;
}
return $this;
}
public function removeAgent(user $agent): self
{
if ($this->agents->contains($agent)) {
$this->agents->removeElement($agent);
}
return $this;
}
/**
* @return Collection|Voiture[]
*/
public function getVoitures(): Collection
{
return $this->voitures;
}
public function addVoiture(Voiture $voiture): self
{
if (!$this->voitures->contains($voiture)) {
$this->voitures[] = $voiture;
$voiture->setParking($this);
}
return $this;
}
public function removeVoiture(Voiture $voiture): self
{
if ($this->voitures->contains($voiture)) {
$this->voitures->removeElement($voiture);
// set the owning side to null (unless already changed)
if ($voiture->getParking() === $this) {
$voiture->setParking(null);
}
}
return $this;
}
}
和实体Voiture
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass="App\Repository\VoitureRepository")
*/
class Voiture
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=200)
*/
private $matricule;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Parking", inversedBy="voitures")
* @ORM\JoinColumn(nullable=false)
*/
private $parking;
/**
* @ORM\Column(type="boolean")
*/
private $parked;
public function getId(): ?int
{
return $this->id;
}
public function getMatricule(): ?string
{
return $this->matricule;
}
public function setMatricule(string $matricule): self
{
$this->matricule = $matricule;
return $this;
}
public function getParking(): ?Parking
{
return $this->parking;
}
public function setParking(?Parking $parking): self
{
$this->parking = $parking;
return $this;
}
}
从 Voiture 实体开始,内部加入 Parking 和 User 实体使用它们的关联:
$queryBuilder = $this->getDoctrine()->getRepository('App:Voiture')->createQueryBuilder('v');
$queryBuilder->innerJoin('v.parking', 'p');
$queryBuilder->innerJoin('v.agents', 'a');
最后,您可以通过条件过滤关系:
$queryBuilder->where('a.id = :userId');
$queryBuilder->setParameter("userId", 1);
$cars = $queryBuilder->getQuery()->getResult();
或将条件放在$queryBuilder->innerJoin('v.agents', 'a', 'WITH', 'a.id = :userId');
上见doctrine inner join with condition
参考资料
- SQL join: where clause vs. on clause
我建议在 User 和 Parking 实体之间添加一个中间实体 UserParking。
所以我们在 User 和 UserParking 之间有 OneToMany 关系,Parking 和 UserParking 之间有 OneToMany 关系,而不是 User 和 Parking 之间有 ManyToMany 关系。
实体将类似于以下代码:
实体用户:
<?php
/**
* @ORM\Entity
* @UniqueEntity(fields="username", message="Username already taken")
*/
class User implements UserInterface
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
public function getId()
{
return $this->id;
}
/**
* @ORM\Column(type="string", length=191, unique=true)
* @Assert\NotBlank()
*/
private $username;
/**
* @ORM\OneToMany(targetEntity="App\Entity\UserParking", mappedBy="agent")
* @ORM\JoinColumn(nullable=true)
*/
private $user_parking;
// getter and setter
}
实体停车场:
<?php
/**
* @ORM\Entity(repositoryClass="App\Repository\ParkingRepository")
*/
class Parking
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=55)
*/
private $libelle;
/**
* @ORM\OneToMany(targetEntity="App\Entity\UserParking", mappedBy="parking")
* @ORM\JoinColumn(nullable=true)
*/
private $user_parking;
/**
* @ORM\OneToMany(targetEntity="App\Entity\Voiture", mappedBy="parking", orphanRemoval=true)
*/
private $voitures;
// getters and setters
}
实体用户停车
/**
* UserParking
*
* @ORM\Table(name="user_parking")
* @ORM\Entity(repositoryClass="App\Repository\UserParkingRepository")
*/
class UserParking
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="user_parking")
* @ORM\JoinColumn(nullable=false)
*/
private $agent;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Parking", inversedBy="user_parking")
* @ORM\JoinColumn(nullable=false)
*/
private $parking;
// getter and setter
}
和实体Voiture
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass="App\Repository\VoitureRepository")
*/
class Voiture
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=200)
*/
private $matricule;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Parking", inversedBy="voitures")
* @ORM\JoinColumn(nullable=false)
*/
private $parking;
/**
* @ORM\Column(type="boolean")
*/
private $parked;
}
因此,要获取属于当前用户相关的所有停车场的汽车(voitures),您需要:
1-获取当前用户对象。
2- 从用户对象中获取 UserParking 数组。
3- 从 UserParking 数组中获取 Parking 对象。
4- 从停车对象中获取汽车。
代码将类似于:
$em = $this->getDoctrine()->getManager();
/* Get user from the session */
$user = $this->getUser();
$userParkings = $user->getUserParking();
$parkings = [];
foreach ($userParkings as $item) {
$parking = $item->getParking();
$parkings[count($parkings)] = $parking;
}
// you can get voitures from parkings
我有 3 个实体,User、Parking 和 Voiture。 User 与 parking 有 ManyToMany 关系,而 Parking 与 voiture 有 OneToMany reamtion。
我想做什么:
获取属于当前用户相关的所有停车场的汽车(voitures)
我是怎么做的:
正在使用 querybuilder,但我仍然不知道如何让它工作
这是我的实体
实体用户:
<?php
/**
* @ORM\Entity
* @UniqueEntity(fields="username", message="Username already taken")
*/
class User implements UserInterface
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
public function getId()
{
return $this->id;
}
/**
* @ORM\Column(type="string", length=191, unique=true)
* @Assert\NotBlank()
*/
private $username;
/**
* @ORM\ManyToMany(targetEntity="App\Entity\Parking", mappedBy="agents")
*/
private $parkings;
public function __construct()
{
$this->parkings = new ArrayCollection();
}
/**
* @return Collection|Parking[]
*/
public function getParkings(): Collection
{
return $this->parkings;
}
public function addParking(Parking $parking): self
{
if (!$this->parkings->contains($parking)) {
$this->parkings[] = $parking;
$parking->addAgent($this);
return $this;
}
return $this;
}
public function removeParking(Parking $parking): self
{
if ($this->parkings->contains($parking)) {
$this->parkings->removeElement($parking);
$parking->removeAgent($this);
}
return $this;
}
}
实体停车场:
<?php
/**
* @ORM\Entity(repositoryClass="App\Repository\ParkingRepository")
*/
class Parking
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=55)
*/
private $libelle;
/**
* @ORM\ManyToMany(targetEntity="App\Entity\user", inversedBy="parkings")
*/
private $agents;
/**
* @ORM\OneToMany(targetEntity="App\Entity\Voiture", mappedBy="parking", orphanRemoval=true)
*/
private $voitures;
public function __construct()
{
$this->agents = new ArrayCollection();
$this->voitures = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
/**
* @return Collection|user[]
*/
public function getAgents(): Collection
{
return $this->agents;
}
public function addAgent(user $agent): self
{
if (!$this->agents->contains($agent)) {
$this->agents[] = $agent;
}
return $this;
}
public function removeAgent(user $agent): self
{
if ($this->agents->contains($agent)) {
$this->agents->removeElement($agent);
}
return $this;
}
/**
* @return Collection|Voiture[]
*/
public function getVoitures(): Collection
{
return $this->voitures;
}
public function addVoiture(Voiture $voiture): self
{
if (!$this->voitures->contains($voiture)) {
$this->voitures[] = $voiture;
$voiture->setParking($this);
}
return $this;
}
public function removeVoiture(Voiture $voiture): self
{
if ($this->voitures->contains($voiture)) {
$this->voitures->removeElement($voiture);
// set the owning side to null (unless already changed)
if ($voiture->getParking() === $this) {
$voiture->setParking(null);
}
}
return $this;
}
}
和实体Voiture
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass="App\Repository\VoitureRepository")
*/
class Voiture
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=200)
*/
private $matricule;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Parking", inversedBy="voitures")
* @ORM\JoinColumn(nullable=false)
*/
private $parking;
/**
* @ORM\Column(type="boolean")
*/
private $parked;
public function getId(): ?int
{
return $this->id;
}
public function getMatricule(): ?string
{
return $this->matricule;
}
public function setMatricule(string $matricule): self
{
$this->matricule = $matricule;
return $this;
}
public function getParking(): ?Parking
{
return $this->parking;
}
public function setParking(?Parking $parking): self
{
$this->parking = $parking;
return $this;
}
}
从 Voiture 实体开始,内部加入 Parking 和 User 实体使用它们的关联:
$queryBuilder = $this->getDoctrine()->getRepository('App:Voiture')->createQueryBuilder('v');
$queryBuilder->innerJoin('v.parking', 'p');
$queryBuilder->innerJoin('v.agents', 'a');
最后,您可以通过条件过滤关系:
$queryBuilder->where('a.id = :userId');
$queryBuilder->setParameter("userId", 1);
$cars = $queryBuilder->getQuery()->getResult();
或将条件放在$queryBuilder->innerJoin('v.agents', 'a', 'WITH', 'a.id = :userId');
上见doctrine inner join with condition
参考资料
- SQL join: where clause vs. on clause
我建议在 User 和 Parking 实体之间添加一个中间实体 UserParking。 所以我们在 User 和 UserParking 之间有 OneToMany 关系,Parking 和 UserParking 之间有 OneToMany 关系,而不是 User 和 Parking 之间有 ManyToMany 关系。
实体将类似于以下代码:
实体用户:
<?php
/**
* @ORM\Entity
* @UniqueEntity(fields="username", message="Username already taken")
*/
class User implements UserInterface
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
public function getId()
{
return $this->id;
}
/**
* @ORM\Column(type="string", length=191, unique=true)
* @Assert\NotBlank()
*/
private $username;
/**
* @ORM\OneToMany(targetEntity="App\Entity\UserParking", mappedBy="agent")
* @ORM\JoinColumn(nullable=true)
*/
private $user_parking;
// getter and setter
}
实体停车场:
<?php
/**
* @ORM\Entity(repositoryClass="App\Repository\ParkingRepository")
*/
class Parking
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=55)
*/
private $libelle;
/**
* @ORM\OneToMany(targetEntity="App\Entity\UserParking", mappedBy="parking")
* @ORM\JoinColumn(nullable=true)
*/
private $user_parking;
/**
* @ORM\OneToMany(targetEntity="App\Entity\Voiture", mappedBy="parking", orphanRemoval=true)
*/
private $voitures;
// getters and setters
}
实体用户停车
/**
* UserParking
*
* @ORM\Table(name="user_parking")
* @ORM\Entity(repositoryClass="App\Repository\UserParkingRepository")
*/
class UserParking
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="user_parking")
* @ORM\JoinColumn(nullable=false)
*/
private $agent;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Parking", inversedBy="user_parking")
* @ORM\JoinColumn(nullable=false)
*/
private $parking;
// getter and setter
}
和实体Voiture
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass="App\Repository\VoitureRepository")
*/
class Voiture
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=200)
*/
private $matricule;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Parking", inversedBy="voitures")
* @ORM\JoinColumn(nullable=false)
*/
private $parking;
/**
* @ORM\Column(type="boolean")
*/
private $parked;
}
因此,要获取属于当前用户相关的所有停车场的汽车(voitures),您需要:
1-获取当前用户对象。
2- 从用户对象中获取 UserParking 数组。
3- 从 UserParking 数组中获取 Parking 对象。
4- 从停车对象中获取汽车。
代码将类似于:
$em = $this->getDoctrine()->getManager();
/* Get user from the session */
$user = $this->getUser();
$userParkings = $user->getUserParking();
$parkings = [];
foreach ($userParkings as $item) {
$parking = $item->getParking();
$parkings[count($parkings)] = $parking;
}
// you can get voitures from parkings