[Laravel]画像アップロード機能作ってみた

簡単なフォトアルバムアプリを作成した際の画像アップロード機能に関する部分だけをまとめました。


##フォームの作成
```php:create.blade.php

@extends("layouts.app")

@section("content")
<h3>Create Album</h3>
{!!Form::open(["action" => "AlbumsController@store" , "method" => "POST", "enctype" => "multipart/form-data"])!!}
    {{ csrf_field() }}
    {{Form::text("name","",["placeholder" => "Album Name"])}}
    {{Form::textarea("description","",["placeholder" => "Album Description"])}}
    {{Form::file("cover_image")}}
    {{Form::submit("submit")}}
{!!Form::close()!!}
@endsection
```
Formファサードで作成。
"enctype" => "multipart/form-data"は二つ以上のファイルを同時にリクエストに格納するための方式。画像アップロードのフォームには必須。
cover_imageが画像の名前を保存するカラム。


##画像の保存
画像データはデータベースにそのまま保存するのではなく、基本的に画像本体は静的ファイル、画像の名前はデータベースに保存するようだ。

```php:AlbumsController.php
    public function store(Request $request)
    {
        //拡張子付きでファイル名を取得
        $filenameWithExt = $request->file("cover_image")->getClientOriginalName();

        //ファイル名のみを取得
        $filename = pathinfo($filenameWithExt, PATHINFO_FILENAME);

        //拡張子を取得
        $extension = $request->file("cover_image")->getClientOriginalExtension();
        
        //保存のファイル名を構築
        $filenameToStore = $filename."_".time().".".$extension;

        $path = $request->file("cover_image")->storeAs("public/album_covers", $filenameToStore);

        $album = new Album;
        $album->name = $request->input("name");
        $album->description = $request->input("description");
        $album->cover_image = $filenameToStore;
        
        $album->save();

        return redirect("/albums")->with("success", "Album Created");
    }
```
###名前の保存
そのままのファイル名で保存してもよいが、時間を入れることで同じ名前の画像を区別できる。
アップロードされたものからファイル名と拡張子を取得する。間にアップロードされた時間を挟み込み保存する新たに名前を構築。


###画像本体の保存

```php
$path = $request->file("cover_image")->storeAs("public/album_covers", $filenameToStore);
```
ブラウザからサーバ上のファイルにアクセスするためには、Laravelインストールディレクトリの下にあるpublicディレクトリの下に保存する必要がある。なのでpublicディレクトリに保存するが、実際に保存されるのは/storage/appという場所である。
<img width="222" alt="スクリーンショット 2019-12-04 23.53.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502145/1d9851d2-699e-4012-5e54-da1c7af2aafb.png">
publicの下ではなく、album_coversというフォルダが/storage/appの下に作られ、保存した画像はその中にある。

そこでpublicディレクトリと/storage/appの間にリンクを持たせる必要がある。



```
>php artisan storage:link
The [public/storage] directory has been linked.
```
php artisan storage:linkを実行することによってシンボリックリンクを張り、その間にリンクを持たせることができる。


<img width="181" alt="スクリーンショット 2019-12-05 00.05.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502145/f11d5ec9-f9c5-8cc2-6327-c8c0faa7c66e.png">

publicの下にstorageというフォルダが作られ画像も保存されている。
これでブラウザ上から画像を見ることができる。

##保存した画像を表示する

```php:create.blade.php
@extends("layouts.app")

@section("content")
@if(count($albums) > 0)
<?php
   $colcount = count($albums);
   $i = 1;
 ?>
<div id="albums">
    <div class="row text-center">
        @foreach($albums as $album)
        @if($i == $colcount)
        <div class="medium-4 columns end">
            <a href="/albums/{{$album->id}}">
                <img class="thumbnail" src="storage/album_covers/{{$album->cover_image}}" alt="{{$album->name}}">
            </a>
            <br>
            <h4>{{$album->name}}</h4>
            @else
            <div class="medium-4 columns">
                <a href="/albums/{{$album->id}}">
                    <img class="thumbnail" src="storage/album_covers/{{$album->cover_image}}" alt="{{$album->name}}">
                </a>
                <br>
                <h4>{{$album->name}}</h4>
                @endif
                @if($i % 3 == 0)
            </div>
        </div>
        <div class="row text-center">
            @else
        </div>
        @endif
        <?php $i++;?>
        @endforeach
    </div>
</div>
@else
<p>No Albums To Display</p>
@endif
@endsection
```
保存の時は画像をpublic/album_coversの下に保存したが、読み込む場合のパスはstorage/album_coversとなることに気を付けたい。

##画像を削除する

```php:PhotosController
    public function destroy($id)
    {
        $photo = Photo::find($id);

        if(Storage::delete("public/photos/".$photo->album_id."/".$photo->photo))
        {
            $photo->delete();

            return redirect("/")->with("success","Photo Deleted");
        }
    }
```
画像の削除はDBテーブル上のデータだけでなく、アプリ内のストレージからも削除する。