画像のファイルのアップロードの基本を学んだところで、少し実践的なことを考えてみましょう。
例えば、ECサイトなら、販売する商品画像を管理画面でアップロードします。しかし、重複となるかもしれないので、アップロードした画像のファイル名で保存することはできません。
そこで、DBが自動発行する商品のIDあるいは商品番号を利用してファイル名を変えてサーバーに保存します。
例えば、Nikeの靴.jpgというファイル名のファイルをアップするなら、その商品のIDが234なら、234.jpgとしてサーバーに保存します。
もう1つ考慮必要なことは、アップする画像はいつも、JPEGの形式とは限らないことです。GIFかもしれませんし、PNGのフォーマットかもしれません。もちろん、手元でJPEGに変換してからアップもできますが、サーバーで違う画像フォーマットに対応できるならそれに越したことありません。
これらのフォーマットの違いの情報を対応するには、アップしたMIMEの情報あるいはファイルの拡張子をDBに保存する必要あります。
まず、DBの設計から始めましょう。ここで必要なのは以下の2つのテーブル、
+------------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+------------------+------+-----+---------+----------------+ | product_id | int(10) unsigned | NO | PRI | NULL | auto_increment | | name | varchar(255) | NO | | NULL | | | created_at | timestamp | YES | | NULL | | | updated_at | timestamp | YES | | NULL | | +------------+------------------+------+-----+---------+----------------+
+------------------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------------+------------------+------+-----+---------+----------------+ | product_image_id | int(10) unsigned | NO | PRI | NULL | auto_increment | | product_id | int(11) | NO | MUL | NULL | | | mime | varchar(255) | NO | | NULL | | | created_at | timestamp | YES | | NULL | | | updated_at | timestamp | YES | | NULL | | +------------------+------------------+------+-----+---------+----------------+
一応、productとproduct_imageのテーブルの関係は、1対多の関係となります。つまり、商品1に対して複数の画像を持つことが可能。
migrationを作成して、以下のように作成してテーブルを作成します。
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateProductTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('product', function (Blueprint $table) {
$table->increments('product_id');
$table->string('name');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('product');
}
}
class CreateProductImageTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('product_image', function (Blueprint $table) {
$table->increments('product_image_id');
$table->integer('product_id');
$table->string('mime');
$table->timestamps();
$table->index('product_id');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('product_image');
}
}
次は管理画面でのルートの作成。
...
Route::group(['prefix' => 'admin', 'middleware' => 'web'], function () {
...
Route::get('product/{product}/image', 'Admin\ProductController@getImage');
Route::post('product/{product}/image', 'Admin\ProductController@postImage');
Route::resource('product', 'Admin\ProductController');
...
});
...
CRUD(Create, Read, Update, Delete)のオペレーションは、Route::resource('product', ..に任せて、画像に関しては、product/{product}/imageのURIを使用して、それらのメソッドはすべてProductControllerに収めます。今回は、前回と違って管理画面での作業のことに注意してください。
それらの定義は、
namespace App\Http\Controllers\Admin;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Product;
use App\ProductImage;
class ProductController extends Controller
{
public function getImage(Product $product)
{
return view('admin/product_image', compact('product'));
}
public function postImage(Request $request, Product $product)
{
$file = $request->file('file');
$image = new ProductImage;
$image->product_id = $product->product_id;
$image->mime = $file->getClientMimeType();
$image->save(); // product_imageにレコードを作成
$image->storeImage($file); // アップロードしたファイルを移動
}
}
となり前回とほぼ同じようなコードです。しかし、前回と違って、product_imageのテーブルに、アップロードしたファイルのMIME情報を入れてレコードを作成しています。
namespace App;
use Illuminate\Database\Eloquent\Model;
class ProductImage extends Model
{
protected $table = 'product_image';
protected $primaryKey = 'product_image_id';
public $incrementing = true;
public $timestamps = true;
protected $fillable = [
'product_id', 'mime'
];
public function storeImage($file)
{
$file->move(public_path('images/product'), $this->filename());
}
public function filename()
{
$ext = 'jpg';
switch($this->mime)
{
case 'image/jpeg':
case 'image/jpg':
$ext = "jpg";
break;
case 'image/png':
$ext = "png";
break;
case 'image/gif':
$ext = "gif";
break;
}
return sprintf("%d.%s", $this->product_image_id, $ext);
}
}
filenameでは、レコードで自動発行されたproduct_image_idとMIMEをもとにした拡張子を合わせて移動先のファイル名を作成します。
コントローラにより使用されるテンプレートも少し違います。
@extends('user.layouts.app')
@section('content')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">File Upload</div>
<div class="panel-body">
<div class="form-group{{ $errors->has('file') ? ' has-error' : '' }}">
<div>
<form
method="POST"
action="/demo/public/admin/product/{{ $product->product_id }}/image"
class="dropzone"
id="imageUpload"
enctype="multipart/form-data">
{{ csrf_field() }}
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
@section('script')
Dropzone.options.imageUpload = {
dictDefaultMessage: 'アップロードするファイルをここへドロップしてください',
maxFiles: 10,
acceptedFiles: '.jpg,.jpeg,.gif,.png',
maxFilesize: 5, // 5 MB
}
@endsection
ファイルのアップロードの結果は以下のようにレコードの生成となります。
+------------------+------------+------------+---------------------+---------------------+ | product_image_id | product_id | mime | created_at | updated_at | +------------------+------------+------------+---------------------+---------------------+ | 1 | 1 | image/jpeg | 2016-05-15 19:46:24 | 2016-05-15 19:46:24 | | 2 | 1 | image/gif | 2016-05-15 19:46:24 | 2016-05-15 19:46:24 | | 3 | 1 | image/png | 2016-05-15 19:46:24 | 2016-05-15 19:46:24 | +------------------+------------+------------+---------------------+---------------------+メルマガ購読の申し込みはこちらから。
