【Laravel初心者】投稿保存の実装

Laravel初学者です。
オリジナルアプリを作成しているのでその過程を記事にしています。

理解が曖昧なところも多いため、ご指摘等ありましたらご連絡いただければ幸いです。

今回は投稿保存の実装について備忘録のため記録として残します。
前回の記事：[【Laravel初心者】一覧表示の実装](https://qiita.com/mumucochimu/items/b0221657a3981f4a4f49)


## 環境

|                | Version     |
|:---------------|------------:|
| PHP            | 7.4.14      |
| Laravel        | 8.24.0      |
| mysql          | 8.0.23      |
| docker         | 20.10.2     |
| docker-compose | 1.27.4      |


## ルーティング

```PHP:routes/web.php
Route::resource('/game', App\Http\Controllers\GameController::class)->only(['create','store']);
```

`resource`で`create`と`store`を指定します。
resourceについては以下の記事がとても分かりやすかったです。ありがとうございます!
参考：[LaravelでRoute::resourceを使うときに気をつけること](https://qiita.com/sympe/items/9297f41d5f7a9d91aa11)

`create`と`store`の役割としては

- create→投稿画面の生成とstoreへデータを送信する役割
- store→データを受け取り保存する役割

です。

## モデル

```PHP:Models/Game.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Game extends Model
{
    use HasFactory;

    protected $table = "games";
	protected $fillable = [
        "id",
        "user_id",
        "name",
        "describe",
        "play_time",
        "players_minimum",
        "players_max",
        "image_path",
        "updated_at",
        "created_at",
    ];

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}
```

`$fillable`で指定して保存を許可します。指定してないとエラーになります。
`$fillable`については[こちらの記事](https://qiita.com/toro_ponz/items/b33c757cb7ba5bb48ed4)が分かりやすかったです。
ありがとうございます。


## マイグレーションファイル

```PHP:database/migrations/2021_××_××_create_games_table.php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateGamesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('games', function (Blueprint $table) {
            $table->id();
            $table->bigInteger('user_id');
            $table->string('name');
            $table->text('describe');
            $table->integer('play_time');
            $table->integer('players_minimum');
            $table->integer('players_max');
            $table->string('image_path')->nullable();
            $table->timestamps();
        });
    }

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

上記の内容でカラムを作成しました。


## コントローラー

```PHP:app/Http/Controllers/GameController.php
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
//以下createアクションでviewを表示する
public function create()
{
  return view('game.create');
}
/**
* Store a newly created resource in storage.
*
* @param  \Illuminate\Http\Request  $request
* @return \Illuminate\Http\Response
*/
//以下storeアクション
public function store(Request $request)
{
  $input = $request->only('user_id', 'name', 'describe', 'play_time', 'players_minimum', 'players_max',  'image_path');
        

  $game = new Game();
  $game->user_id = Auth::id();
  $game->name = $input["name"];
  $game->describe = $input["describe"];
  $game->play_time = $input["play_time"];
  $game->players_minimum = $input["players_minimum"];
  $game->players_max = $input["players_max"];

//以下3行はAWSのS3に保存するための記述
  $image = $request->file('image');
  $path = Storage::disk('s3')->putFile('bgame', $image, 'public');
  $game->image_path = Storage::disk('s3')->url($path);

//saveで保存
  $game->save();

  return redirect('/')->with('success', '投稿しました');
}
```

`create`はビューを返すだけのアクションです。
`store`で保存をしています。
このstoreについてはいろいろな書き方があり勉強になりました。


## ビュー

```PHP:resources/views/game/create.blade.php
<div class="container mt-4">
  <div class="border p-4">
    <h1 class="h4 mb-4 font-weight-bold">新規作成</h1>

    <form action="{{ route('game.store') }}" enctype="multipart/form-data" method="POST" id="new">
      @csrf

      <fieldset class="mb-4">

        <div class="form-group">
          <label for="subject">
            ボードゲーム名
          </label>
          <input
            id="name"
            type="text"
            name="name"
            value="{{old('name')}}"
            class="form-control"
          >
        </div>

        <div class="form-group">
          <label for="subject">
            商品説明
          </label>
          <textarea
            id="new"
            name="describe"
            value="{{old('describe')}}"
            class="form-control"
            rows="8"
          >
          </textarea>
        </div>

        <div class="form-group">
          <label for="subject">
            プレイ時間
          </label>
          <input
            type="number"
            name="play_time"
            value="{{old('play_time')}}"
            class="form-control"
          >
        </div>

        <div class="form-group">
          <label for="subject">
            最小プレイ人数
          </label>
          <input
            type="number"
            name="players_minimum"
            value="{{old('players_minimum')}}"
            class="form-control"
          >
        </div>

        <div class="form-group">
          <label for="subject">
            最大プレイ人数
          </label>
          <input
            type="text"
            name="players_max"
            value="{{old('players_max')}}"
            class="form-control"
          >
        </div>

        <div class="form-group">
          <label for="exampleInputFile">
            商品画像
          </label>
          <input
            id="image"
            type='file'
            name="image"
            accept="image/png, image/jpeg"
            class="form-control-file"
          >
        </div>

        <button type="submit" class="btn btn-primary">
          投稿する
        </button>
  
      </fieldset>
    </form>
  </div>
</div>
```

Bootstrapを活用して作成しています。
以下の記事に作り方をまとめています。
[【Laravel初心者】BootstrapでのFormの作り方](https://qiita.com/mumucochimu/items/5abefd05cf756a327b0e)


```PHP:
<form action="{{ route('game.store') }}" enctype="multipart/form-data" method="POST" id="new">
```

重要なところは上記の部分でしょうか。
`action="{{ route('game.store') }}"`でstoreへデータを送信します。
画像がある場合は`enctype="multipart/form-data"`がないとエラーになりました。
`method="POST"`でメソッドを指定しています。

以上で実装ができました。