Laravel 6.x 【画像アップロード】 ディレクトリの作成、ファイルの保存、データベースへの登録、表示

#制作環境

Windows 10
Laravel : 6.18.35
Laravel/ui : 1.0
Laravel-mix : 5.0.1
Bootstrap : 4.0.0
MDBootstrap : 4.19.1
chart.js : 2.9.3
XAMPP
PHP : 7.4.3
Visual Studio Code

#はじめに
この記事はプログラミングをはじめたばかりの素人が、できたことをメモするのに利用しています。
内容には誤りがあるかもしれません。

#参考にしたサイト
https://qiita.com/10mi8o/items/ce9c875e736c7b1d2498
https://qiita.com/koru1893/items/1d2f522e20744b03e3ad
https://laraweb.net/tutorial/2707/
https://php-junkie.net/framework/laravel/upload_image/

リファレンス
https://readouble.com/laravel/6.x/ja/helpers.html?header=redirect()

#関連記事
続編です。
[Laravel 6.x 【画像アップロード】 ファイルの削除、データベースへのレコード削除](https://qiita.com/Charry/items/423cd11c04ea38dad17f)
[Laravel 6.x 【画像アップロード】 トランザクションを使用する](https://qiita.com/Charry/items/783662cbdf5da2fba55b)

#この記事の目的
Laravelでの画像アップロードを、シンプルに理解できるよう画像アップロードに特化して掲載するのが目的です。
その為、バリデーション等は(素人の自分的に)わかりづらくなるのであえて割愛してます。
またデザインなども一切気にしてません。
あくまで、画像アップロードのやり方だけを見るのに特化させてます。

事前にLaravelをインストールしてからはじめてください。

#完成イメージ
###アップロードフォーム
シンプルにファイル選択画面のみ。

![up.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/760252/3b805566-88cd-d497-d27f-7f8ab4b67f61.jpeg)


###登録完了後
完了メッセージと、登録した画像を表示。

![comp.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/760252/9cb52186-6717-0009-a59c-f06729891a75.jpeg)

#要件
・画像を選択し、アップロードできる。 
・プロジェクトのstorage>app>public>imagesにファイルを保存する。
・保存する際に、ファイル名は元のファイル名と別の名称にする。
・データベースにファイルの名前を登録する。

それでは、はじめていきます。

#モデルとマイグレーションファイルの作成
プロジェクトのディレクトリでターミナルを起動し、以下を実行してください。

```PHP
php artisan make:model Models/FileImage -m
```

これで、モデルとマイグレーションファイルが作成されます。
※モデルはModelsの中に作成するようにしています。

#マイグレーションファイルの編集
database>migrations 内のxxxx_xx_xx_xxxxx_create_file_images_table.phpを開き、以下のように編集します。

```PHP:create_file_images_table.php
    public function up()
    {
        // テーブルがなければ新規作成する
        if (!Schema::hasTable('file_images')) {
            Schema::create('file_images', function (Blueprint $table) {
                $table->bigIncrements('id');
                $table->string('file_name')->comment('ファイル名');
                $table->timestamps();
            });
        }
    }
```

->comment('ファイル名')は、なくてもかまいません。

#モデルの編集
app>Models 内のFileImage.phpを開き、以下を記述します。

```PHP:FileImage.php
class FileImage extends Model
{
    protected $fillable = ['file_name'];
}
```

#コントローラの作成
ターミナルで以下を実行。

```PHP
php artisan make:controller FileUpController
```

#コントローラの編集
app>Http>Controllers 内のFileUpController.phpを開き、以下のように記述します。

```PHP:FileUpController.php
<?php
// モデル使用の為に追記
use App\Models\FileImage;

class FileUpController extends Controller
{
    public function index()
    {
        // データベースからfile_imagesテーブルにある全データを抽出
        $data = FileImage::all();
        // ビューfile.blade.phpに$dataを渡して表示させる
        return view('file', compact('data'));
    }

    // 登録処理
    public function store(Request $request)
    {
        // $request->imgはformのinputのname='img'の値です
        // ->storeメソッドは別途説明記載します
        $path = $request->img->store('public/images');
        // パスから、最後の「ファイル名.拡張子」の部分だけ取得します 例)sample.jpg
        $filename = basename($path);
        // FileImageをインスタンス化(実体化)します
        $data = new FileImage;
        // 登録する項目に必要な値を代入します
        $data->file_name = $filename;
        // データベースに保存します
        $data->save();

        // 登録後/fileにリダイレクトします その際にフラッシュメッセージを渡します
        return redirect('/file')->with('success', '登録完了しました。');
    }
}
```

####storeメソッドについて
下記storeメソッドだけで、

```PHP
$path = $request->img->store('public/images');
```

以下の内容を実行してくれます。

・保存ディレクトリの作成 (今回はstorage>app>public 内にimagesディレクトリを作成してくれます)
・ファイルの保存
・保存の際に自動的に元のファイル名を変更します 例:mMwTQg1AGSNdhPaL5GofWxGF3DYkvgphEnzGEIxF.png
・ルートディレクトリからの相対位置を返します


リファレンス
https://readouble.com/laravel/6.x/ja/requests.html?header=%25E3%2582%25A2%25E3%2583%2583%25E3%2583%2597%25E3%2583%25AD%25E3%2583%25BC%25E3%2583%2589%25E3%2583%2595%25E3%2582%25A1%25E3%2582%25A4%25E3%2583%25AB%25E3%2581%25AE%25E4%25BF%259D%25E5%25AD%2598

#ビューの作成
resources>views 内にfile.blade.phpを作成し、以下のように記述します。

```PHP:file.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>ファイルアップロード</title>
</head>
<body>
  <h1>ファイルアップロード</h1>
  @if (session('success'))
      <p>{{ session('success') }}</p>
  @endif

  <form action="/file" method="post" enctype="multipart/form-data">
  @csrf
  <label>画像選択<input type="file" name="img" accept=".png,.jpg,.jpeg,image/png,image/jpg"></label>
  <br>
  <input type="submit" value="送信">
  </form>

@foreach ($data as $item)
  <img src="{{ asset('storage/images/' . $item->file_name) }}">
@endforeach

</body>
</html>
```

#ルーティングの作成
routes 内のweb.phpを開き、以下のように記述します。

```PHP:web.php
Route::get('/file', 'FileUpController@index');
Route::post('/file', 'FileUpController@store');
```

#シンボリックリンクの作成
ターミナルで以下を実行。
これをやらないと画像を表示できません。

```PHP
php artisan storage:link
```

Laravelの仕様で、ファイルはstrage>app>public>imagesに保存されますが、読み込み時はpublic>storage>imagesからファイルが読み込まれます。
その為、リンクを貼って保存先にアクセスできるようにしてます。

#動作確認
/fileにアクセスして下さい。
以下が表示されればOKです。

![up.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/760252/4a168474-a601-ddd8-41d8-5b4bc1620e5a.jpeg)


次に適当な画像ファイルを選択し、送信をクリックしてください。
送信後、以下のように選択した画像が表示されればOKです。

![peach.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/760252/b150580b-2503-e327-fddc-332826bf295f.jpeg)

#####※ちなみにバリデーションも何もやってないので、ファイルを選択しない状態で送信するとエラーが発生します。

#ファイルの確認
以下の場所にimagesディレクトリと、画像ファイルが保存されていることを確認してください。
ファイル名は自動的に付くので、変な名前になってます。セキュリティー対策ですね。

![storage.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/760252/316fd6b9-4ae3-87d4-64fa-5ee374832423.jpeg)

同じく、以下の場所にimagesディレクトリと、画像ファイルがリンクされていることを確認してください。

![symbol.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/760252/c2a15dee-6dec-4108-2bec-40e13b9146b1.jpeg)

確認ができればOKです。

#データベースの確認
tinkerを使い、データベースにファイル名が保存されているか確認します。

ターミナルで以下を実行します。

```PHP
php artisan tinker
```

次に以下を実行。

```PHP
use App\Models\FileImage;
```

次に以下を実行。

```PHP
FileImage::all();
```

以下のように表示され、storageに保存されているファイル名と、登録されたfile_nameが合っていたらOKです。

![tinker.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/760252/66c724cf-a826-6bef-59ea-4169b1f81ce2.jpeg)

最後にexitかCtrl+cでtinkerを終了します。

```PHP
exit or Ctrl+c
```

以上で終了です。