ファイルをアップロードする ========================== Yii におけるファイルのアップロードは、フォームモデル、その検証規則、そして、いくらかのコントローラコードによって行われます。 アップロードを適切に処理するために何が必要とされるのか、見ていきましよう。 一つのファイルをアップロードする -------------------------------- まず最初に、ファイルのアップロードを処理するモデルを作成する必要があります。 次の内容を持つ `models/UploadForm.php` を作って作成してください。 ```php namespace app\models; use yii\base\Model; use yii\web\UploadedFile; /** * UploadForm がアップロードのフォームの背後にあるモデルである。 */ class UploadForm extends Model { /** * @var UploadedFile file 属性 */ public $file; /** * @return array 検証規則 */ public function rules() { return [ [['file'], 'file'], ]; } } ``` 上記のコードにおいて作成した `UploadForm` というモデルは、HTML フォームで `` となる `$file` という属性を持ちます。 この属性は [[yii\validators\FileValidator|FileValidator]] を使用する `file` という検証規則を持ちます。 ### フォームのビュー 次に、フォームを表示するビューを作成します。 ```php ['enctype' => 'multipart/form-data']]) ?> field($model, 'file')->fileInput() ?> ``` ファイルのアップロードを可能にする `'enctype' => 'multipart/form-data'` は不可欠です。 `fileInput()` がフォームの入力フィールドを表します。 ### コントローラ そして、フォームとモデルを結び付けるコントローラを作成します。 ```php namespace app\controllers; use Yii; use yii\web\Controller; use app\models\UploadForm; use yii\web\UploadedFile; class SiteController extends Controller { public function actionUpload() { $model = new UploadForm(); if (Yii::$app->request->isPost) { $model->file = UploadedFile::getInstance($model, 'file'); if ($model->file && $model->validate()) { $model->file->saveAs('uploads/' . $model->file->baseName . '.' . $model->file->extension); } } return $this->render('upload', ['model' => $model]); } } ``` `model->load(...)` の代りに `UploadedFile::getInstance(...)` を使っています。 [[\yii\web\UploadedFile|UploadedFile]] はモデルの検証を実行せず、アップロードされたファイルに関する情報を提供するだけです。 そのため、`$model->validate()` を手作業で実行して、[[yii\validators\FileValidator|FileValidator]] を起動する必要があります。 [[yii\validators\FileValidator|FileValidator]] は、下記のコアコードが示しているように、属性がファイルであることを要求します。 ```php if (!$file instanceof UploadedFile || $file->error == UPLOAD_ERR_NO_FILE) { return [$this->uploadRequired, []]; // "ファイルをアップロードしてください。" というエラーメッセージ } ``` 検証が成功したら、ファイルを保存します。 ```php $model->file->saveAs('uploads/' . $model->file->baseName . '.' . $model->file->extension); ``` 「ベーシック」アプリケーションテンプレートを使っている場合は、`uploads` フォルダを `web` の下に作成しなければなりません。 以上です。ページをロードして、アップロードを試して見てください。ファイルは `basic/web/uploads` にアップロードされます。 検証 ---- たいていの場合、検証規則を調整して、特定のファイルだけを受け取るようにしたり、アップロードを必須としたりする必要があります。 下記で、よく使われる規則の構成を見てみましよう。 ### Required ファイルのアップロードを必須とする必要がある場合は、次のように `skipOnEmpty` を `false` に設定します。 ```php public function rules() { return [ [['file'], 'file', 'skipOnEmpty' => false], ]; } ``` ### MIME タイプ アップロードされるファイルのタイプを検証することは賢明なことです。 `FileValidator` はこの目的のための `extensions` プロパティを持っています。 ```php public function rules() { return [ [['file'], 'file', 'extensions' => 'gif, jpg',], ]; } ``` ただし、ファイル拡張子が検証されるだけで、実際のファイルの中身は検証されないことを憶えておいてください。 ファイルの中身も検証するためには、`FileValidator` の `mimeType` プロパティを使います。 ```php public function rules() { return [ [['file'], 'file', 'extensions' => 'jpg, png', 'mimeTypes' => 'image/jpeg, image/png',], ]; } ``` [一般的なメディアタイプの一覧表](http://en.wikipedia.org/wiki/Internet_media_type#List_of_common_media_types) ### 画像のプロパティ 画像をアップロードするときは、[[yii\validators\ImageValidator|ImageValidator]] が重宝するでしょう。 このバリデータは、属性が有効な画像を受け取ったか否かを検証します。 その画像は、[Imagine エクステンション](https://github.com/yiisoft/yii2/tree/master/extensions/imagine) によって、保存するか、または、処理することが出来ます。 複数のファイルをアップロードする -------------------------------- 複数のファイルを一度にアップロードする必要がある場合は、少し修正が必要になります。 モデル: ```php class UploadForm extends Model { /** * @var UploadedFile|Null ファイル属性 */ public $file; /** * @return array 検証規則 */ public function rules() { return [ [['file'], 'file', 'maxFiles' => 10], // <--- ここ ! ]; } } ``` ビュー: ```php ['enctype' => 'multipart/form-data']]); ?> field($model, 'file[]')->fileInput(['multiple' => true]) ?> ``` 違いがあるのは、次の行です。 ```php field($model, 'file[]')->fileInput(['multiple' => true]) ?> ``` コントローラ: ```php namespace app\controllers; use Yii; use yii\web\Controller; use app\models\UploadForm; use yii\web\UploadedFile; class SiteController extends Controller { public function actionUpload() { $model = new UploadForm(); if (Yii::$app->request->isPost) { $model->file = UploadedFile::getInstances($model, 'file'); if ($model->file && $model->validate()) { foreach ($model->file as $file) { $file->saveAs('uploads/' . $file->baseName . '.' . $file->extension); } } } return $this->render('upload', ['model' => $model]); } } ``` 単一のファイルのアップロードとは、二つの点で異なります。 最初の違いは、`UploadedFile::getInstance($model, 'file');` の代りに `UploadedFile::getInstances($model, 'file');` が使用されることです。 前者が一つのインスタンスを返すだけなのに対して、後者はアップロードされた **全ての** ファイルのインスタンスを返します。 第二の違いは、`foreach` によって、全てのファイルをそれぞれ保存している点です。