Laravelで画像ファイルアップロードをする簡単なサンプル

Laravelを使ってユーザーのavatar画像をアップロードする想定で、アップロード機能のサンプルを実装してみます。

開発環境

* Laravel 5.3
* Laravel Homestead
  * PHP7.1系
  * MySQL 5.7系 
  * Nginx


# ベースとなる画面をmakeする

```bash
$ php artisan make:auth
```

するとこれだけのファイルが書き出されます。

```git
	new file:   app/Http/Controllers/HomeController.php
	new file:   resources/views/auth/login.blade.php
	new file:   resources/views/auth/passwords/email.blade.php
	new file:   resources/views/auth/passwords/reset.blade.php
	new file:   resources/views/auth/register.blade.php
	new file:   resources/views/home.blade.php
	new file:   resources/views/layouts/app.blade.php
	modified:   routes/web.php
```

DBのmigrationも実行しておきましょう。

```bash
$ php artisan migrate
Migration table created successfully.
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table
```

ここまでやると、ブラウザからアクセスしたときに右上に `REGISTER` というリンクができているので、画面に従ってユーザー登録＆ログインします。

（前提環境準備終了）これからが本番です。

## フォームを作成する

ファイルアップロードするフォームを追加します。
ここでは説明を省略するため、Dashboard画面に追加しますがユーザー情報変更画面などに追加するのが良いでしょう。

```(抜粋)resources/views/home.blade.php
{!! Form::open(['url' => '/upload', 'method' => 'post', 'files' => true]) !!}

{{--成功時のメッセージ--}}
@if (session('success'))
<div class="alert alert-success">{{ session('success') }}</div>
@endif
{{-- エラーメッセージ --}}
@if ($errors->any())
    <div class="alert alert-danger">
    <ul>
        @foreach ($errors->all() as $error)
            <li>{{ $error }}</li>
        @endforeach
    </ul>
    </div>
@endif

<div class="form-group">
    @if ($user->avatar_filename)
        <p>
            <img src="{{ asset('storage/avatar/' . $user->avatar_filename) }}" alt="avatar" />
        </p>
    @endif
    {!! Form::label('file', '画像アップロード', ['class' => 'control-label']) !!}
    {!! Form::file('file') !!}
</div>

<div class="form-group">
    {!! Form::submit('アップロード', ['class' => 'btn btn-default']) !!}
</div>
{!! Form::close() !!}
```

## Routeを追加

```(末尾に追加)routes/web.php
Route::post('/upload', 'HomeController@upload');
```

## Controllerを実装

リファクタリングの余地はたくさんありますが、ここではかんたんな実装ということでControllerに処理を書いていきます。

```app/Http/Controllers/HomeController.php
<?php

namespace App\Http\Controllers;

use App\User;
use Illuminate\Http\Request;

class HomeController extends Controller
{
    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('auth');
    }

    /**
     * Show the application dashboard.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        $user = User::find(auth()->id());

        return view('home', compact('user'));
    }


    /**
     * ファイルアップロード処理
     */
    public function upload(Request $request)
    {
        $this->validate($request, [
            'file' => [
                // 必須
                'required',
                // アップロードされたファイルであること
                'file',
                // 画像ファイルであること
                'image',
                // MIMEタイプを指定
                'mimes:jpeg,png',
                // 最小縦横120px 最大縦横400px
                'dimensions:min_width=120,min_height=120,max_width=400,max_height=400',
            ]
        ]);

        if ($request->file('file')->isValid([])) {
            $filename = $request->file->store('public/avatar');

            $user = User::find(auth()->id());
            $user->avatar_filename = basename($filename);
            $user->save();

            return redirect('/home')->with('success', '保存しました。');
        } else {
            return redirect()
                ->back()
                ->withInput()
                ->withErrors(['file' => '画像がアップロードされていないか不正なデータです。']);
        }
    }
}
```

## Migration

usersテーブルにカラムを追加してavatar画像のファイル名を保存することにしたので、Migrationファイルを作成します。

```bash
$ php artisan make:migration add_avater_filename --table=users
```

```php
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class AddAvatarFilename extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('avatar_filename', 100);
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('avatar_filename');
        });
    }
}
``` 

マイグレーションファイルを記述したら、実行します。

```bash
$ php artisan migrate
```

## storageを公開するためのシンボリックリンク作成

```bash
$ php artisan storage:link
```

このコマンドで public/storage から storage/public にシンボリックリンクが作成されます。

## 動作確認

さて、ここまで実装すればブラウザからファイルアップロードができるようになっているはずです。

<img width="754" alt="スクリーンショット 2017-01-06 13.29.15.png" src="https://qiita-image-store.s3.amazonaws.com/0/18306/9744932c-00b0-5fd9-3473-055ada904c69.png">