投稿機能に画像投稿仕様追加　(実務未経験状態でLaravelを使用した共同開発　タスク7)

## 0.背景
本記事は、記事「実務未経験状態でLaravelを使用した共同開発」で取り組んだタスクのうち、投稿機能に画像投稿仕様追加についてまとめたもの

https://qiita.com/ashi__mo/items/9abff3c5227660dcddba

## 1.仕様内容
・投稿フォームに画像追加機能
・画像の投稿を必須とする（バリデーション）
・画像の投稿は１枚まで
※この仕様は、当時のメンバーMTGにて決定。トップページには画像付きの投稿を表示し、それに対してリプライで語る仕様を構築。

##### UI(デプロイしたもの)
![top1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3842139/6ec40bb2-e6ca-4337-9559-e999e49ac83f.png)

## 2.タスクの見積もり
##### 期間
・１０日
##### 理由
・メンバーが実装した投稿表示機能へのデグレ等の影響が出た際の修正を想定
・その他実装中エラー、イレギュラーを想定
・PRレビュー、修正期間を想定
##### 実績
・仕様作成完了　２日
・PRまで　２日
・マージ完了まで　３日

## 3.実装
⓪シンボリックリンク作成
・php artisan storage:link
　→public内にstrageディレクトリ作成
　→投稿画像のデータを格納するフォルダが生成される
①Routing
・新規記載、変更なし
②Migration
・create_posts_table.php　に追加
・nullableはテストデータ登録時のため
```
$table->string('image_path')->nullable(); // 画像投稿
```
③Model
・Post.php
・保存対象カラムの追加（'image_path'を追加）
```
protected $fillable = ['user_id', 'content', 'image_path'];
```
③Controller
・PostController.php storeメソッド編集
・画像のデータをpublic/images　に格納
・画像データ名をimage_pathカラムへcreate
※旧メソッド
```
    public function store(PostRequest $request)
    {
        Post::create([
            'user_id' => auth()->id(),
            'content' => $request->validated()['content'],
        ]);

        return redirect('/');
    }
```
※新メソッド
```
    public function store(PostRequest $request)
    {
        $imagePath = null;
        if($request->hasFile('image')){
            $imagePath = $request->file('image')->store('images', 'public');
        }

        Post::create([
            'user_id' => auth()->id(),
            'content' => $request->validated()['content'],
            'image_path' => $imagePath,
        ]);

        return redirect('/');
    }
```
④Request
・PostRequest を編集
・画像投稿に関するバリデーションルールを追加
※旧バリデーション
```
    public function rules()
    {
        $isUpdate = $this->routeIs('posts.update');

        return [
            'content' => ['required', 'max:140']
        ];
    }

        public function messages()
        return [
            'content.required' => '内容は必須です。',
            'content.max' => '投稿は140文字以内で入力してください。'
    }
```
※新バリデーション
```
    public function rules()
    {
        $isUpdate = $this->routeIs('posts.update');

        return [
            'content' => ['required', 'max:140'],
            'image' => ['required', 'image', 'mimes:jpeg,png,jpg,gif', 'max:2048'],
        ];
    }

        public function messages()
        return [
            'content.required' => '内容は必須です。',
            'content.max' => '投稿は140文字以内で入力してください。',
            'image.required' => '画像投稿は必須です。'
            'image.image' => 'アップロードは画像ファイルのみです。',
            'image.mimes' => '画像形式はjpeg、png、jpg、gifのみです。',
            'image.max' => '画像サイズは2MB以下にしてください。'
        ];
    }
```
⑤view
・add.post.blade.php を編集
・formタブに【enctype="multipart/form-data】を追加
```
<form method="POST" action="{{ route('post.store') }}" class="d-inline-block w-75" enctype="multipart/form-data">
```
・画像投稿フォームを追加
```
<div class="form-group">
    <label for="image">画像</label>
    <input type="file" name="image" class="form-control-file">
</div>
```
・posts/posts.blade.php を編集
・投稿内容に画像を追加
・image_pathカラムにデータがあるのであれば画像を表示、nullであればトップページの画像を表示する仕様（nullはテストデータ用）
※クリックで画像をモーダル表示する仕様のため、data-image属性を付与しています。（モーダル表示部分の実装解説は本記事では割愛）
```
@if ($post->image_path)
    <img src="{{ asset('storage/' . $post->image_path) }}" class="img-thumbnail clickable-image" style="cursor: pointer;" alt="投稿画像" data-image="{{ asset('storage/' . $post->image_path) }}">
@else
    <img class="img-thumbnail clickable-image my-1" src="{{ asset('images/top.png') }}" style="cursor: pointer;" data-image="{{ asset('images/top.png') }}">
@endif
```

## 4.テスト実施
・画像を選択できる（１枚のみ）ことを確認
・画像の投稿を確認
・画像を選択せず投稿を確認（バリデーション）
・テキストエリアが空欄で投稿を実施（バリデーション）
・画像、テキストエリア共に空欄で投稿を実施（バリデーション）
・投稿削除時に画像表示の有無やエラーの発生を確認 

## 5.本実装に関して
・他メンバーが記述したコードを読み取り、編集する中でコードの可読性を強く意識するようになった
・要所にコメントを入れることや、記述の順序、複雑にならないようシンプルに構成する工夫を意識
・結果として保守性の高いコードを書く意識が身についた