Laravelでユーザーのアバター画像をアップロードする想定で実装してみました。
#テーブルにカラムを追加する
usersテーブルに画像のパスを保存するカラムを追加し、migrateを実行します。
class AddImagePathToUsersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('image_path')->after('email_verified_at');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('image_path');
});
}
}
#画像をアップロードするフォームを作成する。
ここで注意点があります。
① Laravelでフォームを作成する際、CSRF対策がなされていないと、エラーになってしまうので、素のHTMLタグでフォームを作成する際は{{ csrf_field }}
を追記しましょう。
{{ csrf_field }}
で出力される内容は以下のようになります。type属性はhidden。フォームデータを送信する時に、ハッシュ値がリクエストされます。
<input type="hidden" name="_token" value="tyKtcrH2KscjxVAsxb3KHXG5ppZ8te6PA6SJY8df">
※CSRFとはクロスサイトリクエストフォージェリの略で、Webアプリケーションの脆弱性をついた攻撃手法です。信頼できるユーザーになりすまし、ユーザーの不利益になるような処理を行うことができます。Laravelではこの対策として、各セッションごとにトークンを自動的に生成しています。
webミドルウェアグループに含まれている、VerifyCsrfToken ミドルウェアが、リクエスト中のトークンとセッションに保存されているトークンが一致するか、確認しています。
https://readouble.com/laravel/5.7/ja/csrf.html
②formタグに enctype='multipart/form-data'
を忘れず、記述しましょう。これは、複合データ型であることを示し、1回のHTTP通信で、複数の種類のデータ形式を扱う事でできるようになります。(今回の場合は、jpeg、png等)
@section('content')
<form route="/users/{$user->id}/edit" method="post" enctype='multipart/form-data'>
{{ csrf_field() }}
@isset ($filename)
<div>
<img src="{{ asset('storage/avatar/' . $user->image_path) }}">
</div>
@endisset
<div>
<input type="hidden" name="id" value="{{$user->id}}">
</div>
<div>
<input type="text" name="name" value="{{$user->name}}">
</div>
<div>
<input type="text" name="email" value="{{$user->email}}">
</div>
<div>
<input type="file" name="image">
</div>
<input type="submit" value="更新する">
</form>
@endsection
#Routeを追加する
Route::get('/users/{user}/edit', 'UserController@edit')->name('users.edit');
Route::post('/users/{user}/edit', 'UserController@update');
#Controllerを定義
fileメソッドにはinputタグのname属性を、storeメソッドには、保存したいパス(ディレクトリ)を指定します。
(任意のファイル名を指定したい場合はstoreAsメソッドを使用してく
ここではユーザーのavatarをアップロードする事を想定して、public/avatar
を指定します。ファイルが保存される場所は、storage/app
ディレクトリになるので、この場合、storage/app/public/avatar
ディレクトリに画像が保存されます。
次に、このままでは、カラムにパスの情報ごと保存されてしまうので、basename()
を使用して、画像名のみ保存するようにしています。
viewで表示する際にはasset()
を使用して <img src="{{ asset('storage/avatar/' . $user->image_path) }}">
と指定します。
public function edit(Request $request, User $user)
{
return view('users.edit', compact('user'));
}
public function update(Request $request, User $user)
{
$user->name = $request->name;
$user->email = $request->email;
$path = $request->file('image')->store('public/avatar');
$user->image_path = basename($path);
$user->save();
return view('users.edit', compact('user'))->with('filename', basename($path));
}
#シンボリックリンクを張る
最後にシンボリックリンクを張りましょう。
アップロードしただけでは、ブラウザからアップロードした画像を表示する事は出来ません。
ブラウザからサーバー上のファイルにアクセスするには、publicディレクトリの下に保存する必要があります。しかし、アップロードしたファイルが保存される場所は、storage/app
ディレクトリになるので、public/storage
からstorage/app/public
へリンクを持たせる必要があります。Laravelではそのコマンドが用意されています。以下、コマンドを実行しましょう。
$ php artisan storage:link
リンクが貼られていれば、public/storage
にリンクされている事を示すiconが表示されます。
以上、自分用のメモの意味合いが強いですが、これで画像のアップロード、表示が実現できます。
もっと良い方法があれば、コメント頂けると嬉しいです。