Yii File Upload

作成意図

alltime upload disable:

今回はファイルアップロード。Yiiをつかう:その3で書いた簡易記法を用いて、各種ページにアップロードフォームを埋め込みたい、というのが今回の主旨。

個人的に使うだけなのでともかくアップロードが出来ればいい。

なお、本サイトのページにはFacebookのOAuthを用いてアクセス制限をかけるようにしている(これは別の機会に)。

アクセス制限がかかっていない場合は、最初から利用不可能にする。

利用不可能の場合、フォームだけは表示される。

{{upload=利用可能フラグ=アップロードファイルサイズ上限}}

アクセス制限がかかっていて AND そのユーザがそのページにアクセス出来て AND

{{upload=on}}

という風に利用可能フラグがonならば利用可能。

さらに

{{upload=on=30000}}

なら約30KBまでのファイルのアップロード可。数値をかかなければデフォルト値(これは後述のFormモデルのrulesで決めている)。

このページは、

{{upload=on=30000}}

と設定しているが、もともとアクセス制限がかかっていないページなので冒頭フォーム上部に「alltime upload disable:」と表示されているはず。

コード

Formモデルを作る

アップロードファイルのルール(validation ルール)を定めるためにForm Modelを作成する。最低限rulesメソッドだけは必要なのだ。

http://www.yiiframework.com/doc/guide/1.1/ja/form.model

models/FileUpload.php

<?php
class FileUpload extends CFormModel
{
  public $file;
  public $fileUpCheck;
  public $maxSize=100000;
  /**
   * @return array validation rules for model attributes.
   */
  public function rules()
  {
    $ruleOne = array(
        'file',
	'file',
	'allowEmpty'=>true,
	'maxSize'=>$this->maxSize,
	'types'=>'jpg,jpeg,png,pdf,csv'
    );

    $ruleTwo = array(
        'fileUpCheck',
	'required',
	'requiredValue'=>秘密
    );

    return array($ruleOne,$ruleTow);
  }
}

rulesで設定された配列は、validataメソッドがコールされると適用される。

http://www.yiiframework.com/doc/api/1.1/CModel#rules-detail

array('attribute list', 'validator name', 'on'=>'scenario name', ...validation parameters...)

一番目と二番目は必須。

  • attribute list は検証を必要とする属性attributes(変数)名。カンマ区切りで入れられる。
  • validator name はその検証を行うバリデータクラスの指定。

validator nameにはビルトインされている各種バリデータクラスのエイリアス(別名)を指定できる。今回はfile(CFileValidator)を使う。

なお、validateしたい変数は必ずpublic 変数宣言が必要(ここでは$fileと$fileUpCheck)。

'on'=>'scenario name'

というのはオプション。モデルをnewした時に、その「シナリオ名」が指定されていれば、そのシナリオでの検証配列となる。今回は指定していない。上記配列でわかるように複数の配列が指定可能なので、シナリオで検証条件を分岐させることが出来るわけだ。

validation parameters

これは検証に利用する各種パラメタ。実際はこのパラメタ設定がミソになる。

バリデタクラスによる検証

$ruleOne,$ruleTowそれぞれの「フォーム属性」について、どのような検証をするのかについて、リストがhttp://www.yiiframework.com/wiki/56/に纏まっている。

今回の場合、

$ruleOneのfileでは

  • allowEmpty, whether the attribute requires a file to be uploaded or not.
  • maxFiles, the maximum file count the given attribute can hold.
  • maxSize, the maximum number of bytes required for the uploaded file.
  • minSize, the minimum number of bytes required for the uploaded file.
  • tooLarge, the error message used when the uploaded file is too large.
  • tooMany, the error message used if the count of multiple uploads exceeds limit.
  • tooSmall, the error message used when the uploaded file is too small.
  • types, a list of file name extensions that are allowed to be uploaded.
  • wrongType, the error message used when the uploaded file has an extension name that is not listed among extensions.

がチェックできる。

$ruleTwoのrequiredでは

  • requiredValue, the desired value that the attribute must have.
  • strict, whether the comparison to requiredValue is strict.

がチェック出来る。

fileにについては

	'allowEmpty'=>true,
	'maxSize'=>$this->maxSize,
	'types'=>'jpg,jpeg,png,pdf,csv'

requiredについては

'requiredValue'=>秘密

後者の場合、秘密です、というのは大げさで一意のmd5を入れている。ポスト時のチェック用である。

upload用のビューファイルを作る。

簡易記法の一つなのでYiiをつかう:その3にあるように、

protected/extensions/htmlparse/views/upload.php

としてupload用ビューファイルをつくる。

CForm

CHtmlクラスのFormに関するメソッドを使ってもいいが、今回はフォームビルダークラスであるCFormで一挙作成する。

http://www.yiiframework.com/wiki/265/file-uploads-in-cform-form-builder/のThe CForm Arrayをほぼそのまま踏襲する。

実際は、下記のようにconfig/yaml.d/params.ymlで一括指定している。

elementsが2つあるのは、上のモデルの検証属性と対応している。

uploadForm:
 title: 'upload file'
 attributes:
  enctype: 'multipart/form-data'
 elements:
  file:
   type: 'file'
  fileUpCheck:
   type: hidden
   value: NULL
 buttons:
  reset:
   type: 'reset'
   label: 'reset'
  submit:
   type: 'submit'
   label: 'upload'

upload.phpの骨格

極端な話4行でフォームが書ける。

$conf = Yii::app()->params['uploadForm'];

$model = new FileUpload();

$form = new CForm($conf,$model);

echo $form;

あとは、投稿後のチェックと保存。これはControllerで行う

Controllerで保存

ルーターで分岐はしない。$_POSTの値をチェックして、主コントローラーにuploadメソッドを書く。

もし$_POSTでFileUploadが変数があれば以下のupload()メソッドを稼働させたのちにページを描画する。

private function upload()
{
  $model = new FileUpload();
  $model->attributes=$_POST['FileUpload'];
  $file = CUploadedFile::getInstance($model,'file');
  if(!$file)
     return;
  if($model->validate())
  {
  $dir = 投稿URL直下のディレクトリに投稿用パスをつくる。
    if(!is_dir($dir))
        mkdir($dir);
    $path = $dir.$file->name;
    if($file->saveAs($path))
       $this->redirect(array(Yii::app()->request->url));
  }
}

感想

最初、慣れるのに大変な気がしたが、極めて合理的。

短いコードで書けるのがすばらしい。ただCFormは自由度がなくて辛い感じがした。少々行数が増えてもCHtmlでやったほうが視認性が高まる気がした。いつまでたっても抽象思考は苦手である。

this file --> last modified:2012-03-31 13:57:51