我有以下表格:
一段内容 has_many 类别,而某个类别也是一段内容。
在内容中,我有以下代码:
rv.notifyDataSetChanged();
对于后端的网格视图,我想为每个内容显示逗号分隔的类别列表。
我知道我可以单独选择这些信息,但我想将其作为查询查询的一部分并尽可能使用现有关系。
答案 0 :(得分:1)
这种方法采用典型方式,适用于相关的Category
模型。因此它需要大量的内存。
class Content extends \yii\db\ActiveRecord
{
/**
* Returns comma separated list of category titles using specified separator.
*
* @param string $separator
*
* @return string
*/
public function getCategoriesCsv($separator = ', ')
{
$titles = \yii\helpers\ArrayHelper::getColumn($this->categories, 'title');
return implode($separator, $titles);
}
// ...
}
应该与急切加载一起使用:
Content::find()
->with('categories')
->all();
此方法使用子查询,不使用关系和相关模型。因此这种方式更快,并保留了大量内存。
class Content extends \yii\db\ActiveRecord
{
const ATTR_CATEGORIES_CSV = 'categoriesCsv';
/**
* @var string Comma separated list of category titles.
*/
public $categoriesCsv;
/**
* Returns DB expression for retrieving related category titles.
*
* @return \yii\db\Expression
*/
public function prepareRelatedCategoriesExpression()
{
// Build subquery that selects all category records related with current content row.
$queryRelatedCategories = Category::find()
->leftJoin('{{%content_category}}', '{{%content_category}}.[[category_id]] = {{%category}}.[[id]]')
->andWhere(new \yii\db\Expression('{{%content_category}}.[[content_id]] = {{%content}}.[[id]]'));
// Prepare subquery for retrieving only comma-separated titles
$queryRelatedCategories
->select(new \yii\db\Expression('GROUP_CONCAT( {{%category}}.[[title]] )'));
// Prepare expression with scalar value from subquery
$sqlRelatedCategories = $queryRelatedCategories->createCommand()->getRawSql();
return new \yii\db\Expression('(' . $sqlRelatedCategories . ')');
}
// ...
}
当附加列的别名等于某个模型属性时,它将由all()
方法填充:
$contentModels = Content::find()
->andSelect([
'*',
Content::ATTR_CATEGORIES_CSV => Content::prepareRelatedCategoriesExpression(),
])
->all();
foreach ($contentModels as $contentModel) {
$contentModel->id;
$contentModel->categoriesCsv; // it will be also populated by ->all() method.
// ...
}
ps:我没有测试过这段代码,可能应该修复查询类别。
此外,在这个例子中,它使用基本的简单语法编写,但它可以使用各种帮助器,连接模型等优化到更可爱的状态。
答案 1 :(得分:0)
加载类别
最初我将它实现为:
public function getCategoriesCsv(){
$categoryTitles = [];
foreach ($this->categories as $category){
$categoryTitles[] = $category->title;
}
return implode(', ', $categoryTitles);
}
感谢@IStranger,我将其添加到:
public function getCategoriesCsv()
{
$titles = ArrayHelper::getColumn($this->categories, 'title');
return implode(', ', $titles);
}
没有加载类别
我现在设法通过添加单独的CategoryCsv ActiveRecord来避免加载所有类别模型:
class CategoryCsv extends ActiveRecord
{
public static function tableName(){
return '{{%content_category}}';
}
public function attributes(){
return ['content_id', 'value'];
}
public static function find(){
return parent::find()
->select([
'content_id',
'GROUP_CONCAT(
categoryCsv.title
ORDER BY categoryCsv.title
SEPARATOR ", "
) value'
])
->innerJoin('content categoryCsv','category_id = categoryCsv.id')
->groupBy('content_id');
}
}
然后在Content ActiveRecord中:
public function getCategoriesCsv(){
return $this->hasOne(CategoryCsv::className(), ['content_id' => 'id']);
}
因此我可以像这样访问值:
$contents = Content::find()->with('categoryCsv')->all();
foreach($contents as $content){
echo $content->categoryCsv->value;
}