Laravelで画像投稿機能を実装する

Laravelで画像投稿機能を実装します。具体的には以下のような機能を実装します。

![552fd9bc96fec88f5411232502dec300.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477146/e9048ab5-a89c-81de-119d-bbd71330decf.png)
上記の写真で画像ファイルを選択し、フォームにコメントを入力後、投稿ボタンを押すと、
![a063c3be9f1d7d7c178c88822d409816.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477146/c2676739-57fb-44da-b362-71c5e60b1b0d.png)

上記の写真のように、画像とコメントを表示する機能を実装します。

#開発環境
Laravel:5.5.46
MYSQL:5.5.62
PHP:7.2.17
画像アップロードサーバ:AmazonS3

#マイグレーションファイル作成
画像とコメントを保存するためのテーブルpostsを作成します。ターミナルで以下のコマンドを実行しマイグレーションファイルを作成します。

`$ php artisan make:migration _create_posts_table --create=posts`

作成したマイグレーションファイルを編集します。

```php:database/migrations/2019_10_22_220848_create_posts_table.php
<?php

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

class CreatePostsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->increments('id');
            $table->string('image_file_name',100);//追加
            $table->string('image_title',100);//追加
            $table->timestamps();
            
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('posts');
    }
}
```

ターミナルで以下のコマンドを入力しマイグレーションを実行します。

`$ php artisan migrate`



#モデル

Postモデルを作成します。ターミナルで以下のコマンドを実行しモデルを作成します。

`$ php artisan make:model Post`


作成したモデルを編集します。


```php:app/Post.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
//後にcreate()メソッドで保存するカラムを指定
    protected $fillable = [
        'image_file_name', 'image_title',
    ];
}
```

#ルータ
ルータを記述します。ページを表示したときと、投稿ボタンを押したときのルーティングを記述します。

```php:routes/web.php
Route::post('upload', 'PostsController@upload')->name('upload');
Route::get('/', 'PostsController@index');
```

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

`$ php artisan make:controller PostsController`

web.phpに記述した各ルーティングに対応したアクションを記述します。

```php:/app/Http/Controllers/PostsController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Foundation\Auth\User;
use App\Post;
use Storage;
use Illuminate\Support\Facades\Validator;

class PostsController extends Controller
{
//画像およびコメントアップロード
public function upload(Request $request){
        
//Validatorファサードのmakeメソッドの第１引数は、バリデーションを行うデータ。
//第２引数はそのデータに適用するバリデーションルール
        $validator = Validator::make($request->all(), [
            'file' => 'required|max:10240|mimes:jpeg,gif,png',
            'comment' => 'required|max:191'
        ]);
        
//上記のバリデーションがエラーの場合、ビューにバリデーション情報を渡す
        if ($validator->fails()){
            return back()->withInput()->withErrors($validator);
        }
//s3に画像を保存。第一引数はs3のディレクトリ。第二引数は保存するファイル。
//第三引数はファイルの公開設定。
        $file = $request->file('file');
        $path = Storage::disk('s3')->putFile('/', $file, 'public');
//カラムに画像のパスとタイトルを保存
        Post::create([
            'image_file_name' => $path,
            'image_title' => $request->comment
        ]);
   
        return redirect('/');
    }
//ページ表示
    public function index(){
        $posts = \App\Post::all();
    
        $data = [
            'posts' => $posts,
        ];
     
        return view('welcome',$data);
    }
}
```

ターミナルで以下のコマンドを入力し、ビューでFormファサードを使用できるようにします。

`$ composer require "laravelcollective/html":"5.5.*"`


AWSの S3にアクセスするライブラリを導入します。
`$ composer require league/flysystem-aws-s3-v3`

.envで環境変数を設定します。

```php:.env

AWS_S3_KEY=[your Access Key ID]
AWS_S3_SECRET=[your Secret Key]
AWS_S3_REGION=[your region]
AWS_S3_BUCKET=[your backet]
```

#ビュー
resources/viewsに最初からあるwelcome.blade.phpを編集します。

```php:resources/views/welcome.blade.php
//投稿フォーム
{!! Form::open(['route' => 'upload', 'method' => 'post','files' => true]) !!}
    <div class="form-group">
        {!! Form::label('file', '画像投稿', ['class' => 'control-label']) !!}
        {!! Form::file('file') !!}
    </div>
    <div class="form-group m-0">
        {!! Form::label('textarea', '投稿コメント', ['class' => 'control-label']) !!}
        {!! Form::textarea('comment',null,['class' => 'form-control']) !!}
    </div>   
    <div class="form-group text-center">
        {!! Form::submit('投稿', ['class' => 'btn btn-primary my-2']) !!}
    </div>
{!! Form::close() !!}
//画像とコメントをすべて表示
@foreach($posts as $post)
    <div class="card-header text-center">
        <img src= {{ Storage::disk('s3')->url($post->image_file_name) }} alt="" width=250px height=250px></a>
    </div>
    <div class="card-body p-1">
        <span class="card-title">{{ $post->image_title }}</span>
    </div>
@endforeach
```