Yii

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)