0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Laravel】CRUDメソッド一覧

Last updated at Posted at 2024-09-27

はじめに

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

都度、更新予定です

indexメソッド(一覧表示)

一般的にLaravelのコントローラーでリソース(データベースのレコードなど)の一覧を取得し、表示するために使われるメソッドです。

テーブル単体から全レコードを取得する

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

 class ***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は親モデルの名前に基づいて外部キーを推測します。これにより、特定のテーブル名の場合には、実際には存在しない外部キーを参照しようとすることがあるため、外部キーを明示的に指定することがより確実です。

多対多リレーションの設定方法(リンク)

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へという順番でリレーションをたどる必要があります

Showメソッド

特定のIDに関するレコードを取得する

api.php
Route::get('/request/{id}', [***Controller::class, 'show']);

モデルのfindメソッドを使うことで、指定されたIDのレコードを取得できますが、findメソッド単体ではリレーションデータは取得できません

***Controller.php
public function show($id)
{
    // 指定されたIDをもとにユーザー情報を取得
    $user = User::find($id);

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

    // ユーザーが見つかった場合、ユーザー情報をJSON形式で返す
    return response()->json($user);
}

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ステータスコード』

バリデーションとエラーハンドリングの参考リンク

マスアサインメントの設定

見本のように連想配列を用いてデータを保存したい場合は、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)が親テーブルに存在しない場合、エラーが発生します

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

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

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文で直接カスケード操作を設定することも可能です

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に置き換わる可能性もあるため、各々使い分けすることが推奨されます

メソッド

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を含めます
ボディ.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

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

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

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

0
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?