[Laravel]レコードをDBへ保存する方法 save()とcreate() ~備忘録~

##はじめに
この記事はプログラミング初学者による備忘録用の記事であり、また、少しでも他の初学者のお役に立てればと思い書いています。

今回は、Laravelでレコードをテーブルに追加する方法について色々と調べたので、そのメソッドの使い方や特徴を備忘録としてまとめておきたいと思います。

間違いなどがございましたら、ご指摘のほどよろしくお願い致します。

##モデルの追加と更新
Laravelでは、レコードをDBに保存する方法として、主に下記のメソッドが用意されています。

・save()
・create()
・firstOrCreate(少し特殊なので補足欄で説明します)

これらのメソッドについて、順に説明していきます。

##save()
``使い方:``
特定のテーブルに新しいレコードを保存する際は、その保存対象となるテーブルのModelクラスをインスタンス化した後に、``saveメソッド``を呼び出します。

>
モデルから新しいレコードを作成するには新しいインスタンスを作成し、saveメソッドを呼び出します。
[Laravel 6.x Eloquent:利用の開始](https://readouble.com/laravel/6.x/ja/eloquent.html#inserting-and-updating-models)

``特徴:``
・新規レコードの追加を実行する
・既存レコードの更新を実行する
・タイムスタンプ(``created_atとupdated_at``)は自動的に設定される
・Modelクラスで追加、更新するカラムを``$fillableか$guarded``で指定する必要がある(**複数代入保護の為**)
・**save()メソッドの後のインスタンスには、直前にテーブルに保存したレコードのデータが格納される**

``コード記述例:``

```.php
<?php

class Article extends Model
{
...
// fillableかguardedのどちらかを指定する必要があります
    protected $fillable = [
        'title', 'body',
    ];
//or
    protected $guarded = [
        'id',
    ];
...
}

class ArticleController extends Controller
{
public function store(Article $article, StoreRequest $request)
 {
  $article->fill(['title' => 'test','body' => 'test'])->save();
  //or
  $article->fill($request->validated())->save();
//$request->validated()はStoreRequestクラス内でバリデーションを実行しています
...
 }
}
```
大まかな流れとしては、``インスタンスの作成->属性の代入(fillメソッドを実行)->データの保存(saveメソッドを実行)``となっています。
インスタンスの作成をメソッドインジェクションで行い、属性の代入を``fillメソッド``で行い、DBへの保存を``saveメソッド``で実行します。
後で説明する``createメソッド``と同じく**複数代入保護**のため、該当するModelクラスで``$fillableか$guarded``を指定する必要があります。

**※更新時**
モデルを更新するにはまず更新対象を取得する必要があり、更新したい属性をセットしてそれからsaveメソッドを呼び出します。
この場合もupdated_atタイムスタンプは自動的に更新されますので、値を指定する必要はありません。

##create()
``使い方:``
モデルクラスから``createメソッド``を呼ぶことで、``インスタンスの作成->属性の代入(fillメソッドを実行)->データの保存(saveメソッドを実行)``を一気に実行することができます。

>
一行だけで新しいモデルを保存するには、createメソッドが利用できます。
挿入されたモデルインスタンスが、メソッドから返されます。しかし、これを利用する前に、Eloquentモデルはデフォルトで複数代入から保護されているため、モデルへfillableかguarded属性のどちらかを設定する必要があります。
[Laravel 6.x Eloquent:利用の開始](https://readouble.com/laravel/6.x/ja/eloquent.html#mass-assignment)

``特徴:``
・新規レコードの追加を実行する
・複数代入が可能
・・Modelクラスで追加対象のカラムを``$fillableか$guarded``で指定する必要がある(**複数代入保護の為**)
・createメソッドは保存したモデルインスタンスを返します

``コード記述例:``

```.php
<?php

class Article extends Model
{
...
// fillableかguardedのどちらかを指定する必要があります
    protected $fillable = [
        'title', 'body',
    ];
//or
    protected $guarded = [
        'id',
    ];
...
}

class ArticleController extends Controller
{
public function store(Article $article, StoreRequest $request)
 {
  $article->create($request->validated());
//$request->validated()はStoreRequestクラス内でバリデーションを実行しています
...
 }
}
```
大まかな流れとしては、``create() = fill()->save()``となっています。
createメソッドを使うことで、インスタンスの作成 → 属性の代入 → データの保存を一気に実行することができます。
また、作成したインスタンスをその場で返すことも可能なので、保存したデータを受け取ることができます。

注意点として、Eloquentはデフォルトで複数代入から保護されているため、createメソッドを使う際は、``$fillableか$guarded属性``のどちらかをModelクラスで指定する必要があります。

##補足
###・Eloquentの複数代入とリスクについて
複数代入とは、``入力フォームの項目名と同名のカラム(複数可)にデータを追加する``ことを指します。

Laravelでは、フォームの入力名とデータベースのカラム名を同じにすることで、入力値をデータベース（もしくはORMがインスタンスのモデル）へ代入することができてしまいます。

``~複数代入のリスク~``
以前のバージョンでは、ユーザーからの入力を元にユーザーデータを更新する際のなどに、悪意のあるユーザーが管理者権限などでテーブル操作を行う危険性がありました。
このリスクを避けるために、Laravelではバージョン４からデフォルトのままでは複数代入ができないようになりました。
複数代入する際は、``$fillableか$guarded属性``のどちらかをModelクラスで指定する必要があります。

``~$fillableと$guarded~``
Laravelでは、複数代入の危険性を避けるために``$fillableか$guarded``のどちらか一方を予め設定しておく必要があります。

``$fillable`` -> レコードを追加して良いカラムを設定(ホワイトリスト)

``$guarded`` -> レコードを追加しないカラムを設定(ブラックリスト)
全属性を複数代入可能にする場合は、$guardedプロパティに空の配列を定義します。

基本的に、create()を行う時は必ず``$fillableまたは$guarded``を設定すると覚えておきましょう。

###・firstOrCreate()
firstOrCreate()も複数代入可能な生成メソッドであり、指定されたカラム／値ペアでデータベースレコードを見つけようします。指定されたデータがデータベースで見つからない場合は、最初の引数が表す属性、任意の第２引数があればそれが表す属性も同時に含むレコードが登録されます。

``コード記述例:``

```.php
// 該当するnameデータを取得するか、存在しなければ登録する
$flight = App\Article::firstOrCreate(['title' => 'test']);
```

##参考文献
[Laravel 6.x Eloquent:利用の開始](https://readouble.com/laravel/6.x/ja/eloquent.html)
[Eloquentの複数代入のリスク](https://kore1server.com/336/Eloquent%E3%81%AE%E8%A4%87%E6%95%B0%E4%BB%A3%E5%85%A5%E3%81%AE%E3%83%AA%E3%82%B9%E3%82%AF)