カテゴリー : Yii

Yii アプリケーションへ初期設定値を与える簡便な方法

Zend Framework には Zend_Config という便利なクラスがあって、設定情報を簡単に扱える。Yii では CConfiguration が利用できそうだ。それを利用するのもいいがもっと簡単な方法は?

アプリケーション起動時にコアモジュールには「config/main.php」を利用できるのだが、確かチュートリアルにユーザー定義値を扱った操作が有ったのを思い出した。その度チュートリアルをひっくり返して確認するのも何なので、以下にその内容を。

アプリケーションのプロテクト領域(basePath)で、「config/main.php」にユーザー定義のパラメータファイルの読み込みを次のように設定し、「return array()」で定義したパラメータファイルを用意すればよい。

config/main.php 例

<?php
return array(
	:
	// application-level parameters that can be accessed
	// using Yii::app()->params['paraName']
	'params'=>require(dirname(__FILE__) . '/params.php'),
);

config/params.php 例

<?php
return array(
	'title'=>'チュートリアル',
	'adminEmail'=>'webmaster@example.com',
	'postsPerPage'=>10,
	'recentCommentCount'=>10,
	'tagCloudCount'=>20,
	'commentNeedApproval'=>true,
	'copyrightInfo'=>'Copyright &copy; 2010 by My Company',
);

パラメータにアクセスする際は、「Yii::app()->params[‘postPerPage’]」というようにすればよい。

(yii-1.1.1.r1907)

Yii 画像ファイルのアップロード処理について

画像ファイルのアップロードに関しては、クックブックの How to upload a file using a model にレシピがある。これを見て、作りたいと思う機能をサクッと作れるほど Yii に慣れ親しんでいないので、自分なりの解釈を記して見た。

ファイルアップロードの処理を行なう際、Ajax を利用するにしても、Yii を利用した場合はどのような作業フレームで、そしてどのクラスを利用すればいいのかの手掛かりになればと思います。

作業フレーム

MVC に合わせて、

  • 画像ファイル用のモデルを作る
    バリデーションのルール定義、ビュー用のタイトルなど
  • アップロード受付表示のビューを作る
    アップロードする情報の CHtml ヘルパーを利用したデザイン
  • アップロード処理のコントローラを作る
    アップロード受付表示、アップロードファイルの保存処理など

モデルは、ファイルタイプやサイズ制限などを「rules()」に指定して、バリデーションを利用する。また、「attributeLabels()」を利用してビューでラベル表示する。親クラスには「CFormModel」を利用する。

ビューには、ファイル選択入力と既存画像の表示を行なうようにする。form タグに指定する enctype は、ヘルパーでパラメータを与える。

コントローラでは、アップロード受付表示処理とアップロード保存処理を、それぞれ別のアクションで実行するようにしてみた。アクセス認証は割愛した。

次の画像はアップロードでエラーが出たときの表示例。

プログラム

モデル

class ImageForm extends CFormModel
{
	public $image;

	public function rules() {
		return array(
			array('image', 'file', 'types'=>'jpg,jpeg,gif,png', 'maxSize'=>30720),
			array('image', 'required'),
		);
	}

	public function attributeLabels() {
		return array(
			'image'=>'ファイルパス',
		);
	}
}

バリデーションルールの「’file’」は、組み込み済みで「CFileValidator」が利用される。ここでのチェックは、ファイル拡張子名とファイルサイズを設定した。

アップロードの受付表示画面は、画像の一覧表示をするようにし、コントローラ/アクションを「image/index」とした。ビューファイルは「views/image/index.php」とする。

<div class="form">
<?php $form = $this->beginWidget('CActiveForm', array(
	'id'=>'post-form',
	'action'=>$this->createUrl('image/upload'),
	'htmlOptions'=>array('enctype'=>'multipart/form-data'),
	'enableAjaxValidation'=>false,
));
?>
	<p class="note">「*」は必須項目です。</p>
	<?php echo $form->errorSummary($model); ?>
	<div class="row">
		<?php echo $form->labelEx($model, 'image'); ?>
		<?php echo $form->fileField($model, 'image'); ?>
		<?php echo $form->error($model, 'image'); ?>
	</div>
	<div class="row buttons">
		<?php echo CHtml::submitButton('アップロード'); ?>
	</div>
<?php $this->endWidget(); ?>

<?php foreach ($imgfs as $imgf) : ?>
	<p><img src="<?php echo $imgurl . $imgf; ?>" /></p>
<?php endforeach; ?>

デモの blog で表示するように合わせてみた。

最後にコントローラだ。コントローラは、index と upload の2つのアクションを準備する。

class ImageController extends Controller
{
	const IMAGE_PATH = '/www/images';
	const IMAGE_URL = '/images';

	public function actionIndex()
	{
		$model = new ImageForm;

		$errmsg = Yii::app()->user->getFlash('image');
		if (!empty($errmsg)) {
			$model->addError('', $errmsg);
		}

		$imgfpaths = CFileHelper::findFiles(self::IMAGE_PATH, array(
			'fileTypes'=>array('jpg','JPG','gif','GIF','png','PNG','jpeg','JPEG')));
		$imgfs = array();
		foreach ($imgfpaths as $imgfpath) {
			$imgfs[] = substr($imgfpath, strrpos($imgfpath, DIRECTORY_SEPARATOR));
		}
		$this->render('index', array(
			'model'=>$model,
			'imgurl'=>self::IMAGE_URL,
			'imgfs'=>$imgfs,
		));
	}

	public function actionUpload()
	{
		$model = new ImageForm;
		if (isset($_POST['ImageForm'])) {
			$model->attributes=$_POST['ImageForm'];
			// get an instance of CUploadFile object
			$upfile = CUploadedFile::getInstance($model, 'image');
			if ($upfile !==null) {
				$model->image = $upfile->name;
				if ($model->validate()) {
					if (!$upfile->saveAs(self::IMAGE_PATH . "/{$model->image}")) {
						Yii::app()->user->setFlash('image', 'アップロードファイルの保存でエラーが発生しました。');
					}
				} else {
					if ($model->hasErrors()) {
						Yii::app()->user->setFlash('image', $model->getError('image'));
					} else {
						Yii::app()->user->setFlash('image', 'Validate error occured. Sorry.');
					}
				}
			} else {
				Yii::app()->user->setFlash('image', '画像ファイルを指定して下さい。');
			}
		} else {
			throw new CHttpException('404', 'The requested page does not exist');
		}
		$this->redirect($this->createUrl('index'));
	}
}

エラーメッセージは、画像ファイルの保存を upload アクションで実行し、リダイレクトで index 画面に戻すため、次のセッションに持ち越す必要がある。そこで、ユーザーセッションにメッセージを保存することにした。

アップロードファイルのバリデーションはモデルで指定したように、組み込み済みのオブジェクトを利用する。画像サイズのチェックとかも行ないたいならば、メソッドをモデルに定義すればいいだろう。

(yii-1.1.1.r1907)

Yii 1.1.1 の2カラムメニューを以前の1カラムにする

バージョンアップにより、スキャフォールド(yiic)で生成される CRUD のビューに係る部分で、それまで1カラム表示だったのが2カラム表示へ変更された。それに合わせて、サブメニューが右側に表示されるようになった。

実は以前作った CMS で、サブメニューを今回の 1.1.1 のように右側に表示するようにしたところ、結構使い難かった。そのためそれ以降は、サブメニューはメインメニューの下側へ表示するようにしている。

ということで、yiic で吐き出されたソースを変更して 1.1.0 で表示していたようにしたいと思う。

編集は以下の3点だ。

  • コントローラの「$layout=’coloumn2’」プロパティを削除
  • ビューで widget を利用して「CMenu」で表示するように追加
  • CSS で以前定義されていた「.action」を移植

3番目の CSS の指定で、main.css から「action」クラスとそれに関する「li,a,a:hover」などの定義が削除されてしまっていた。そこで、1.1.0 の main.css からその部分をコピーした。

1番目だが、yiic はコントローラのプロパティに $layout=’column2′ を書き出してくる。コントローラは components/Controller.php から派生するようだが、ここにデフォルトレイアウトの指定で「column1」を設定している。書き出すよりこちらを「column2」にした方が、良かったような気がするのだが。

最後にビューの変更だ。以前のサブメニューは、ulタグに actions クラス指定をして、直接メニューを html で記述していた。1.1.1 ではコントローラのメンバに $menu があり、そこにメニューのパラメータを配列指定している。CMenu を利用することでそれをそのまま使える。

$this->menu=array(
	array('label'=>'List user', 'url'=>array('index')),
	array('label'=>'Create user', 'url'=>array('create')),
);

$this->widget('zii.widgets.CMenu', array(
	'htmlOptions'=>array(
		'class'=>'actions',
	),
	'items'=>$this->menu,
));

ulタグに actions クラス指定を忘れないように。

(yii-1.1.1.r1907)

Yii Blog Tutorial コメント入力にキャプチャを表示し、検証を追加する

サイト訪問者からのコメントは、ブログを公開している者にとって嬉しいものだ。しかしコメント欄はスパム攻撃の対象にされやすい。特にプログラムを利用したスパムは、定期的に送られてくるので削除してもキリがない。そこでプログラムによる自動スパムコメントを排除する方法の1つとして、キャプチャ(CAPTCHA)を利用する方法がある。

Yii のブログチュートリアルを見ると、コンタクトページにキャプチャが利用されている。Yii にはキャプチャを利用するための仕組みが既に用意されているようだ。そこで、コメント入力にキャプチャによる入力の検証機能を追加してみた。

キャプチャ画像の表示や検証は、Yii のオブジェクトを組み込めば自分でプログラミングすることは何もないので、次のようにして簡単に組み込むことができた。

組み込み

キャプチャ画像の表示はビューへ、検証はコントローラへ、検証を実行するためのトリガーはモデルへそれぞれ指定する。

先ずはキャプチャ画像に対する入力項目をコメントモデルに追加する。Comment クラスに「public $verifyCode;」をアトリビュートとして追加する。

そして、バリデーションチェックでキャプチャの検証を実行するトリガーになるように、rules メソッドに「’captcha’」を追加する。

class Comment extends CActiveRecord
{
	public $verifyCode;

	public function rules()
	{
		return array(
			array('author, email, content', 'required'),
			array('author, email, url', 'length', 'max'=>128),
			array('email', 'email'),
			array('url', 'url'),
			array('content', 'safe'),
			array('verifyCode', 'captcha', 'allowEmpty'=>!extension_loaded('gd')),
		);
	}

キャプチャ画像生成に PHP の gd を利用するようなので、gd がインストールされていないと画像は表示されないだろう。

検証はコントローラで「captcha」アクションを用意し、「CCaptchaAction」オブジェクトを利用して自動で行なえるようになっている。ところで Yii のコントローラでアクションメソッドを用意する方法として、直接「public function actionIndex()」のようにプログラムを書く以外に、アクション名とそこで実行するオブジェクトのクラスを指定するだけで、モジュールを組み込む方法が用意されている。それを利用するにはコントローラに、「actions」メソッドを追加して必要なパラメータを書き込めばいいようだ。チュートリアルの Site コントローラのまねをして、Post コントローラに以下のメソッドを追加する。

class PostController extends Controller
{
	public function actions()
	{
		return array(
			'captcha'=>array(
				'class'=>'CCaptchaAction',
				'backColor'=>0xffffff,
			),
		);
	}

	public function accessRules()
	{
		return array(
			array('allow',
				'actions'=>array('index', 'view', 'captcha'),
				'users'=>array('*'),
			),
		:

アクションでアクセス制御を行なっているようであれば、上の例のように「captcha」を追加する。そうしないと次のビューでキャプチャ画像を表示するように指定しても、画像が表示されない。

コメント入力欄にキャプチャ画像を表示するための指定を、「CHtml::beginForm」と「CHtml::endForm」の間に挟み込む。

<div class="form">
<?php echo CHtml::beginForm(); ?>
 :
<?php if (extension_loaded('gd')) : ?>
	<div class="row">
		<div>
			<?php $this->widget('CCaptcha'); ?>
			<?php echo CHtml::activeTextField($model, 'verifyCode'); ?>
		</div>
		<div class="hint">表示されている文字を入力してください。<br />大文字小文字どちらでも構いません。</div>
	</div>
<?php endif; ?>

	<div class"row buttons">
		<?php echo CHtml::submitButton($model->isNewRecord ? '投稿' : '保存'); ?>
	</div&gh;
<?php echo CHtml::endForm(); ?>

(yii-1.1.0.r1700)

Yii TinyMCE textarea編集にjQuery版TinyMCEを組み込む

表示内容に画像を含む文章を掲載したいとき、利用者に textarea タグを使ったボックスへ直接 HTML を入力させるのは、開発者として悩ましいところがある。解決策の1つとして JavaScript の wysiwyg エディタとして有名で、今でも開発が続いている TinyMCE の jQuery 版を Yii のシステムに組み込んでみる。


組み込みは、大きく2つの異なる設定がある。1つは、JavaScript の TinyMCE jQuery 版を Yii にどうやって組み込むか。もう一つは、高機能な TinyMCE で必要とする機能の選択と、表示の仕方の設定である。

1.Yii に TinyMCE jQuery 版を組み込む

JavaScript を表示ページに組み込む際、自分はできればヘッダ部に組み込みたい方だ。しかしヘッダ部を含む layout ファイルに直接組み込むと、利用しないページに JavaScript が読み込まれてしまうので、それは避けたい。

動的に組み込む方法として、JavaScript が必要となるアクションで CClientScript クラスを利用すれば、ヘッダ部やボディ部など指定位置に挿入することができる。なおこのクラスは、アプリケーションコアコンポーネントにも指定されている。

今回行なった方法は読込みの順番とその実行動作の関係で、layout ファイルに Google サイトから jQuery を読み込む指定をし、create(update)アクションで TinyMCE のソースを読み込むようにした。

<?php echo CGoogleApi::init(); ?>
<?php echo CHtml::script(CGoogleApi::load('jquery', '1.4.2') . "\n"); ?>
<title>タイトル</title>

title タグの上に、Google の jsapi をロードして google.load メソッドで jQuery を読み込むように、Yii で用意されたクラスを利用した。

<?php
	$cs = Yii::app()->clientScript;
	$cs->registerScriptFile(Yii::app()->baseUrl . "/js/tiny_mce/jquery.tinymce.js", CClientScript::POS_HEAD);
	$cs->registerScriptFile(Yii::app()->baseUrl . "/js/tiny_mce/init.js", CClientScript::POS_BEGIN);
?>

create(update)アクションの上部に、上のようなソースを挿入して TinyMCE の jQuery 版の読み込みをヘッダ部に読み込むように指定し、TinyMCE の設定ファイルは、ヘッダ部の開始直後に読み込むようにした。

Yii 1.1.1-r1907 (100319)

本バージョンで jQeury を Google サイトから取り込むように指定すると、ビューから出力される HTML を囲む id=contents のdiv タグが何故か出力されなくなった。

そこで Google サイトからロードを止め、Yii にバンドルされた jQuery を利用するように _form ビューの先頭に、次のように各jsソースを読み込むように変更した。なお、config/main.php の components で clientScript 初期設定部分は削除した。

<php
	$cs = Yii::app()->clientScript;
	$cs->registerCoreScript('jquery');
	$cs->registerScriptFile(Yii::app()->baseUrl . "/js/tiny_mce/jquery.tinymce.js", CClientScript::POS_HEAD);
	$cs->registerScriptFile(Yii::app()->baseUrl . "/js/tiny_mce/init.js", CClientScript::POS_HEAD);
?>

2. TinyMCE の機能選択と設定

create(update)アクションのページが表示された際、TinyMCE を起動し、textarea タグ領域を図のようにエディタ表示するため、init.js を実行している。その init.js で起動時に初期値を与えて機能の選択や表示の仕方を決めている。下の例は機能の選択、ボタンの配置を自分なりに取捨選択したものなので、各人の好みに合わせてまとめて下さい。

jQuery(document).ready(function($) {
	$('textarea').tinymce({
		script_url : "/js/tiny_mce/tiny_mce.js",
		language : "ja",
		theme : "advanced",
		theme_advanced_toolbar_location : "top",
		theme_advanced_toolabr_align : "left",
		theme_advanced_source_editor_width : 500,
		theme_advanced_source_editor_height : 400,
		theme_advanced_statusbar_location : "bottom",
		theme_advanced_resizing : true,
		theme_advanced_resize_horizontal : false,

		plugins : "table,advhr,emotions,media,advlist",
		theme_advanced_buttons1 : "code,|,undo,redo,cleanup,|,justifyleft,justifycenter,justifyright,justifyfull,|,bullist,numlist,|,outdent,indent,blockquote,|,link,unlink,anchor,image,media",
		theme_advanced_buttons2 : "bold,italic,underline,strikethrough,|,sub,sup,|,forecolor,backcolor,|,charmap,emotions,|,advhr,|,fontsizeselect,formatselect",
		theme_advanced_buttons3 : "tablecontrols,|,visualaid,|,help",

		content_css : "/js/tiny_mce/content.css"
	});
});

最後に、jQuery ライブラリが2回挿入されてしまう問題の回避についてである。

layout ファイルに今回のように jQuery を組み込んだ場合、Yii が提供する Manager 機能で利用する jQuery の読込みがダブってしまう。その回避方法が、スクリプトファイルを最小化するに掲載されている。

それは、設定ファイル(config/main.php)に以下のようにパラメータを設定しておくことだ。

return array(
	'components'=>array(
		'clientScript'=>array(
			'jquery.js'=>false,
		),
	),
	:

(yii-1.1.0.r1700), (tinymce_3_3rc1_jquery)