Filtro com relacionamento HAS_MANY

Bom dia pessoal, estou com um problema ha um bom tempo, já tentei por vários tópicos existentes no fórum e nada. Na listagem das news, gostaria de listar os pageslot e nesta coluna do pageslot, queria colocar uma combo para filtro populada com todos os pageslot. Foram muitas as tentativas sem sucesso, segue abaixo uma delas:

model News.php, método relations


'newsPageSlot' => array(self::MANY_MANY, 'NewsPageSlot', 'newsPublications(news_id, pageslot_id)'),

			'newsPublications' => array(self::HAS_MANY, 'NewsPublication', 'news_id'),

método search


$criteria->with = array('user', 'segmentation', 'newsPublications');

...

$criteria->compare('pageslot_id', NewsPublication::model()->getPublicationNews( $this->id )->pageslot_id);

O problema é que a combo de filtro não aparece na coluna pageslot(imagem 1)

Segue minha modelagem (imagem 2)

Muito Obrigado!

http://www.yiiframew…ry-with-through

Opa

algo como isto resolve




//news model

function relations(){

 return array(

  'newsPublications'=>array(self::HAS_MANY,'newsPublication','news_id'),

  'pageSlots'=>array(self::HAS_MANY,'newsPageSlot','pageslot_id','through'=>'newsPublications'),

 );

}



Olá Gustavo, obrigado pela eficiente e rápida resposta, tentei aplicar seu código, porém me retorna o seguinte erro:

Opa Fábio

A função through está disponível a partir da versão 1.1.7

Qual versão vc está usando ?

Se <1.1.7, atualize e tente de novo

Atualizei e realmente não ocorreu o erro, porém a combo continua sem aparecer.

tentei isso:


'newsPublications' => array(self::HAS_MANY, 'newsPublication', 'news_id'),

			//'newsPageSlot' => array(self::MANY_MANY, 'NewsPageSlot', 'newsPublications(news_id, pageslot_id)'),

			'pageSlots'=>array(self::HAS_MANY,'newsPageSlot','pageslot_id','through'=>'newsPublications'),

e isso:


'newsPublications' => array(self::HAS_MANY, 'newsPublication', 'news_id'),

			'newsPageSlot' => array(self::MANY_MANY, 'NewsPageSlot', 'newsPublications(news_id, pageslot_id)'),

			'pageSlots'=>array(self::HAS_MANY,'newsPageSlot','pageslot_id','through'=>'newsPublications'),

Na view a coluna está assim:


array(

			'name' => 'NewsPublication.pageslot_id',

			'value' => 'NewsPublication::model()->getPublicationNews( $data->id )->pageslot->name',

			'filter' => CHtml::listData(NewsPageSlot::model()->findAll(),'id','name'),

		),

primeiro teste se o relacionamento funciona e veja se a query gerada está correta

pra habilitar o log das querys adicione na config do db enableProfiling como true e enableParamLogging como true e habilite o log

o método search deve ter algo como isto:




//na model adicione o campo / propriedade slotName

public $slotName;

//metodo search

$criteria->select='t.*,pageSlots.name as slotName';

$criteria->compare('pageSlots.name',$this->slotName);

$criteria->with=array('pageSlots');



e o filter deve ser apenas




array(

'name'=> 'slotName',

'filter'=>//como estava

),



Parece que não está se relacionando com news_publication

model News


public $slotName;

relation


'newsPublications' => array(self::HAS_MANY, 'newsPublication', 'news_id'),

			'newsPageSlots'=>array(self::HAS_MANY,'newsPageSlot','pageslot_id','through'=>'newsPublications'),

search


public function search()

	{

		// Warning: Please modify the following code to remove attributes that

		// should not be searched.


		$criteria=new CDbCriteria;

		$criteria->with = array('user', 'segmentation', 'newsPublications', 'newsPageSlots');


		# o "t" faz referência a tabela principal, para poder fazer o filtro

		$criteria->compare('t.id',$this->id,true);

		$criteria->compare('title',$this->title,true);

		$criteria->compare('summary',$this->summary,true);

		$criteria->compare('summary2',$this->summary2,true);

		$criteria->compare('body',$this->body,true);

		$criteria->compare('author',$this->author,true);

		$criteria->compare('summaryimage_id',$this->summaryimage_id,true);

		$criteria->compare('bodyimage1_id',$this->bodyimage1_id,true);

		$criteria->compare('bodyimage2_id',$this->bodyimage2_id,true);

		$criteria->compare('bodyimage3_id',$this->bodyimage3_id,true);

		$criteria->compare('bodyimage4_id',$this->bodyimage4_id,true);

		$criteria->compare('bodyimage5_id',$this->bodyimage5_id,true);

		$criteria->compare('summarycaption',$this->summarycaption,true);

		$criteria->compare('bodycaption1',$this->bodycaption1,true);

		$criteria->compare('bodycaption2',$this->bodycaption2,true);

		$criteria->compare('bodycaption3',$this->bodycaption3,true);

		$criteria->compare('bodycaption4',$this->bodycaption4,true);

		$criteria->compare('bodycaption5',$this->bodycaption5,true);

		$criteria->compare('t.created_at',$this->created_at,true);

		$criteria->compare('t.updated_at',$this->updated_at,true);

		$criteria->compare('status_id',$this->status_id,true);

		$criteria->compare('user_id_review',$this->user_id_review,true);

		$criteria->compare('note_review',$this->note_review,true);

		$criteria->compare('date_review',$this->date_review,true);

		

		// 3º parametro false (default) pois deve considerar o valor exato para combos

		$criteria->compare('t.active',$this->active);

		

		// combos

		$criteria->compare('user.id', $this->user_id);

		$criteria->compare('segmentation.id', $this->segmentation_id);

		$criteria->compare('pageslot_id', NewsPublication::model()->getPublicationNews( $this->id )->pageslot_id);

		

		$criteria->select='t.*,newsPageSlots.name as slotName';

		$criteria->compare('newsPageSlots.name', $this->slotName);

				

		return new CActiveDataProvider(get_class($this), array(

			'criteria'=>$criteria,

			'sort' => array('defaultOrder' => 'title'),

		));

	}

Sobre o log, tive que inclui ‘class’=>‘CWebLogRoute’ para funcionar


'routes'=>array(

                array(

                    'class'=>'CFileLogRoute',

                    'levels'=>'error, warning',

                    'filter'=>'CLogFilter',

                ),

                array(

					'class'=>'CWebLogRoute',

				),

            ),

Baseado nisso, fiz isso:


'newsPublications' => array(self::HAS_MANY, 'NewsPublication', 'pageslot_id'),

			//'newsPageSlots'=>array(self::HAS_MANY,'NewsPageSlot','pageslot_id','through'=>'newsPublications'),

e nada de aparecer o filtro na grid.

http://www.yiiframework.com/forum/index.php?/topic/20500-filter-with-has-many/page__p__100324__fromsearch__1#entry100324

Isso Funcionou, mas é estranho, pois em outra coluna isto functiona:




array(

        	'name' => 'segmentation_id',

            'value' => '$data->segmentation->name',

            'filter' => CHtml::listData(Segmentation::model()->findAll(),'id','name'),

		),



Deve ser por causa do relacionamento, que é o meu maior problema aqui, news possui segmentation_id, porem pageslot_id está em news_publication, onde news_publication possui news_id.

Bom, a combo apareceu, mas agora o problema é o relacionamento citado no primeiro post, como ficaria na model News de acordo com o DER que postei?

Muito obrigado pela atenção!

de acordo com a documentação, na model $News, fiz desta maneira:




public function relations()

	{

...

return array(

...

'newsPublications' => array(self::HAS_MANY, 'NewsPublication', 'news_id'),

			'newsPageSlots'=>array(self::HAS_MANY,'NewsPageSlot','pageslot_id', 'through'=>'newsPublications'),

..


public function search()

	{

...

$criteria=new CDbCriteria;

		$criteria->with = array('user', 'segmentation', 'newsPublications', 'status', 'newsPageSlots');

...

$criteria->compare('pageslot_id', NewsPublication::model()->getPublicationNews( $this->id )->pageslot_id);

...



Mas ao selecionar uma opção da combo, ele envia o valor, mas a combo volta com o valor inicial(vazio) e não aplica o filtro na grid.

Tentei de várias maneiras mas não inclui a tabela news_publication na consulta.




...

public function relations()

	{

return array(

...

'newsPublications' => array(self::HAS_MANY, 'NewsPublication', 'news_id'),

			'newsPageSlots'=>array(self::HAS_MANY,'NewsPageSlot','pageslot_id', 'through'=>'newsPublications'),

		);

	}

...



Coloquei o valor manualmente para testes:




...

public function search()

	{

...

$criteria=new CDbCriteria;

		$criteria->with = array('user', 'segmentation', 'newsPublications', 'status', 'newsPageSlots');

...

$criteria->compare('newsPublications.pageslot_id', 12);

...



Mas se não colocar o value, nao aparece valor nenhum na grid.

Opa, funcionou, porém agora nao sei como enviar para a model o valor selecionado no filtro. Quando seleciono um valor no filtro, a combo perde o valor:

view


array(

			'name' => 'slotName',

			'filter' => CHtml::activeDropDownList(NewsPublication::model(), 'pageslot_id', CHtml::listData(NewsPageSlot::model()->findAll(), 'id', 'name'), array('prompt' => ''))

		),

model:




public $slotName;

public function relations()

	{

		return array(

			'newsPublications' => array(self::HAS_MANY, 'NewsPublication', 'news_id'),

			'newsPageSlots'=>array(self::HAS_MANY,'NewsPageSlot','pageslot_id', 'through'=>'newsPublications'),

		);

	}


public function search()

	{

		$criteria=new CDbCriteria;

		$criteria->with = array('newsPageSlots');

		# utiliza through no relacionamento 

		$criteria->together = true;

		$criteria->compare('newsPageSlots.name', $this->slotName);

		

		return new CActiveDataProvider(get_class($this), array(

			'criteria'=>$criteria,

		));

	}

Opa

show de bola que funcionou

pra funcionar a busca, tenha certeza de declarar "slotName" safe on search

Realmente não estava, mas coloquei, o filtro funciona, mas a combo nao mantém o valor.


public function rules()

	{

return array(

// The following rule is used by search().

			// Please remove those attributes that should not be searched.

			array('slotName', 'safe', 'on'=>'search'),

);

}

agora que notei, tem uma inconsistencia no filter, o name do dropdown esta como ‘pageslot_id’ enquanto o valor esta registrado em slotName

ajuste para slotName que vai funcionar

o filter da grid tambem aceita valores em array, como só


CHtml::listData(NewsPageSlot::model()->findAll(), 'id', 'name')

para gerar o dropdown

Isso mesmo cara :D , quase lá. Agora gerou um problema estranho, na pagina 1 da grid filtra perfeitamente, mas na pagina 2 ocorre o seguinte erro:


PHP Error [2]


htmlspecialchars() expects parameter 1 to be string, array given (C:\Users\fabio.policeno\Documents\PHP\xampp\htdocs\EspiralEducadoraEF1\Dev\yii\framework\web\helpers\CHtml.php:66)

view


array(

			'name' => 'slotName',

			'filter' => CHtml::listData(NewsPageSlot::model()->findAll(), 'name', 'name')

		),

search


$criteria->select='t.*,newsPageSlots.name as slotName';

		$criteria->compare('newsPageSlots.name',$this->slotName);

show de bola

não saberia te dizer o que é, mas na action search veja os valores que está recebendo por get e que vão pra model, por algum motivo um desses valores está vindo como array

faça um


print_r($_GET['NewsPageSlot']);

pra ver o que vem

Amigos,

Este problema eu resolvo assim:

Em cada models sempre crio uma função para guardar as informações do banco, assim:




    public static function listEscolaridade($withName = true) {

        $list = array();


        $models = self::model()->findAll('idescolaridade!="9999"');

        foreach ($models as $model) {

            $list[$model->idescolaridade] = $withName ? $model->descricao : $model->descricao;

        }

        return $list;

    }



Na função search() deixo da mesma forma




    public function search() {

        // Warning: Please modify the following code to remove attributes that

        // should not be searched.


        $criteria = new CDbCriteria;


        $criteria->compare('idescolaridade', $this->idescolaridade);


        $criteria->compare('descricao', $this->descricao, true);


        return new CActiveDataProvider('escolaridade', array(

            'criteria' => $criteria,

        ));

    }



E no admin faço da seguinte forma




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

        'id' => 'colaborador-grid',

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

        'filter' => $model,

        'pager' => array('cssFile' => Yii::app()->baseUrl . '/css/gridViewStyle/gridView.css'),

        'cssFile' => Yii::app()->baseUrl . '/css/gridViewStyle/gridView.css',

        'htmlOptions' => array('class' => 'grid-view rounded'),

        'columns' => array(

            'nome',

            'idade',

            'temposerv',

            array(

                'name' => 'escolaridade',

                'value' => '$data->escolaridade0->descricao',

                'filter' => ccusto::model()->listEscolaridade(),

            ),

            array(

                'class' => 'CButtonColumn',

                'header' => 'Ações',

                'viewButtonImageUrl' => Yii::app()->baseUrl . '/css/gridViewStyle/images/' . 'gr-view.png',

                'updateButtonImageUrl' => Yii::app()->baseUrl . '/css/gridViewStyle/images/' . 'gr-update.png',

                'deleteButtonImageUrl' => Yii::app()->baseUrl . '/css/gridViewStyle/images/' . 'gr-delete.png',

            ),

        ),

    ));



Desta forma sempre dá tudo certo. Espero ter ajudado.

Sds,

Adriano Silva