2016-09-28 61 views
0

我有以下表格:Yii2渴望负荷聚集

  • 内容 - ID (PK),标题,...等领域...
  • CONTENT_CATEGORY - CONTENT_ID (FK到内容),CATEGORY_ID (FK到内容)

其中一条内容has_many类别,而类别也是一段内容。

在内容我有以下代码:

public function getCategories() 
{ 
    return $this 
     ->hasMany(Category::className(), ['id' => 'category_id']) 
     ->viaTable('content_category', ['content_id' => 'id']); 
} 

public function getCategoriesCsv(){ 
    ... 
} 

对于在后端我的网格视图中,我想显示逗号分隔的类别列表针对每条内容。

我知道我可以单独选择这些信息,但是我希望将它作为find查询的一部分,并在可能的情况下使用现有关系。

+2

使用匿名函数获取gridview的所需值进行优化,以更可爱的状态。 –

+0

@InsaneSkull,但它会为每一行运行一个单独的查询。 – Arth

+2

不是真的,如果你使用'joinWith()'或'with()'。只是使用循环。如果使用gridview和dataProvider输出更新问题。我可以搭把手。 –

回答

0

使用类别受压

本来我实现它:

public function getCategoriesCsv(){ 
    $categoryTitles = []; 
    foreach ($this->categories as $category){ 
     $categoryTitles[] = $category->title; 
    } 
    return implode(', ', $categoryTitles);  
} 

感谢@IStranger,我neatened这:

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'); 
    } 
} 

然后在内容的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; 
} 
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:我没有测试此代码,大概应该是固定的查询检索类别。

此外,在本例中使用的书面基地简单的语法,但它可能会使用不同的助手,结款等

+0

更新我的代码以使用像你的ArrayHelper,非常整洁!谢谢 – Arth