2016-09-28 2 views
0

나는 다음과 같은 테이블이 있습니다Yii2 열망로드 집계

  • 내용 - ID (PK), 제목, ... 다른 분야 ...
  • content_category을 - content_id (FK to content), category_id (FK to content)

콘텐츠 부분이 has_many이고 범주가 콘텐츠이기도합니다.

내용에서 나는 다음과 같은 코드가 있습니다 백엔드 내 그리드 뷰

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

public function getCategoriesCsv(){ 
    ... 
} 

을, 나는 쉼표 각 콘텐츠 범주로 구분 된 목록을 표시하고 싶습니다.

이 정보를 별도로 선택할 수 있다는 것을 알고 있지만 가능한 경우 기존 쿼리를 사용하고 기존 쿼리를 사용하고 싶습니다. 나는 이것을 neatened, @IStranger에

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

감사 : 범주

+2

사용 익명 함수를 사용하여 더 귀여운 상태가 최적화 될 수있다. –

+0

@InsaneSkull,하지만 각 행에 대해 별도의 쿼리를 실행합니다. – Arth

+2

'joinWith()'또는'with()'를 사용하면 실제로는 그렇지 않습니다. 그냥 루프를 사용합니다. gridview 및 dataProvider 출력으로 질문을 업데이트하는 경우 내가 도움이 될 수 있습니다. –

답변

0

원래 내가로 구현 넣었 CATEGORIES없이

public function getCategoriesCsv() 
{ 
    $titles = ArrayHelper::getColumn($this->categories, 'title'); 
    return implode(', ', $titles); 
} 

로드

는 이제 별도의 CategoryCsv 액티브을 추가하여 모든 카테고리 모델을로드하지 않도록 관리해야 :

내용에서 다음
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'); 
    } 
} 

액티브에게 :

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. 
    // ... 
} 

추신 : 나는 카테고리를 검색하는 아마 고정되어야하며, 쿼리를이 코드를 테스트하지.

또한, 본 실시 예에서베이스 간단한 문법을 ​​이용하여 기록 있지만있는 gridview의 목표 값을 얻기 위해 다양한 헬퍼 접합 모델 등

+0

내 코드가 당신처럼 ArrayHelper를 사용하도록 업데이트되었습니다. 매우 정교합니다! 감사 – Arth