Laravelで画像投稿機能の作成

## はじめに
今回はとても時間かかったLaravelでの画像投稿機能を作ったのでそちらを記事に書いていこうと思います。
調べても思ったより上手くいかなくて、３日ほど使ってしまったのでこれから導入する人の少しでも参考になれば幸いです。



※１ヶ月前くらいから少しづつLaravelの勉強を始めた初心者ですので間違ってる箇所、至らない点などあると思いますが、指摘して頂けると助かります
（こちらはメモとして書いているのでフロント部分は簡易的なものになります）
ただ、コントローラーの部分はしっかり書いているので、画像を投稿して表示する機能を作成するのに少しでもお役に立てたら嬉しいです

### 使用する環境
- MacOS(M1)
- PHP　8.0.19 
- Laravel 9.19.0
- MySQL 8.0.29

## 前提条件
laravelのプロジェクトを作成しており、認証機能（ログインや会員登録）まで作成済
リレーションは後日記事を出します

### ルーティングの作成

```web.php
Route::get('/input', [ContentsController::class, 'input'])->name('input');
Route::post('/save', [ContentsController::class, 'save'])->name('save');
Route::get('/output', [ContentsController::class, 'output'])->name('output');
```

### モデルを作成
まず投稿用のモデルを作成
```
モデル作成
例：$php artisan make:model モデル名
$ php artisan make:model Content
マイグレーションファイル作成
$ php artisan make:migration create_テーブル名 --create='テーブル名'
$ php artisan make:migration create_contents --create=contents
```

```.migrationfile
$table->string('body')->nullable();
$table->integer('user_id')->nullable();
```

```
$ php artisan migrate
```

次に画像を保存用のContentImageモデルとDBを作成

```
$ php artisan make:model ContentImage
$ php artisan make:migration create_content_images --create=contents_images
```

```.migrationfile
$table->integer('content_id')->nullable();
$table->string('file_name');
```
```
$ php artisan migrate
```
### シンボリックリンクの作成
```
$ php artisan storage:link
storageの配下のapp配下のpublicの配下にimagesディレクトリがない場合は作成してください
```

### コントローラーの作成

```
$ php artisan make:controller コントローラー名
例：　$ php artisan make:controller contents
```

```ContentsController.php
    use Illuminate\Support\Facades\Auth;//Auth::id()でログイン中のユーザIDを取得するのに必要
    public function input()
    {
        return view('contents.input');
    }

    public function save(Request $request)
    {
        $input_content = new Content();//インスタンスの生成
        $input_content->user_id = Auth::id();//ログインしてるユーザーのIDを取得してcontentのidに代入
        $input_content->body = $request['body'];//viewから取得したbodyの値をcontentのbodyに代入
        $input_content->save();
        if ($request->file('file')) {//もし画像が投稿されていたら
            $this->validate($request, [
                'file' => [
                    // 空でないこと
                    'required',
                    // アップロードされたファイルであること
                    'file',
                    // 画像ファイルであること
                    'image',
                    // MIMEタイプを指定
                    'mimes:jpeg,png',
                ]
            ]);
            //viewから送られてきたファイルが存在しており、問題なくアップロードできたのかを確認
            if ($request->file('file')->isValid([])) {
                $old_file_path = Storage::putFile('public/images', $request->file('file'));//'public/images'にviewから渡された画像を配下に保存
                $file_name = str_replace('public/images/', '', $old_file_path);
                //str_replaceで文字列を置換できる$old_file_pathの変数の中身をファイル名だけに置換している。
                //要は$old_file_pathの中身は'public/images/ファイル名.pdf'になっているので,
                //ファイル名だけ保存するために'public/images/'を''に置き換える事でファイル名だけになる
                $image_info = new ContentImage();
                $image_info->content_id = $input_content->id;//contentのidに上で作ったインスタンスのcontent_idに代入している
                $image_info->file_name = $file_name;
                $image_info->save();
            }
        }
        return redirect(route('output'));//outputにリダイレクト
    }

    public function output()
    {
        $contents_get_query = Content::select('*');
        $contents = $contents_get_query->get();
        //テーブルを取得して変数に代入
        $content_images = DB::table('content_images')->get();
        $contents = DB::table('contents')->get();
        // issetは何か文字が入ってたらtrueを返す
        return view('contents.output', [
            'content_images' => $content_images,
            'contents' => $contents,
            //ここでviewで使えるように変数渡している
        ]);
    }

```

### viewの作成

```
$ touch resources/views/contents/input
$ touch resources/views/contents/output
```

```.input.blade.php
@section('content')
    <h1>input</h1>
    //enctype='multipart/form-data'これがないとコントローラーでファイルを受け取れない
    <form action="{{ route('save') }}" method="post" enctype='multipart/form-data'>
        @csrf
        <textarea name="body" cols="30" rows="10"></textarea>
        <br>
        @error('file')
            {{ $message }}
            <br>
        @enderror
        <input type="file" name="file">

        <input type="submit" value="送信">
    </form>
@endsection
```

```.output.blade.php
@section('content')
    <h1>output</h1>
    @foreach ($content_images as $content_image)
        //　画像ファイルがあるなら下記の処理をする
        @if (isset($content_image->file_name))
            //　.で文字列と変数をつなげているつまり画像までのパスになる
            <img src="{{ asset('storage/images/' . $content_image->file_name) }}"
                alt="{{ asset('storage/images/' . $content_image->file_name) }}">
        @endif
    @endforeach
    @foreach ($contents as $content)
　　　　　　　　　　 <p>{{$content->body}}</p>
 　　　　　　@endforeach
@endsection
```

## 最後に
またリレーションなど出来次第どんどん更新していこうと思っています
また今回の記事は投稿機能の概要だけ知って頂きたく、また自分用のメモとして残しているのでフロントはとても簡素なものになっている点申し訳ありません
間違ってる事などありましたら教えて頂けると助かります