はじめに
画像の保存の処理で苦戦したため、記録として残します。
少しでもつまづいている方の参考になれば嬉しいです。
条件
mac OS "11.2.3 Big Sur"
Laravel Framework "6.20.27"
PHP "8.0.7"
nginx "1.18"
mysql "8.0"
画像の保存方法
・DBには画像のパス(画像がどこに保存されているのか)のみを保存
$table->string('cooking_img_file')
// パス名を保存するので、String型
・画像本体はStorageに保存
保存の流れ
大まかな保存の処理の流れは以下のような感じです。
-
フォームに画像を送付して送信
-
画像本体はStorageに保存
3. DBには画像のパス(画像がどこに保存されているのか)のみを保存
実装の流れ
実装
1. 画像データの保存先を作成
画像本体は、Storage/app/public
に保存されるようにします。
今回は、public
ディレクトリの中にさらに料理写真を保存するrecipe
ディレクトリを作成しています。
上記のように、画像データがどんどん保存されていきます。
そして、DBにはここまでの道のり、つまりstorage/app/public/recipes/....
の部分を保存するようにします。
2. シンボリックリンク
画像を保存するディレクトリは作成しましたが、先ほど説明したstorage/app/public
は、ウェブ上に公開されない非公開ディレクトリであるため、このままでは保存した画像を画面上で見ることはできません。
そのため、公開ディレクトリであるpublic
ディレクトリから非公開ディレクトリにアクセスして画面上に画像を表示させるようにします。
赤枠が公開ディレクトリ、青枠が非公開ディレクトリとなっています。
この青枠の中に保存されている画像を赤枠からアクセスできるようにして、保存されている画像をウェブ上で見れるようにします。
それを行うには、シンボリックリンクというものでこの赤枠と青枠を繋げないといけません。
そのリンクを貼る方法は簡単で、以下のコマンドをターミナルでうつだけです。
# php artisan storage:link
すると、赤枠の中にある、storage
ファイルに矢印
がつきます。
これでリンクが貼られたことになります。
3. テーブルにカラムの追加
1. 画像を保存するためテーブルに画像のファイルパスを保存するカラムを追加する
$table->string('cooking_img_file')->nullable();
今回、画像データは空でもOKという条件で実装しているためnullable()
を入れています。
3. Blade
<form method="POST" action="{{ route('recipes.store') }}" enctype="multipart/form-data">
<input type="file" name="cooking_img_file" class="d-none" accept="image/png,image/jpeg,image/gif" />
① form に enctype = "multipart/form-data"
を記述する。
これを記述しないと画像がアップロードされない。
② input: type = "file"
送られてくるデータはfileとなるため.
4. 画像の保存処理
アップロードされた画像の保存処理の全体像は以下のようになります。
public function store(RecipeRequest $request, Recipe $recipe)
{
$recipe->fill($request->all());
// ❶ アップロードされたファイル情報を $recipe_image に格納
$recipe_image = $request->file('cooking_img_file');
// 画像保存処理
if($recipe_image) {
// ❷ `recipe`というディレクトリに $recipe_image のデータを保存する
$path = Storage::disk('public')->putFile('recipes', $recipe_image);
// ❸ basenameで $path の最後の部分(=ファイルの名前のみ)を取得
$recipeFileName = basename($path);
// ❹ 取得したファイルの名前のみをDBに保存する
$recipe->cooking_img_file = $recipeFileName;
} else {
$path = null;
}
$recipe->save();
// 以下省略
}
❶ アップロードされた画像の取得
以下のコードで、アップロードされた画像の情報を取得でき、それを$recipe_image
に格納しています。
$recipe_image = $request->file('cooking_img_file');
file()
には、第一引数にinputタグのname属性の値を指定します。
(今回はinputタグに、name="cooking_img_file"
と記述しています。)
画像保存処理
「画像の添付は必須ではない」という設定をしているため、if文で画像が添付されている時とされていない時で条件分岐しています。
// 条件分岐
// 画像がアップロードされている時
if($recipe_image) {
$path = Storage::disk('public')->putFile('recipes', $recipe_image);
$recipeFileName = basename($path);
$recipe->cooking_img_file = $recipeFileName;
// 画像がアップロードされていない時
} else {
$path = null;
}
❷ 画像を保存する場所の指定
$path = Storage::disk('public')->putFile('recipes', $recipe_image);
1つずつ見ていきます。
Storage::disk('public')
Storage::disk('○')
→ ○にはディスク名を指定。
ディスク名は、デフォルトで以下の3つがconfig/filesystems.php
に用意されています。
今回はユーザーも画像を閲覧できるようにしたいので、diskにはpublic
を指定しています。
- local:ローカルディスク(非公開)
- public:ローカルディスク(公開)
- s3:AWS S3
'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
],
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL').'/storage',
'visibility' => 'public',
],
's3' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET'),
'url' => env('AWS_URL'),
'endpoint' => env('AWS_ENDPOINT'),
],
->putFile('recipes', $recipe_image);
// 第一引数 : 保存先のフォルダ名. 上記では、`Storage/app/public/recipes` になる
// 第二引数 : 保存したいファイル
❸ 画像のパス名
$recipeFileName = basename($path);
// basename : パスの最後にある名前の部分を返す
現時点で$path
には、
というように、public/recipes/
が含まれているため、basename
で画像のファイル名のみを取り出しDBに保存できるようにします。
この時点で$recipeFileName
には、以下のようにファイル名のみを保存されるようにします。
❹ 取得したファイルの名前のみをDBに保存する
❸の処理で取り出した画像のファイル名を$recipeFileName
に格納します。
$recipe->cooking_img_file = $recipeFileName;
そして、画像のファイル名をDBのcooking_img_fileに保存しています。
画像を添付していない場合は、$path
の値にnull
が入るようにしています。
} else {
$path = null;
}
これでコントローラー内の画像保存処理は終了です。
おわりに
画像の保存処理は、初めは何をしているのか少しややこしかったですが一つ一つみていくことで少しずつ理解ができてきました。
この調子で頑張っていくぞ!
参考