从反面加入

Join from inversed side

给出以下两个实体:

<?php
    /**
     * User
     * @ORM\Entity()
     */
    class User implements AdvancedUserInterface, \Serializable, EncoderAwareInterface
    {
        /**
         * @var Vip
         * @ORM\OneToOne(targetEntity="Vip", mappedBy="user", fetch="EAGER")
         */
        protected $vip;

        // …

<?php
    /**
     * Vip
     * @ORM\Entity()
     */
    class Vip
    {
        /**
         * @ORM\id @ORM\OneToOne(targetEntity="User", inversedBy="vip", fetch="EAGER")
         * @ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false)
         */
        protected $user;

        // …

简短:
我如何在上面给出的 DQL 中做到这一点 SQL 实体:
SELECT u.firstName, v.foo FROM User join Vip v ON v.user_id = u.id

换句话说,我如何使用 DQL join 检索 10 个第一个用户(如果存在,则包含他们的 VIP 信息),这样 Doctrine 只会生成一个 SQL 查询.这可能吗?


长话短说:
拥有方是 Vip 实体,因为它持有数据库中用户的 reference/foreign 键。

我正在尝试检索所有 User 及其 Vip 数据。
使用 knplabs/knp-paginator-bundle,我首先设置了一个简单的查询:

$dql = "SELECT u, p FROM AppBundle:User u;

尽管将获取属性强制为“EAGER”,但Vip信息不是初始查询的一部分。因此,从树枝 for in 循环内部的每次迭代调用 getter getVip() 就像

{% for user in pagination %}
    {% if user.getVip() %}
        <span class="label label-warning">V.I.P</span>
    {% endif %}
{{% endfor %}}

..导致在每次迭代时发出查询!

Symfony 开发栏显示 6 个查询:

DQL 文档并说可以使用 JOIN 关键字。所以我的查询变成了:
$dql = "SELECT u, v FROM AppBundle:User u JOIN u.vip v;

但现在我得到这个错误:

Warning: spl_object_hash() expects parameter 1 to be object, null given

我卡住了,想知道如何在单个查询中获取 Vip 数据(或 null)以及 User 数据。

In other words how can I retrieve 10 first users ( with their VIP infos if it exists), using DQL join in such a way that only one SQL query will be generated by Doctrine. Is that possible ?

您应该使用 select 子句初始化所有相关实体,以避免在访问相关对象时进行额外查询。

$repository = $em->getRepository(User::class);

$users = $repository->createQueryBuilder('u')
        ->addSelect('v') // Initialize Vip's
        ->join('u.vip', 'v')
        ->getQuery()
        ->setMaxResults(10)
        ->getResult();

是的,可以在 SELECT 语句中添加关联实体。
但更准确地说,应该只添加真正涉及预期结果的关系,换句话说,作为 "EAGER".

获取的实体

我意识到 vip 实体有另一种关系(oneToMany 与 vehicule 实体)。我只想用他们的 vip 元数据检索用户。在查询中添加另一个 JOIN 只会带来更多数据,因为无论如何我都不会使用车辆(并在幕后发布额外的工作)。

-> 所以我只是 更改 d fetch 属性从 "EAGER" 到 "LAZY"vip OneToMany 声明中。

总结:

问问自己 «涉及哪些实体?»,它应该是一部分 结果(你只需要这些信息)。

如果否,您可以在关系声明中将 fetch 属性设置为“[EXTRA_]LAZY”,例如

/**
 * @ORM\OneToMany(targetEntity="Vehicule", mappedBy="vip", fetch="LAZY", …)
 */

protected $vehicules;

如果是你将不得不select你查询中的那些实体。

使用 DQL:
SELECT u, v, w FROM AppBundle:User u LEFT JOIN u.vip v LEFT JOIN v.vehicules w

使用查询生成器:

$repository = $em->getRepository(User::class);
$users = $repository->createQueryBuilder('u')
    ->addSelect('v')
    ->join('u.vip', 'v')
    ->addSelect('w')
    ->join('v.vehicules', 'w')
    // …