画像のファイルのアップロードの基本を学んだところで、少し実践的なことを考えてみましょう。
例えば、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 | +------------------+------------+------------+---------------------+---------------------+メルマガ購読の申し込みはこちらから。