Doctrine DQL:4个实体之间的DQL连接

时间:2018-02-06 09:25:10

标签: php mysql join doctrine-orm dql

所以,我有这个实体结构:

  • User有一些Email s(同时超过一个);
  • 属于Email的每个Domain(如果是email@example.com,那么我在数据库中有Domain“example.com`)
  • 每个Domain都可以与Profile相关联。

现在,给定User我想获得他可以访问的所有Profiles(如果有的话!可能Domain没有Profile)。

实体结构:

// User entity
use FOS\UserBundle\Model\User as BaseUser;

/**
 * @ORM\Entity
 * @ORM\Table(name="users")
 */
class User extends BaseUser
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @var ArrayCollection
     *
     * @ORM\OneToMany(targetEntity="AppBundle\Entity\Email", mappedBy="forUser")
     */
    protected $emails;

    ...
}


// Email entity
/**
 * @ORM\Entity(repositoryClass="\AppBundle\Repository\EmailRepository")
 * @ORM\Table(name="emails")
 */
class Email
{
    /**
     * @var EmailValue
     *
     * @ORM\Id
     * @ORM\Column(name="email", type="email", nullable=false, unique=true)
     */
    protected $email;

    /**
     * @var Domain
     *
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\Domain", inversedBy="emails")
     * @ORM\JoinColumn(name="domain", referencedColumnName="second_level")
     */
    protected $domain;

    /**
     * @var \MyNamespace\Bundle\UserBundle\Entity\User
     *
     * @ORM\ManyToOne(targetEntity="MyNamespace\Bundle\UserBundle\Entity\User", inversedBy="emails")
     * @ORM\JoinColumn(name="for_user", referencedColumnName="id")
     */
    protected $forUser;

    ...
}


// Domain entity
/**
 * @ORM\Entity(repositoryClass="\AppBundle\Repository\DomainRepository")
 * @ORM\Table(name="domains")
 */
class Domain
{
    /**
     * @var string
     *
     * @ORM\Id
     * @ORM\Column(type="string", nullable=false, unique=true)
     */
    private $secondLevel;

    /**
     * @var UrlValue
     *
     * @ORM\Column(name="url", type="uri", nullable=false, unique=true)
     */
    private $url;

    ...

    /**
     * Each Domain can be linked to only one Profile.
     *
     * @var Profile|null
     *
     * @ORM\OneToOne(targetEntity="AppBundle\Entity\Profile", inversedBy="domain")
     */
    private $profile;

    /**
     * Each Domain can have more than one Email.
     *
     * @var Collection
     * @ORM\OneToMany(targetEntity="AppBundle\Entity\Email", mappedBy="domain")
     */
    private $emails;

    ...
}


// Profile entity
/**
 * @ORM\Table(name="profiles")
 * @ORM\Entity(repositoryClass="\AppBundle\Repository\ProfileRepository")
 * @ORM\HasLifecycleCallbacks
 */
class Profile
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="string", unique=true)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="CUSTOM")
     * @ORM\CustomIdGenerator(class="AppBundle\Doctrine\IdGenerator")
     */
    private $id;

    /**
     * Each Store can have only one Domain.
     *
     * @var Domain
     *
     * @ORM\OneToOne(targetEntity="AppBundle\Entity\Domain", mappedBy="profile")
     */
    private $domain;

    ...
}

现在,给定User我想获得他可以访问的所有Profiles(如果有的话!可能Domain没有Profile)。

我决定User是否可以访问Profile Email Domain是否User

因此,如果EmailProfile email@example.com,则他可以访问Domain example.com的JOIN

我知道我需要加入这四张桌子,但我不知道如何才能这样做<div> .. </div>

你能帮助我吗?

1 个答案:

答案 0 :(得分:0)

由于您创建了所有关系双向,因此可以通过连接Profile表中的表来执行此类查询。您可以使用the query builder并将以下方法添加到ProfileRepository,以便根据User实例执行搜索:

public function getUserProfiles(User $user)
{
    return $this->createQueryBuilder('profile')
        ->addSelect('domain') // select the corresponding domain at the same time
        ->join('profile.domain', 'domain')
        ->join('domain.emails', 'emails')
        ->where('emails.forUser = :user')
        ->setParameter('user', $user)
        ->getQuery()
        ->getResult();
}

如果您只是可以访问用户ID,则可以使用where('IDENTITY(emails.forUser) = :userId')代替上面写的where子句。

注意:如果用户有多个电子邮件链接到同一个域/配置文件,则基础SQL查询将多次返回相同的配置文件条目,但如果您使用,则Doctrine会将它们折叠为结果集中的单个条目getResultgetArrayResult。如果您使用getScalarResult as described in the documentation

,您将获得重复项

你也可以反过来这样做,加入来自emails的{​​{1}},domainprofile,以便能够覆盖{{1}的所有数据实例。重要的是将User用于User联接,以便仍然可以选择没有配置文件的域。在这种情况下,您需要向leftJoin类添加一个方法来抓取用户的profile并检索相应的配置文件。