как связанные данные вывести в одной таблице в представлении

Приветствую!

Возникла такая проблема, т.к. совсем новичок.

Есть две сущности, связанные друг с другом отношением "один-ко-многим" с помощью третьей сущности.

Связать их удалось, но не получается вывести в представлении в одной таблице. Например нужно вывести каталог фильмов и к каждому фильму привязать список актёров, игравших в нём. Отдельно могу получить таблицу с фильмами и отдельно список актёров и вывести их через контроллер, ну всё как в разделе "Реляционная Active Record".

Вот модель:


<?php

/**

 * This is the model class for table "films".

 *

 * The followings are the available columns in table 'films':

 * @property integer $id

 * @property string $title

 * @property integer $year

 * @property string $director

 * @property string $description

 * @property string $poster

 * @property double $rate

 */

class Films extends CActiveRecord

{

    public $dir;


	public static function model($className=__CLASS__)

	{

		return parent::model($className);

	}


	public function tableName()

	{

		return 'films';

	}


	public function relations()

	{

		return array(

            'actors'=>array(self::MANY_MANY, 'actors', 'chain(id_films, id_actors)'),

		);

	}


	public function attributeLabels()

	{

		return array(

            'id' => 'Номер',

	    'title' => 'Название',

	    'year' => 'Год',

	    'director' => 'Режисёр',

            'description' => 'Описание',

            'poster' => 'Обложка',

            'rate' => 'Рейтинг',

            'actors' => 'Актёры',

		);

	}

}

И представление, сгенеринное с помощью CRUD:


<?php

$this->breadcrumbs=array(

    'Films'=>array('index'),

    'Manage',

);


$this->menu=array(

    array('label'=>'List Films', 'url'=>array('index')),

    array('label'=>'Create Films', 'url'=>array('create')),

);


Yii::app()->clientScript->registerScript('search', "

$('.search-button').click(function(){

	$('.search-form').toggle();

	return false;

});

$('.search-form form').submit(function(){

	$.fn.yiiGridView.update('films-grid', {

		data: $(this).serialize()

	});

	return false;

});

");

?>


<h1>Manage Films</h1>


<p>

    You may optionally enter a comparison operator (<b>&lt;</b>, <b>&lt;=</b>, <b>&gt;</b>, <b>&gt;=</b>, <b>&lt;&gt;</b>

    or <b>=</b>) at the beginning of each of your search values to specify how the comparison should be done.

</p>


</div><!-- search-form -->


<?php $this->widget('zii.widgets.grid.CGridView', array(

    'id'=>'films-grid',

    'dataProvider'=>$model->search(),

    'filter'=>$model,

    'columns'=>array(

        'id',

        'title',

        'year',

        'director',

        'description',

        'poster',

        'rate',

    ),

)); ?>

Что еще за третья сущность? Может все же "многие-ко-многим" как следует из кода?

Список фильмов ты уже выводишь. Осталось задать к модели "фильм" метод, например getActorNames() - возвращающий список актеров через запятую.

И выводить это в качестве столбца таблицы:




    'columns'=>array(

        'id',

        'title',

array('header'=>'Актеры', 'values'=>'$data->getActorNames()'),



Сущность, содержащая первичные ключи из двух других, аналог сущности tbl_post_category из руководства.

Да, прошу прощения, ошибся, многие-ко-многим отношение.

в модели фильмов попробуй задать




function getActorNames(){

    $ARs=$this->actors; //массив AR записей актеров

    $arr=CHtml::listData($ARs, 'id', 'name'); //преобразуем в обычный массив id=>name (если имя актера хранится не в поле name то поправь)

    return implode(", ", $arr); //склеиваем имена через запятую

}

эту функцию можно будет использовать в качестве значения столбца при выводе списка фильмов

Всё работает теперь. Спасибо за помощь! Редко, где так дружелюбно к новичкам относятся. За это тоже спасибо! :)

P.S.

вот тут:


array('header'=>'Актеры', 'values'=>'$data->getActorNames()'),

надо values на value сменить, и всё заработает.

И такой ещё вопрос возник - как можно сделать сортировку по значениям в этих столбцах с использованием ajax? Например ввожу несколько первых букв из названия искомого фильма и у меня обновляется таблица и отображаются фильмы, название которых начинается на это сочетание буков. C использованием виджета CGridView или вручную форму создать легче? Если пользоваться CGridView, то как сделать текстовое поле для колонки "Актёры", а то у меня там сейчас так:

Было бы хорошо, если бы ещё и ткнули куда-нибудь, где можно про это почитать, а то способы, которые находил, не получается адаптировать под себя, так как не везде понятна логика из-за того, что ещё очень мало знаю этот фреймворк.

Ты спрашиваешь про сортировку, но тут же говоришь про фильтрацию. Неужели не понимаешь разницу?

Фильтрацию по актерам сделать можно, статья на эту тему кажется называется "Сортировки и фильтрация по реляционным столбцам CGridView" но ссылки на неё у меня не сохранилось и найти не могу. Возможно потому что она на английском, а ищу на русском…

Общий смысл в том, что вводимые тобою в строку фильтра данные передаются GET запросом в действие actionIndex:


public function actionIndex(){

    $model=new MyModel('search'); 

    $model->unsetAttributes(); 

    if(isset($_GET['MyModel'])) 

        $model->attributes=$_GET['MyModel']; 

    $this->render('index',array('model'=>$model));

}



это я привел стандартный контроллер, его генерирует Gii. Эта модель создана для поиска, а не для записи чего-то в базу, поэтому у неё заполнена только часть полей.

Следующий знаковый момент, это метод модели search() - он подготавливает DataProvider на основании которого отображаются данные в CGridView.




$criteria->compare('name',$this->name); // если заполнено поле name по нему наложится условие фильтрации



Из этого следует, что тебе нужно создать приватное поле в моделе: public $actorNames; и добавить его в правило как ‘safe’ - что бы в контроллере это поле назначалось в модель из $_GET.

Потом особым образом в search() нужно составить условие на запрос к связанной таблице актеров и отборки только значений егде есть актеры с таким именем как $this->actorNames. Как ты это сделаешь, уже как получится. Можно сделать JOIN на три таблицы, можно сначала выбрать ID актеров в имени которых встречаются введенные буквы и основную таблицу объеденить с таблицей связи где actor_id IN (…)

Понимаю, просто уже столько материала перерыл, что в голове каша.

Вы про эту? - http://www.yiiframework.com/wiki/281/searching-and-sorting-by-related-model-in-cgridview/

Ещё столкнулся с такой проблемой - в сущности есть столбец "director", когда создавал форму и хотел сделать текстовое поле по этому атрибуту:


<?php echo $form->textField($model,'director'); ?>

то поле не создавалось, а когда создал в модели переменную $dir и на основе неё создал текстовое поле - оно появилось. Это какое-то зарезервированное слово или ещё что-то такое?

Именно :) Я всего лишь её пересказал. По идее она должна решить вашу задачу.

Вряд ли зарезервированное слово. Нужно смотреть код и логи, что бы понять причину. Возможно где-то была опечатка.

Добавлю что CGridView позволяет писать




'columns'=>array(

        'id',

        'title',

        'year',

        'director',

        'description',

        'poster',

        'rate',

        'actors.name'//возьмет поле name из отношения actors

    ),



По поводу поле не создавалось, если нету такого аттрибута у модели, то обычно получаем ошибку. А раз поле просто не появилось, могу предположить что был пропущен echo, у самого часто бывает такое =)

Нет, тут что-то другое с этим именем, т.к. даже после создания формы с помощью CRUD оно не отображается - все остальные поля и лейблы есть, а поля director нет.

Опять я, по сути с тем же вопросом:

Сделал фильтрацию по полям сущности Films с помощью метода addSearchCondition класса CDbCriteria, всё робит. Теперь нужно сделать по полю name сущности Actors и вот тут проблема - если пользовать метод join, при этом метод search() модели выглядит таким образом:


public function search()

    {

        $criteria=new CDbCriteria;


        $criteria->addSearchCondition('id',$this->id);

        $criteria->addSearchCondition('title',$this->title);

        $criteria->addSearchCondition('year', $this->year);

        $criteria->addSearchCondition('director',$this->dir);

        $criteria->addSearchCondition('rate',$this->rate);


        $criteria->join = 'JOIN chain ON `t`.id = chain.id_films';

        $criteria->join = 'JOIN actors ON chain.id_actors = actors.id';


        $criteria->addSearchCondition('actors.name', $this->actor);


        return new CActiveDataProvider($this, array(

            'criteria'=>$criteria,

        ));

    }

всё ищется как надо, но вот таблица в виджете CGridView в представлении при этом отображается не как одна_строка_по_фильму, а как 10 строк по одному фильму, т.к. к одному фильму привязаны 10 актёров. Мучаюсь с этой проблемой уже несколько часов и ни как не получается найти решение. Не подскажете, что можно сделать?

wika

Да да, уже наводили на эту тему. Но то ли я чего-то не понимаю там, то ли она мне не очень и подходит:

:rolleyes:

Разобрался. Надо было ещё и


$criteria->distinct = true;

написать.