CakePHP 3:改变结果集的结构
CakePHP 3: Changing structure of result set
我是 CakePHP 新手,遇到以下问题:
我有表格 "Images"、"Keywords" 和 "KeywordCategories"。每个图像都可以有多个关键字(多对多),每个关键字都有一个类别(多对一)。使用
检索图像列表
$images = $this->Images->find()->contain(['Keywords', 'Keywords.KeywordCategories']);
returns 结果结构如下:
[
{
"id":1,
"keywords":[
{
"keyword":"Dog",
"keyword_category":{
"title":"Animal"
}
},
{
"keyword":"Cat",
"keyword_category":{
"title":"Animal"
}
},
{
"keyword":"Black",
"keyword_category":{
"title":"Color"
}
}
]
}
]
很好,但我希望关键字按其关键字类别分组,结构如下:
[
{
"id":1,
"keyword_categories":[
{
"title":"Animal",
"keywords":[
{
"keyword":"Dog"
},
{
"keyword":"Cat"
}
]
},
{
"title":"Color",
"keywords":[
{
"keyword":"Black"
}
]
}
]
}
]
知道如何使用 CakePHP 查询实现吗?
该格式几乎是一个反向包含,仅使用 ORM 关联是不可能的 auto-magic。您将不得不单独获取关联数据,对其进行过滤,然后将其注入图像结果中……您甚至可以创建一个自定义关联 class 来包含、获取并将结果拼接在一起,但这是一种如果你问我太过分了。
因为无论如何你都必须做一些额外的格式化(并且只是为了将结果拼接在一起),我会简单地使用一些 collection 格式化 foo 来代替,类似于
// ...
->find()
->contain(['Keywords', 'Keywords.KeywordCategories'])
->formatResults(function ($results) {
/* @var $results \Cake\Datasource\ResultSetInterface|\Cake\Collection\CollectionInterface */
return $results->map(
function ($image) {
$image['keyword_categories'] =
collection($image['keywords'])
->groupBy('keyword_category.title')
->map(function ($keywords, $category) {
foreach ($keywords as &$keyword) {
unset($keyword['keyword_category']);
}
return [
'title' => $category,
'keywords' => $keywords
];
})
->toList();
unset($image['keywords']);
return $image;
}
);
});
* 用于说明目的的未经测试的示例代码
即在每个图像结果上创建一个名为 keyword_categories
的计算字段,由包含的关键字组成,按相关类别标题分组,关键字嵌套在数组中 title
和 keywords
从关键字中删除类别的字段,最后 re-index 整个内容作为基本的数字索引数组。
另见
- Cookbook > Database Access & ORM > Query Builder > Adding Calculated Fields
- Cookbook > Collections > Grouping and Counting > Collection::groupBy()
- Cookbook > Collections > Iterating > Collection::map()
- API > \Cake\Collection\CollectionInterface::toArray()
- API > \Cake\Collection\CollectionInterface::toList()
我是 CakePHP 新手,遇到以下问题:
我有表格 "Images"、"Keywords" 和 "KeywordCategories"。每个图像都可以有多个关键字(多对多),每个关键字都有一个类别(多对一)。使用
检索图像列表$images = $this->Images->find()->contain(['Keywords', 'Keywords.KeywordCategories']);
returns 结果结构如下:
[
{
"id":1,
"keywords":[
{
"keyword":"Dog",
"keyword_category":{
"title":"Animal"
}
},
{
"keyword":"Cat",
"keyword_category":{
"title":"Animal"
}
},
{
"keyword":"Black",
"keyword_category":{
"title":"Color"
}
}
]
}
]
很好,但我希望关键字按其关键字类别分组,结构如下:
[
{
"id":1,
"keyword_categories":[
{
"title":"Animal",
"keywords":[
{
"keyword":"Dog"
},
{
"keyword":"Cat"
}
]
},
{
"title":"Color",
"keywords":[
{
"keyword":"Black"
}
]
}
]
}
]
知道如何使用 CakePHP 查询实现吗?
该格式几乎是一个反向包含,仅使用 ORM 关联是不可能的 auto-magic。您将不得不单独获取关联数据,对其进行过滤,然后将其注入图像结果中……您甚至可以创建一个自定义关联 class 来包含、获取并将结果拼接在一起,但这是一种如果你问我太过分了。
因为无论如何你都必须做一些额外的格式化(并且只是为了将结果拼接在一起),我会简单地使用一些 collection 格式化 foo 来代替,类似于
// ...
->find()
->contain(['Keywords', 'Keywords.KeywordCategories'])
->formatResults(function ($results) {
/* @var $results \Cake\Datasource\ResultSetInterface|\Cake\Collection\CollectionInterface */
return $results->map(
function ($image) {
$image['keyword_categories'] =
collection($image['keywords'])
->groupBy('keyword_category.title')
->map(function ($keywords, $category) {
foreach ($keywords as &$keyword) {
unset($keyword['keyword_category']);
}
return [
'title' => $category,
'keywords' => $keywords
];
})
->toList();
unset($image['keywords']);
return $image;
}
);
});
* 用于说明目的的未经测试的示例代码
即在每个图像结果上创建一个名为 keyword_categories
的计算字段,由包含的关键字组成,按相关类别标题分组,关键字嵌套在数组中 title
和 keywords
从关键字中删除类别的字段,最后 re-index 整个内容作为基本的数字索引数组。
另见
- Cookbook > Database Access & ORM > Query Builder > Adding Calculated Fields
- Cookbook > Collections > Grouping and Counting > Collection::groupBy()
- Cookbook > Collections > Iterating > Collection::map()
- API > \Cake\Collection\CollectionInterface::toArray()
- API > \Cake\Collection\CollectionInterface::toList()