0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Laravel】「クエリパラメータに存在する場合のみ検索条件とする」をEloquentでスマートに

Posted at

本記事における PHP と Laravel のバージョン

$ sail php -v

PHP 8.1.13 (cli) (built: Nov 26 2022 14:07:55) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.13, Copyright (c) Zend Technologies
    with Zend OPcache v8.1.13, Copyright (c), by Zend Technologies
    with Xdebug v3.1.5, Copyright (c) 2002-2022, by Derick Rethans
$ sail artisan --version

Laravel Framework 9.42.2

前提

本記事では例として、都道府県名を一覧取得できるAPIを実装するものとします。
本機能はクエリパラメータを付与することができますが必須ではないため、クエリパラメータの存在有無によって検索条件を変えられるような実装にする必要があります。

リクエストに関する仕様

GET /api/prefectures

パラメータ名 論理名 型式 必須 説明
prefecture_name 都道府県名 string No 省略可能。
設定されている場合は、値に完全一致する都道府県名のみレスポンスに含める。

レスポンスに関する仕様

下表のオブジェクトを格納した配列形式のJSONを返却する。

フィールド 論理名 型式 説明
name 都道府県名 string
created_at 登録日時 string Y-m-d H:i:s 形式とする。
updated_at 更新日時 string Y-m-d H:i:s 形式とする。

テーブル構造、および初期データは以下の通りです。

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('prefectures', function (Blueprint $table) {
            $table->id();
            $table->string('name', 20)->comment('都道府県名');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('prefectures');
    }
};
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

class PrefectureSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        DB::table('prefectures')->insert(
            [
                'name' => '北海道'
            ],
            [
                'name' => '青森県'
            ],
            [
                'name' => '岩手県'
            ],
            [
                'name' => '宮城県'
            ],
            [
                'name' => '秋田県'
            ],
            // 中略
            [
                'name' => '宮崎県'
            ],
            [
                'name' => '鹿児島県'
            ],
            [
                'name' => '沖縄県'
            ]
        );
    }
}
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;

class Prefecture extends Model
{
    use HasFactory;

    protected $hidden = [
        'id'
    ];

    protected function createdAt(): Attribute
    {
        return Attribute::make(
            get: fn ($value) => Carbon::parse($value)->format(Carbon::DEFAULT_TO_STRING_FORMAT)
        );
    }

    protected function updatedAt(): Attribute
    {
        return Attribute::make(
            get: fn ($value) => Carbon::parse($value)->format(Carbon::DEFAULT_TO_STRING_FORMAT)
        );
    }
}

Eloquent::when() を使う

Eloquent::when() は第1引数に指定した値が true の場合のみ、第2引数に指定したコールバックを実行します。

今回は以下のような関係になります。

  • 第1引数 = クエリパラメータに prefecture_name が存在する場合。
  • 第2引数 = クエリパラメータ prefecture_name の値を検索条件とする。

実装したコードは以下になります。
メソッドチェーンを使って記述できるため、DBアクセスの際にどのような条件を設定しているかが追いやすく、コードも簡潔です。

<?php

use App\Models\Prefecture;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

// 前略

Route::get('/prefectures', function (Request $request) {
    $callback = function (Builder $builder) use ($request) {
        return $builder->where('name', '=', $request->query('prefecture_name'));
    };
    $data = Prefecture::query()
        ->when($request->has('prefecture_name'), $callback)
        ->get()
        ->all();
    return response()->json($data, 200);
});

if を使う

比較のために、愚直に if で実装した場合も記述しておこうと思います。

<?php

use App\Models\Prefecture;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

// 前略

Route::get('/prefectures', function (Request $request) {
    $query = Prefecture::query();
    if ($request->has('prefecture_name')) {
        $query = $query->where('name', '=', $request->query('prefecture_name'));
    }
    $data = $query->get()->all();

    return response()->json($data, 200);
});

一見するとシンプルになったように思えますが $query->get()->all() の部分で暗黙的に検索条件が追加されている場合があるため、クエリビルダの状態がどうなっているのかをイメージしながらコードを読まなければなりません。

これは簡単な例なので大したことないですが、もっとパラメータが多い場合や、長大なコードの場合は、コードを読んだだけだとどのようなクエリが発行されるかイメージしづらくなっていくと思います。

まとめ

Eloquent::when() を使って特定の場合のみ条件を追加させる方法を知りました。
Eloquent::when() 自体は検索条件追加のメソッドではないため、第2引数に指定するコールバックの内容次第で色々なことができます。
便利な使い方を知ったら、また記事にしようと思います。

0
1
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
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?