仅使用Doctrine从ManyToMany获取ID列表

时间:2015-07-14 07:22:31

标签: php mysql symfony doctrine-orm

我有一个与ManyToMany相关联的字段,而我正处于这样一种情况:我只是试图获取所有ID而不保护字段中的任何子实体。

我知道在我访问该字段时会有一个查询来获取实体引用,这很好/期望。但是我需要遍历ID,但我不知道如何在不执行的情况下获取它们

$ids = [];
foreach($mainEntity->getSubEntities() as $subentity) {
   $ids[] = $subentity->getId();
}

由于foreach循环,这似乎也会自动给子实体加水。这会导致大量不必要的查询并影响页面加载时间。

我的子实体字段也标有EXTRALAZY

/**
 * @var ArrayCollection
 * @ORM\ManyToMany(targetEntity="User", fetch="EXTRA_LAZY")
 * @ORM\JoinTable(name="user_friends")
 */
protected $friends;

2 个答案:

答案 0 :(得分:6)

由于getKeys()仅返回PersistentCollection的顺序索引,因此我找到了该oneliner作为解决方案:

$myCollectionIds = $myCollection->map(function($obj){return $obj->getId();})->getValues();

IN上下文中顺利运行。

答案 1 :(得分:1)

我不认为使用实体定义实体的方式来做您是不可能的。 Doctrine PersistentCollection类将始终进行初始化,并在对其进行迭代或映射时从DB加载所有实体。如前所述,在mapgetIterator的实现中可以看到,它是在foreach循环或闭包中运行代码之前完成的。

即使将集合标记为Extra Lazy,只要您使用contains,containsKey,count,get或slice以外的任何方法,所有内容都将从DB装入。

但是,如果您不介意稍微调整实体结构,则可以得到所需的行为。

Reify将ManyToMany关联为一个实体。

您的关系称为friend,因此您可以创建一个friendship实体:

/** @Entity */
class Frendship
{
    /**
     * @var int
     * @Id @Column(type="integer") @GeneratedValue 
     **/
    protected $id;

    /**
     * @var User
     * @ORM\@ManyToOne(targetEntity="User", inversedBy="friendships")
     **/
    protected $friendA

    /**
     * @var User
     * @ORM\@ManyToOne(targetEntity="User", inversedBy="friendships")
     **/
    protected $friendB

    public function getOtherFriend(User $friend){
        if ($this->friendA === $friend)) {
            return $this->friendB; 
        } else if ($this->friendB === $friend)) {
            return $this->friendA
        } else {
            throw new \Exception("$friend is not part of this friendship");
        }
    }
}

然后,您可以将friends集合替换为frendships集合并进行遍历。友谊将被完全水化,但这没关系,因为它们仅包含两个整数。

您可以从每个友谊中获取另一个朋友,它将成为一个代理对象,除非它恰好已被加载。由于您只想在其上调用getId(),因此不需要从数据库中检索完整实体。