0

If I have a document Shop that has many Activities defined as ReferenceMany, is there a way I can directly query for the list of Activities for a Shop without hydrating a Shop instance?

For example:

{
    "_id": fd390j09afj09dfj,
    "activities": [
        ...
    ]
}

All I want is to be able to say "get me the array activities where _id is fd390j09afj09dfj, and hydrate them as Activity instances.


Here's the first solution I came up with:

    /**
     * Gets all activities configured for a shop.
     *
     * @param string $shopId
     * @return \BikeShed\Domain\Activity[]|\Doctrine\Common\Collections\ArrayCollection
     */
    public function findByShopId($shopId) {

        /** @var \BikeShed\Domain\Repository\Shop $shopRepository */
        $shopRepository = $this->dm->getRepository('BikeShed\Domain\Shop');

        $shop = $shopRepository->findOneById($shopId);

        return $shop->getActivities();

    }

It's simply fetching the Shop and then getting all the Activities via the defined relation.


Here's a working example of how you would implement jmikola's last suggestion:

    /**
     * @param string $shopId
     * @return ActivityModel[]
     */
    public function findByShopId($shopId) {

        $partialShopData = $this->dm->getRepository('BikeShed\Domain\Shop')->createQueryBuilder()
            ->hydrate(false)
            ->field('activities')
            ->getQuery()
            ->getSingleResult()
        ;

        $activityIds = [];
        if(!empty($partialShopData['activities']))
            foreach($partialShopData['activities'] as $activity)
                if(!empty($activity['$id']))
                    $activityIds[] = $activity['$id'];

        return $this->createQueryBuilder()
            ->field('id')
            ->in($activityIds)
            ->getQuery()
            ->toArray()
        ;

    }
4

1 回答 1

1

您不能直接查询 Shop 集合或(或 ODM 存储库)并接收 Activity 实例;但是,您可以使用Query Builder API来指定带有select('activities'). 执行的查询仍将返回 Shop 实例,但该activities字段应该是唯一水合的内容(作为 Activity 实例的 PersistentCollection)。在这种情况下,您不应修改任何非水合 Shop 字段,因为 ODM 会将任何非空值检测为更改。

在 ShopRepository 上添加一个方便的方法应该很简单,该方法使用它发出上述查询select()并返回 Activity 文档的集合(或数组)而不是 Shop。保持商店不可访问还可以防止您无意中修改其中的其他非水合字段。

这种方法的缺点是活动将是代理对象并延迟加载。您可以使用参考启动来缓解这种情况。通过启动,您最终将执行两个查询(一个针对 Shop,一个针对所有引用的 Activity 文档)。


关于您关于将此方法放在 Activity 存储库中的后续问题,您确实有另一种选择。首先,我同意这ActivityRepository::findByShopId()比在 ShopRepository 上调用返回 Activity 对象的方法更可取。

getRepository()每个存储库都有一个对文档管理器的引用,您可以使用它通过该方法访问其他存储库。AnActivityRepository::findByShopId()可以执行以下操作:

  • 通过文档管理器访问 Shop 存储库
  • 通过 ID 查询商店,仅投影activities字段并完全禁用水化
  • activities数组中收集标识符。根据 Activity 引用是否简单,该数组中的元素可能是原始_id值或DBRef对象。
  • 对所有 Activity 对象执行查询(很简单,因为我们已经在该存储库中),其中 ID 是$in标识符数组
于 2014-08-21T22:57:46.000 回答