Yii

Yii actionAdminのsearch-formとCGridViewで悪戦苦闘

Ver.1.1.1 になってAjax検索やフィルタが追加されたのだが、スキャフォールドで吐き出されたアクションやビューを見たり、あるいはドキュメントを見ても使い方がよく分からない。そこでドキュメントの例など見ながらいろいろ操作して、結果何となく動くようになった今の時点でのメモです。

データが GridView に表示されない

吐き出されたビューには、表のタイトル直下にフィルタ入力用の項目が差し込まれる。また、「Advanced Search」リンクボタンにより全ての項目を含む検索ブロックが利用できる。それぞれの検索条件は、どうやらモデルに出力されたメソッド「search()」を利用するようだ。

ところで最初の表示でそれらの項目には、テーブル定義で設定した default 値がセットされる。そのため、数値項目で「0」をデフォルトに指定しているとそれを一致条件として検索するため、データが何も表示されないハメになるようだ。

回避策として、$model インスタンスのテーブル項目属性に null など初期値を与えておくとよい。

Grid View のフィルタを使わないようにしたい

CGridView ウィジェットに与えるパラメータに「’filter’=>$model」と言うのがあり、この「filter」パラメータを取り去ってしまえばよい。

Grid View の表示項目にリレーションデータを表示したい

例えば、モデルで以下のように定義されていたとする。

public function relations() {
	return array(
		'category' => array(self::BELONGS_TO, 'Category', 'category_id'),
	);
}

ビューでカテゴリ名を表示したいとき

<?php $this->widget('zii.widgets.grid.CGridView', array(
	'dataProvider'=>$dataProvider,
	'columns'=>array(
		'title',
		array(
			'name'=>'カテゴリ名',
			'value'=>'$data->category->name',
		),
		array(
			'class'=>'CButtonColumn',
			'template'=>'{view} {update}',
			'viewButtonUrl'=>'Yii::app()->createUrl("/post/archive", array("id"=>$data->id))',
		),
	),
)); ?>

のようにして関係データを表示できる。

また、編集などのボタンリンクは、「template」パラメータで「{delete}」を指定しない事で2つだけにしたり、「viewButtonUrl」でリンク先を変更したりできる。

sub query を利用したデータを使いたい

例えば、post データの中からカテゴリ別に最新の記事の一覧を表示したいとする。post データには category_id 項目があるとする。SQL の副問い合わせを利用して

$sql = "SELECT * FROM post WHERE id IN "
	. "(SELECT MAX(id) FROM post GROUP BY category_id);"; 
$posts = Post::model()->findAllBySql($sql);

と検索できる。これは残念ながら CGridView の data provider には使えないようだ。同じような質問がフォーラムに上がっていた。
Using addInCondition() with sub-query

そこで、それを参考に強引にやったやり方が次の通り。

$posts = Post::model()->findAll(array(
	'select'=>array(new CDbExpression('MAX(id) as id')),
	'group'=>'category_id',
));

foreach ($posts as $post) {
	$ids[] = $post->id;
}

$cr = new CDbCriteria;
$cr->addInCondition('id', $ids);
$dataProvider = new CActiveDataProvider('Post', array(
	'criteira'=>$cr,
));

$this->render('admin', array(
	'dataProvider'=>$dataProvider,
));

この中で、関数 MAX() を使った場合、オブジェクトに項目のデータをどうやって引き込むのか悩んだ。取りあえずプログラム例のようにやったらできた、と言うだけ。本当の使い方はよく分かりません。なお使われている SQL を見ると、MAX(id) は、MAX(t.id) の方がいいかも知れません。

ページネーションできるかこれから検討です。— (できました)

CGridView の項目(カラム)のデータにリンクを貼りたい

カテゴリ名をクリックすると、そのカテゴリのアーカイブを表示する処理へ移るリンクを貼りたいときは、どうすればいいのだろうか?以下のようにするとできた。

<?php $this->widget('zii.widgets.grid.CGridView', array(
	'dataProvider'=>$dataProvider,
	'columns'=>array(
		'title',
		array(
			'header'=>'カテゴリ名',
			'class'=>'CLinkColumn',
			'labelExpression'=>'$data->category->name',
			'urlExpression'=>'array("archive/category", "id"=>$data->category->id)',
		),
		array(
			'class'=>'CButtonColumn',
			'template'=>'{view} {update}',
			'viewButtonUrl'=>'Yii::app()->createUrl("/post/archive", array("id"=>$data->id))',
		),
	),
)); ?>

どうやら html の部品は、framework/zii/widgets/grid にあるクラスを使えばいいようだ。時間が掛った … orz

ところで、value の指定に 「CHtml::link(“label”, url)」を利用すると、タグがhtmlエンティティに置き換えられる(‘type’=>’raw’の指定をすればいいことが後で分かった)。

それと、ここで指定された処理は eval() で実行されるため、eval() を利用しない方法として、call_user_func_array() で実行する方法がフォーラムにありました。

avoid eval() on CGridView columns’ values

(yii-1.1.1.r1907)