从反面加入
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')
// …
给出以下两个实体:
<?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')
// …