【Laravel】CRUDメソッド一覧

# はじめに
この記事はLaravelのAPIを開発する際によく使われるメソッドおよび、Laravelの開発方法についてまとめたメモになります

都度、更新予定です

## Laravelサーバーを起動する
```
php artisan serve
```
## コントローラーを作成する
Laravelでコントローラーを作成するArtisanコマンドです
`ControllerName` は作成したいコントローラーの名前に置き換えます

例：CustomerController　など
```
php artisan make:controller ControllerName
```
## indexメソッド(一覧表示）
一般的にLaravelのコントローラーでリソース（データベースのレコードなど）の一覧を取得し、表示するために使われるメソッドです。


### ルーティング
```api.php
use App\Http\Controllers\ControllerName;

Route::get('/index', [ControllerName::class, 'index']);
```

#### api.phpが存在しない場合

https://qiita.com/EasyCoder/items/161f7d7049914d964551


### テーブル単体から全レコードを取得する(メソッド）

```***Controller.php
use App\Models\Product;

 class ControllerName extends Controller
{
    public function index()
    {
        // Productテーブルから全てのレコードを取得
        $products = Product::all();
        
        // JSON形式で返す
        return response()->json($products);
    }
}
```
### withメソッドを使ってリレーションデータを取得する


Withメソッドは次のように`リレーション関係を持ったテーブル`から関連データを取得できます





### リレーションの使用例
- 1対多の場合:親テーブル(`hasMany`)
```Category.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use App\Models\Product;

class Category extends Model
{
    public function products() 
    {
        return $this->hasMany(Product::class, 'category_id'); 
        // 外部キーは省略可能
    }
}

```
```Product.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use App\Models\Category;
use App\Models\Order;

class Product extends Model
{
    public function category()
    {
        return $this->belongsTo(Category::class, 'category_id'); 
        // 外部キーは省略可能
    }

    public function orders() 
    {
        return $this->hasMany(Order::class, 'product_id'); 
        // 外部キーは省略可能
    }
}

```
```Order.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use App\Models\Product;

class Order extends Model
{
    public function product()
    {
        return $this->belongsTo(Product::class, 'product_id'); 
        // 外部キーは省略可能
    }
}

```

外部キーは省略できますが、省略するとLaravelは親モデルの名前に基づいて外部キーを推測します。これにより、特定のテーブル名の場合には、実際には存在しない外部キーを参照しようとすることがあるため、外部キーを明示的に指定することがより確実です。

### 多対多リレーションの設定方法（リンク）

https://qiita.com/EasyCoder/items/770861cc21852a7ed0b0

### APIの使用例
- ProductとCategoryのデータを取得する場合

```***Controller.php

use App\Models\Product;

public function index()
{
    $products = Product::with('category')->get();
    return response()->json($products);
}
```
Withメソッドを使うことでProductモデルから全ての商品データを取得し、関連するCategoryデータも同時に取得できます

### ProductとCategoryおよびOrderのデータを同時に取得

```***Controller.php
use App\Models\Product;

public function index()
{
    $products = Product::with(['category', 'orders'])->get();
    return response()->json($products);
}

```
ProductモデルはCategoryとOrderに対して直接リレーションを組んでいるため、CategoryとOrderのデータを一度に取得しています。

### Orderモデルからリレーションデータを取得
```***Controller.php
use App\Models\Order;

public function index()
{
    $orders = Order::with('product.category')->get();
    return response()->json($orders);
}
```
OrderモデルはProductとは直接関係がありますが、Categoryとは直接関係がありません。CategoryはProductを介して初めてアクセスできるものなので、Orderモデルから関連するProductを取得しさらに関連しているCategoryのデータを取得しています


### Withメソッドについて
- Withメソッドには必ず引数が必要になるため、単一のテーブルに対しての利用は不向きです
- withメソッドでは、親モデルからのリレーション名を最初に、次にそのリレーションに続く子モデルのリレーション名を指定します
- 複数のリレーションを指定する場合は、ドットでつなげて記述することができます
- `'product.category'` という指定は、親モデルである `Order`から`Product`へ、さらにその`Product`から `Category`へという順番でリレーションをたどる必要があります

https://qiita.com/EasyCoder/items/011b84b60ffd2b5d111a

### Laravelでフロントエンドを実装する場合

Laravelでビューを表示さえるルーティングは`web.php`に記述します
```web.php
use App\Http\Controllers\YourControllerName;

Route::get('/index', [YourControllerName::class, 'index']);
```



次のようにモデルのデータをビューに送信します
```YourControllerName.php
use App\Models\Product;

public function index()
{
    // カテゴリ情報も一緒に取得
    $products = Product::with('category')->get();
    
    // 'index'はビュー名で、'products'は渡すデータのキーです
    return view('index', ['products' => $products]);
}
```
データを表示させるためにはBladeテンプレートが必要です

例：resources/views/index.blade.php
```index.blade .php
<!-- resources/views/index.blade.php -->
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>商品一覧</title>
</head>
<body>
    <h1>商品一覧</h1>
    <ul>
        @foreach($products as $product)
            <li>
                <strong>{{ $product->name }}</strong> (カテゴリ: {{ $product->category->name }})
            </li>
        @endforeach
    </ul>
</body>
</html>
```

## Showメソッド
### 特定のIDに関するレコードを取得する
showメソッドのリクエストは一般的にGETリクエストを使用することが標準とされています。

次のようにformタグに対してaction属性を用いてURLを設定し、受け取った変数でIDなどを設定すれば『送信ボタン』を押した際に詳細画面（show)を開くことができます

例：/request/1

```show.blade .php
@foreach($customers as $customer)
            <li>

                <form action="/request/{{ $customer->customer_id }}" method="get">
                    <input type="submit" value="送信"></p>
                </form>

            </li>
        @endforeach
```

```api.php
Route::get('/request/{id}', [***Controller::class, 'show']);
```
モデルの`findメソッド`を使うことで、指定されたIDのレコードを取得できますが、findメソッド単体では`リレーションデータは取得できません`
```***Controller.php
public function show($id)
{
    // 指定されたIDをもとにユーザー情報を取得
    $customer = User::find($id);

    // ユーザーが見つからない場合、404エラーを返す
    if (!$user) {
        return response()->json(['message' => 'User not found'], 404);
    }

    // ユーザーが見つかった場合、ユーザー情報の詳細ページを返す
    // views/customers/show
    return view('customers.show', compact('customer'));
}

```
`findOrFailメソッド`を使うとデータが見つからなかった場合、自動的に404エラーを発生させるためコードの簡略化に役立ちます
```***Controller.php
public function show($id)
{
    // 指定されたIDをもとにユーザー情報を取得、存在しない場合は404を自動で返す
    $user = User::findOrFail($id);

    // ユーザー情報をJSON形式で返す
    return response()->json($user);
}
```

### 特定のIDからリレーションデータを取得する
`Productモデル`が`Categoryモデル`に`belongsToのリレーションを持っている`場合、`findメソッド`と`withメソッド`を使ってリレーションのデータを一緒に取得することができます。

```***Controller.php
$product = Product::with('category')->find(1);
```

`with('category')`の部分は、Productモデルで定義されているcategoryというリレーションを指定しています。


## storeメソッド(保存）
### 新しいレコードを保存する
POSTリクエストで送信されたデータを使用して新しいレコードを作成します。

```***Controller.php
use App\Models\Client;
use Illuminate\Http\Request;

class ClientController extends Controller
{
    public function store(Request $request)
    {
        // バリデーション
        $validatedData = $request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:clients,email',
        ]);

        // 新しいクライアントの作成
        $client = Client::create($validatedData);

        return response()->json($client, 201);
    }
}

```
### 保存動作(store)に必要なメソッド
storeメソッドを動作させるためには最低限、次のようなメソッドが必要になります
- **Request::validate()**
    - リクエストデータが正しい形式かどうかを確認(バリデーション)するために使用します
    - 一般的にデータはjsonで送信されている
- **create()**
    - データベースに新しいレコードを作成するためのメソッドであり、通常はJSON形式のデータを与えることでレコードを追加します
- **response()->json()**
    - データをJSON形式で返すための方法です
    - このメソッドは、主に2つの引数を受け取ります（以下）
    - 『JSON形式で返したいデータ』,『HTTPステータスコード』




### バリデーションとエラーハンドリングの参考リンク    
https://qiita.com/EasyCoder/items/83e7043fd6baaf5c3176




### マスアサインメントの設定
見本のように連想配列を用いてデータを保存したい場合は、modelファイルに対してマスアサインメントの設定をする必要があります


```Client.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Client extends Model
{
    // マスアサインメントを許可するフィールドを指定
    protected $fillable = ['name', 'email'];
}
```

### マスアサインメントが不要になるケース
マスアサインメントの設定が不要となる状況は、手動で個々の属性を設定する場合です。具体的には`create()`や`update()メソッド`の代わりに、モデルのインスタンスを生成して個別にフィールドを指定して保存する方法です。

ただし、マスアサインメントを使用すれば、リクエストデータを直接モデルに渡すことができ、コードがシンプルで読みやすくなるため、複数のフィールドを個別に設定する必要がなく、開発効率が向上するためマスアサインメントを使用する方が一般的です
```***controller.php
public function store(Request $request)
{
    // リクエストデータのバリデーション
    $validatedData = $request->validate([
        'name' => 'required|string|max:255',
        'price' => 'required|integer|min:0',
        'description' => 'nullable|string|max:500', // 必須でない項目（オプション）
        'category_id' => 'required|integer|exists:categories,id', // 外部キー制約の確認
        'stock_quantity' => 'required|integer|min:0'
    ]);

    // 新しいProductインスタンスを作成
    $product = new Product();

    // プロパティに個別に値を設定（マスアサインメントは不要）
    $product->name = $validatedData['name'];
    $product->price = $validatedData['price'];
    $product->description = $validatedData['description'] ?? ''; // descriptionがnullの場合は ''　を設定する
    $product->category_id = $validatedData['category_id'];
    $product->stock_quantity = $validatedData['stock_quantity'];

    // 保存
    $product->save();

    // レスポンスを返す
    return response()->json(['message' => 'Product created successfully!'], 201);
}

```
#### プロパティに直接、値を代入する方法について
new Product()で新しいモデルインスタンスを作成して直接プロパティに値を設定しています。この場合は`$fillable`の設定は不要になります
#### save()メソッドを使用する方法
create()やupdate()メソッドは使用せずに値を保存するためには`save()メソッド`使用します。この場合マスアサインメントの設定は不要になります


### リレーションテーブルへ保存する場合
`外部キー`でリレーション関係にあるテーブルがある場合でも、条件を満たせば各々のテーブルにレコードを保存をすることは可能です

- Categorysテーブル

|    | 型        | 説明                                  |
|--------------|-----------|---------------------------------------|
| id           | 整数      | 主キー                                |
| name         | 文字列    | カテゴリ名                          

- Productテーブル

|    | 型        | 説明                                  |
|--------------|-----------|---------------------------------------|
| id           | 整数      | 主キー                                |
| name         | 文字列    | 商品名                           
| category_id  | 整数   | 外部キー（Category テーブルの id を参照）     | 外部キー（Category テーブルの id を参照） |

### リレーションテーブルにレコードを追加する場合に発生するエラー

子テーブルにレコードを追加する際、指定した外部キー（category_id）が親テーブルに存在しない場合、エラーが発生します

ただし、次の条件の場合にはエラーは発生しません

- `親テーブルにレコードを追加する場合`
    - テーブルに外部キーの制約がない限り、問題なく追加可能
- `子テーブルにレコードを追加する場合`
    - 親テーブルに存在する外部キーを指定すれば問題なく追加可能

### storeメソッドが失敗した場合

バリデーションを設定することで予期しないエラーが発生する可能性もあるため、以下の方法でバリデーションを外した状態を確認することが可能です

**マスアサインメントを使う場合**
```***controller.php
    public function store(Request $request){
    
        // $validateData = $request->validate([

            // 'name' => 'required|string|max:255',
            // 'description' => 'required|string|max:255',
            // 'price' => 'required|integer|max:255',
            // 'category_id' => 'required|integer|max:255',
            // 'stock_quantity' => 'required|integer|max:255',

        // ]);

        // $product = Product::create($request->all);

        $product = Product::create($request->all());
        
        return response()->json($product, 201);


    }

```
**マスアサインメントを使わない場合**
```***controller.php
public function store(Request $request)
{
    // リクエストデータのバリデーション
    $validatedData = $request->validate([
        'name' => 'required|string|max:255',
        'price' => 'required|integer|min:0',
        'description' => 'nullable|string|max:500', // 必須でない項目（オプション）
        'category_id' => 'required|integer|exists:categories,id', // 外部キー制約の確認
        'stock_quantity' => 'required|integer|min:0'
    ]);

    // 新しいProductインスタンスを作成
    $product = new Product();

    // プロパティに個別に値を設定（マスアサインメントは不要）
    $product->name = $validatedData['name'];
    $product->price = $validatedData['price'];
    $product->description = $validatedData['description'] ?? ''; // descriptionがnullの場合は ''　を設定する
    $product->category_id = $validatedData['category_id'];
    $product->stock_quantity = $validatedData['stock_quantity'];

    // 保存
    $product->save();

    // レスポンスを返す
    return response()->json(['message' => 'Product created successfully!'], 201);
}
```

### カスケード操作について
外部キー制約を設定することで、親テーブルのレコードが削除または更新された際に子テーブルの関連するレコードも自動的に削除または更新する機能のことをカスケードと呼びます

カスケードはマイグレーションファイルで設定することができ、外部キー制約を設定する際にカスケード操作を指定します

```マイグレーションファイル.php
Schema::create('products', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->foreignId('category_id')->constrained()->onDelete('cascade'); // カスケード削除を設定
    $table->timestamps();
});
```
上記の例では、category_id が削除された場合、関連する Product レコードも自動的に削除される設定になります。


### MySQLなどでカスケード設定
MySQLやPostgreSQLなどのデータベース管理ツールを使用して、テーブルを作成する際に、SQL文で直接カスケード操作を設定することも可能です

```sql
ALTER TABLE products
ADD CONSTRAINT fk_category
FOREIGN KEY (category_id)
REFERENCES categories(id)
ON DELETE CASCADE;  -- カスケード削除を設定
```
## updateメソッド（更新）

`PUTリクエスト`もしくは`PATCHリクエスト`で送信されたデータを使用して特定のレコードを更新します。

### ルーティング
```api.php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Api\***Controller;

// レコードを更新するルーティング

// putの場合
Route::put('/update/{id}', [ApplicationController::class, 'update']);
// patchの場合
Route::patch('/update/{id}', [ApplicationController::class, 'update']);
```
### putとpatchの違いについて
- PUT `プット`
    - テーブルのレコードを**全て更新**する場合に使用します
    - 全てのフィールドの値を指定して、新しいデータでリソースを置き換えます
- PATCH `パッチ`
    - テーブルのレコードを**一部更新**する場合に使用します
    - 更新フィールドのみを指定し、他のフィールドはそのまま保持されます

この使い分けにより、APIの設計がより明確になり、クライアント側も意図する更新操作を分かりやすく行うことができます。

逆にPUTで一部の更新したりPATCHで全ての更新することもできますが、サーバー側で意図しない`デフォルト値やnullに置き換わる可能性`もあるため、各々使い分けすることが推奨されます

### メソッド
```php
public function update(Request $request, $id)
{
    // バリデーション
    $validatedData = $request->validate([
        'name' => 'sometimes|required|string|max:255',
        'email' => 'sometimes|required|email|unique:clients,email,' . $id,
    ]);

    $client = Client::findOrFail($id);
    $client->update($validatedData);

    return response()->json($client);
}
```
- **findOrFail($id)**
指定されたIDに一致するレコードをデータベースから検索し、そのレコードを返します。 該当するレコードが見つからない場合は、ModelNotFoundException例外をスローし、通常は404エラーページ（「Not Found」）が表示されます。

- **update()**
既存のEloquentモデルインスタンスに対して、データを更新するために使用されるメソッドです。


- **sometimes**
リクエストにemailフィールドが存在する場合のみ、以下のバリデーションを適用するという意味です。つまり、emailフィールドが存在しない場合はスキップされます。
更新時には、必ずしもemailを送信する必要がない場合があるため、存在する場合のみバリデートするために使用します。

- **'unique:clients,email,'**
clientsテーブルのemailカラム内で、メールアドレスがユニーク（重複していない）であることをチェックします。

- **$id**
顧客情報を更新する際、既に存在する顧客のメールアドレスをそのまま使う場合があるので、現在の顧客（$idで識別される）のメールアドレスは重複チェックから除外します。つまり、自分のレコードに対しては重複エラーを出さないようにしています。

### リクエストの送信方法（URL)について

- HTTPメソッドが`PUT`もしくは`PATCH`の場合、特定のリソース（データ）のIDをリクエストするには、一般的にはURLにプロパティとしてIDを含めます
    - URL: http://localhost:8000/api/put/1
    - メソッド: ``put``もしくは``patch``
```ボディ.json
{
  "name": "テスト",
  "email": "tesuto@example.com"
}

```

## destroyメソッド（削除）
Laravelのコントローラー内で特定のリソース（例えば、データベースのレコード）を削除するために使用されます。
```api.php
Route::delete('/delete/{id}', [****Controller::class, 'destroy']);
```
### リクエストの送信方法（URL)について
HTTPメソッドがPUTもしくはPATCHの場合、特定のリソース（データ）のIDをリクエストするには、一般的にはURLにプロパティとしてIDを含めます
URL: http://localhost:8000/api/delete/1
メソッド: delete

```php
public function destroy($id){
        $client = Client::findOrFail($id);
        $client->delete();

        return response()->json(null, 204);
    }
```

- **json(null, 204);**
    - **null** 
レスポンスのボディに含まれるデータです。ここではnullを指定しているため、レスポンスのボディにはデータが含まれません。

    - **HTTPステータスコードの204**
    「No Content」を意味し、リクエストが成功したことを示しますが、返すべきデータがないことを示します