【laravel】データベースの操作：Eloquent ORM編

# 概要
Eloquent
Eloquentを使用したデータベースの操作です。
全てをまとめて一つの記事として投稿したかったのですが長くなったため分割しました。
Eloquentの基本的な部分とリレーションについては以下の記事をご参考ください。

[【laravel】Eloquentの基本](https://qiita.com/gone0021/items/4b1b4555b9b8f2eca458)
[【laravel】データベースのリレーション：Eloquent ORM編]()（作成後にリンクします）


# コントローラーによるデータベースの操作
モデルクラスでは`クラス名::メソッド()`のように記述してクリエビルダでデータベースを操作します。
ここではモデルクラス、コントローラー、bladeテンプレートの3つで操作するため、以下のように3つのファイルの中身をタイトルで区別して記述していきます。
また、[【laravel】マイグレーション](https://qiita.com/gone0021/items/5cec0c945b41c00441cc)で作成したsampleテーブルの内容を例に作成します。

```:Model
// モデルクラス
```

```:Controller
// コントローラー
```

```:view
// bladeテンプレート
```


## 全てのレコードを取得
`all`で値を取得します。
`SELECT* FROM sample`のSQL文と同じ内容です。

```php:Controller
public function select(Request $request)
{
  $items = sample::all();
  return view('sample.index', ['items' => $items]);
}

// 記述方法：クラス名::all();
```

```php:view
@foreach ($items as $item)
  {{ $items->name }}
  {{ $items->tel }}
  // 中略... 
@endforeach
```

## id（主キー）による検索
主キーで検索する場合は`find()`を使用します。
`SELECTION * FROM sample where id =:primary`のSQL文と同じような内容です。

```php:view
<input type="text" name="primary" id="" value="">
```

```php:Controller
public function find(Request $request)
{
  $item = sample::find($request->primary);
  return view('sample.index', ['item' => $item]);
}

// 記述方法：クラス名::find('主キー');
```

```php:view
{{ $items->name }}
{{ $items->tel }}
// 中略... 
```

### findメソッドの注意点
`find()`の引数の型によって`first()`で文字列として値を取得するか、`findMany()`で配列として取得するかが変わります。
`findMany()`は`get()`で配列として値を取得するメソッドです。
長くなるのでコメントは割愛しますが、Builderからfindメソッドを抜粋します。

```php:Builder
namespace Illuminate\Database\Eloquent;

  public function find($id, $columns = ['*'])
  {
    if (is_array($id) || $id instanceof Arrayable) {
      return $this->findMany($id, $columns);
  }

  return $this->whereKey($id)->first($columns);
}
```

### 複数のidから値を取得する
findメソッドで複数のレコードを取得する場合は引数を配列で記述するか、findメソッドの代わりに`findMany()`で記述します。
また、下記する`複数の値を検索対象にする`の段落の書き方で検索する値に主キーを指定する方法でも可能です。

```php:Controller
$flights = Sample::find([1, 2, 3,]);
// または
$flights = Sample::findMany([1, 2, 3,]);
```


## 特定の値を検索
主キー以外の値で検索する場合は`where()`を使用します。
`SELECTION * FROM samle where name =:'name`のSQL文と同じ内容です。
ここでは`get()`で全ての値を取得していますが、`get('カラム名', '値')`や`first()`で取得する値を指定することもできます。


```php:view
<input type="text" name="name" id="" value="">
```

```php:Controller
public function search(Request $request)
{
  $items = sample::where($request->name)->get();
  return view('sample.index', ['items' => $items]);
}

// 記述方法：クラス名::where('検索値')->取得方法;
```

```php:view
@foreach ($items as $item)
  {{ $items->name }}
  {{ $items->tel }}
  // 中略... 
@endforeach
```

## 複数の値を検索対象にする
複数の値を検索対象とする場合は`whrer()`と`orwhere()`をメソッドチェーンしていきます。

`SELECTION * FROM samle where name =:name or address = :adrress`のSQL文と同じ内容です。
ここでは`address`を`like %search%`で検索していきます。


```php:view
<input type="text" name="search" id="" value="">
```

```php:Controller
public function search(Request $request)
{
  $items = sample::samplee::where('name', ($request->search)->orwhere('address', 'LIKE', "%{$request->search}%")->get();
  return view('sample.index', ['items' => $items]);
}

// 記述方法：クラス名::where('検索値')->取得方法;
```

```php:view
@foreach ($items as $item)
  {{ $items->name }}
  {{ $items->tel }}
  // 中略... 
@endforeach
```


## レコードの新規登録
NotNullのname、tel、addressを新規登録します。

まずはフォームで新規作成する値をリクエストします。
そしてリクエストした値はインスタンスしたモデルクラスの属性に代入して`save()`で保存されます。

また、idはオートインクリメントでテーブルを作成しているので`$guarded`プロパティで保護しますが、当投稿では共通した設定のため以下では省略します。

```php:view
<input type="text" name="name" id="" value="">
<input type="text" name="tel" id="" value="">
<input type="text" name="address" id="" value="">
```

```php:Model
// 以下に共通
protected $guarded = array('id');
```

```php:Controller
public function insert(Request $request)
{
  $sample = new Smaple;
  $sample->name = $request->name;
  $sample->tel = $request->tel;
  $sample->address = $request->addres;
  $sample->save();
}

// 記述方法：属性に登録する値を代入して保存
```


## 特定のidのレコードの更新する
新規登録と同じようにNotNullのname、tel、addressを更新します。
順序としては、更新する値の主キー（id）を取得してから、そのidに該当する値のプロパティを更新するという処理です。

更新も新規登録と同じように、リクエストした値をモデルクラスの属性に代入してsave()で保存します。

```php:view
<input type="text" name="id" id="" value="">
<input type="text" name="name" id="" value="">
<input type="text" name="tel" id="" value="">
<input type="text" name="address" id="" value="">
```

```php:Controller
public function update() {
  $sample = Sample::find($request->id);
  $sample->name = $request->name;
  $sample->tel = $request->tel;
  $sample->address = $request->addres;
  $sample->save();
}

// 記述方法：検索した属性に更新する値を代入して保存
```

### saveメソッドにおける注意点
`save()`は単一のレコードしか更新できないため、複数レコードの値をまとめて更新する場合は`update()`で保存します。
`update()`は指定のフィールドに何かしらの値があれば保存するというメソッドです。
`fill()`については後の段落で解説します。

長くなるのでコメントは割愛しますがModelからupdateメソッドを抜粋します。
`$this->exists`は初期値がfalseとなっているbool型の変数です。


```php:Illuminate\Database\Eloquent
public function update(array $attributes = [], array $options = [])
{
  if (! $this->exists) {
    return false;
  }

  return $this->fill($attributes)->save($options);
}
```

### updateメソッドによる値の更新
例にしているsampleテーブルに`is_repeat`という判定の項目があるので、このフィールドが1となっているレコードの値を更新するという例を書きます。
`where()`で対象のカラムを検索して`update('値の配列')`で記述します。

```php:Controller
public function update(PersonRequest $request)
{
  Sample::where('is_repeat','0')->update(['comment'=>リピーターです,]);
  // commentのカラムは存在しませんが追加したと仮定します
}
// 記述方法：Sample::where('検索値')->update([更新する値]);

```


## fillメソッドによるレコードの取得
`fill()`は引数に用意された配列の値をモデルのプロパティに代入するメソッドです。
そのため、フォームからリクエストされる値の全てを保存するため不要な情報を削除してから保存します。
エラーにはなりませんが不要な情報は不要です。


```php:Controller
public function create(Request $request)
{
  $sample = new Smaple;
  $form = $request->all();
  unset($form['_token']);

  $sample->fill($form)->save();
  return redirect('/sample');
}
```


## レコードの削除
レコードを削除する場合は、削除するレコードを取得して`delete()`で実行します。

```php:Controller
public function remove(Request $request)
{
  Person::find($request->id)->delete();
  return redirect('/person');
}
```


### レコードの論理削除
Eloquentではテーブルの物理削除と論理削除を設定で変更することができます。
デフォルトでは物理削除となっているので、論理削除をする場合は以下のように設定します。

```php:Model
use SoftDeletes;
```


### 論理削除したレコードの確認
論理削除した値は`onlyTrashed()`で確認することができます。
また、論理削除されていない値と論理削除された値の全てを確認する場合は`withTrashed()`を使用します。

```php:Controller
public function showDel(Request $request)
{
  $items = Person::onlyTrashed()->get();
  $param = ['items' => $items];
  return view('person.find', $param);
}
```


## バリデーションの作成
モデルクラスでバリデーションのルールとメッセージを作成する例を記述します。
コントローラーに全てを記述するとスクリプトが読みづらくなるため、バリデーションのルールとメッセージはモデルに記入しています。

また、バリデーションは`\Illuminate\Validation\ValidatesRequests`のvalidateメソッドが使用されています。

validateメソッドについては以下をご参考ください。
[validateメソッドによるバリデーション]
(https://qiita.com/gone0021/items/5699c29c7ce64f08b8d2)

```php:Controller
public function insert(Request $request)
{
  $this->validate($request, Sample::$rules, Sample::$messages);
}

// 記述方法：$this->validate(検証の値, 検証ルール, エラーメッセージ);
```

```php:Sample
class Sample extends Model
{  
  public static $rules = array(
    'name' => ['regex:/^[a-zA-Z0-9]+$/u' ],
    'mail' => 'email',
    'age' => 'integer|min:0|max:150',
  );

  public static $messages = [
    'name.regex' => '半角英数字のみ',
    'mail.email' => 'メールアドレスが必要です',
    'age.numeric' => '整数で入力してください',
    'age.between' => '0～150まで',
  ];
}
```

　
# モデルクラスにメソッドを作成する
モデルクラスにメソッドを記述してデータベースを操作する方法です。
bladeテンプレートでform入力によるリクエストを行い、コントローラーでリクエストの処理を振り分け、モデルクラスで処理を記述してデータベースを操作していきます。



## 出力する値のフォーマットを定義する
モデルで出力する値のフォーマットを作成してテンプレートに出力します。
モデルクラスに出力する値を定義したら、テンプレートで定義したメソッドを読み込むという流れです。

ここでは`name`で検索していることを仮定したコントローラーだけ作成して、リクエストするテンプレートは割愛しています。


```php:Controller
public function search(Request $request)
{
  $items = sample::where($request->name)->get();
  return view('sample.index', ['items' => $items]);
}
```

```php:Model
public function getData()
{
  if (is_repeat != 0) {
    $repeat = '再';
  } else {
    $repeat = '初';
  }
  return $this->name . ' (' . $repeat . ')';
}
```

```php:view
@foreach
  {{$item->getData()}}
@endforeach

// 出力例：なまえ (再)
```

# スコープによる検索
モデルクラスでは上記したメソッドの他に、スコープというメソッドの記述方法があります。
スコープとは検索などの際に条件をメソッド化する機能です。
よく使用する条件や煩雑になる条件をスコープとして記述することによって可読性を高められて作業が楽になるというメリットがあります。
またスコープには、Eloquentモデルを使うと自動で条件が追加される`グローバルスコープ`と、条件を追加したい時にだけ使う`ローカルスコープ`があります。
グローバルスコープの使用例が思いつかなかったので、当投稿では`ローカルスコープ`のみ記述します。


## ローカルスコープの作成と使用
ローカルスコープを作成するにはモデルクラスに`scope◯◯($queru, 引数)`というメソッドを作成します。
第一引数の`$query`はWhereで取得されるのと同じようにBuilderのインスタンスが渡れます。
作成したローカルスコープはコントローラーでメソッドとして呼び出すことができます。


```php:Model
public function scopeNameOrAddress($query, $str)
{
  return $query->where('name', 'LIKE', "%{$str}%")->orwhere('address', 'LIKE', "%{$str}%");
}
```

```php:Controller
public function search(PersonRequest $request)
{
  $items = Person::Name($request->name)->Address($request->address)->get();
  $param = ['items' => $items];
  return view('person.find', $param);
}
```

## 複数のスコープを使用する
上記したスコープに`email`の登録があるかどうかを検索条件に追加してみます。


```php:Model
public function scopeNameOrAddress($query, $str)
{
  return $query->where('name', 'LIKE', "%{$str}%")->orwhere('address', 'LIKE', "%{$str}%");
}

public function scopeEmail($query, $str)
{
  return $query->where('email', 'LIKE', "%{$str}%");
}
```

```php:Controller
public function search(PersonRequest $request)
{
  $items = Person::NameOrAddress($request->input)->Email($request->email)->get();
  $param = ['items' => $items];
  return view('person.find', $param);
}
```



# 参考

[Laravel 6.x Eloquent：利用の開始]
(https://readouble.com/laravel/6.x/ja/eloquent.html)