2
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?

Laravel用のComposerパッケージを自作して公開してみた

Last updated at Posted at 2024-11-09

やりたかっとこと

LaravelのEloquent Builderで、LIKE検索を以下のように記述できるクラスを作った。

UserController.php
$words = ['山田', '岐阜市'];
$comulns = ['name', 'company.address'];
User::whereLike($columns, $words)->get();  // 岐阜市で働く山田さんを取得

これをいろんなプロジェクトで使いたいので、パッケージ化してcomposer requireで利用できるようにしたい。

結果

ここにパッケージをリリースして、

以下コマンドで利用可能に。

composer require kazuki/laravel-where-like

パッケージのロジック

Laravel標準機能のMixinを利用して、Illuminate\Database\Eloquent\BuilderクラスにwhereLikeメソッドを追加しています。
※Mixinは\Illuminate\Support\Traits\Macroable.phpトレイトを利用しているクラスに、メソッドを後づけできる機能です。

標準機能とはいえ、既存クラスの仕様を外部パッケージが変更するのはよろしく無いかもしれません。その是非はここでは一旦おいておきます。

全体の流れ

  1. GitHubリモートレポジトリを作成
  2. Composerパッケージを作成
  3. GitHubにリリース
  4. PackagistにSubmit

すべて以下のサイトに記載されています。この記事では、2.でMixinを有効にできた手順を紹介します。

ディレクトリ構成

composer initで作成されたディレクトリに、ソースを追加&composer.jsonの変更をしていきます。

/
├─ .git
│  └─ ...
├─ src
│  ├─ BuilderMixin.php // 追加
│  └─ WhereLikeServiceProvider.php // 追加
├─ vendor
│  └─ ...
├─ composer.json // 編集
├─ composer.lock
├─ LICENSE
└─ README.md

src

BuilderMixin.php(メソッドの実装)
BuilderMixin.php
<?php

namespace Kazuki\LaravelWhereLike;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Arr;

class BuilderMixin
{
    public function whereLike()
    {
        /**
         * あいまい検索
         *
         * @param  array|string  $attributes 検索対象とするカラム名。リレーション先のカラム名を指定する場合は、テーブル名とカラム名を`.`で連結する(複数リレーション連結可能)。
         * @param  array|string  $keywords 検索値。配列の場合は、各検索値をor条件で連結する。
         * @param  int  $position `-1` => 前方一致 `1` => 後方一致 `0` => 部分一致(デフォルトは`0`)
         * @param  string  $boolean `and`か`or`を指定。デフォルトは`and`。
         * @return \Illuminate\Database\Eloquent\Builder
         */
        return function (array|string $attributes, array|string $keywords, int $position = 0, $boolean = 'and') {
            // 検索値が配列の場合、再帰呼び出し。
            // 検索値同士は、or検索で連結する。
            if (is_array($keywords)) {
                /** @var Builder $this */
                return $this->where(function (Builder $query) use ($attributes, $keywords, $position) {
                    foreach ($keywords as $keyword) {
                        $query->whereLike($attributes, $keyword, $position, 'or');
                    }
                }, boolean: $boolean);  // and・or 条件
            }

            // SQLインジェクション対策
            $keywords = addcslashes($keywords, '\_%');

            // 検索位置
            $condition = [
                1 => "{$keywords}%",
                -1 => "%{$keywords}",
            ][$position] ?? "%{$keywords}%";

            // カラム名が単体(文字列)で与えられた場合、配列に変換
            if (! is_array($attributes)) {
                $attributes = [$attributes];
            }

            /** @var Builder $this */
            return $this->where(function (Builder $query) use ($attributes, $condition) {
                foreach (Arr::wrap($attributes) as $attribute) {
                    $query->when(
                        // $attributeが、'.'(リレーション)を含むか確認
                        ! ($attribute instanceof \Illuminate\Contracts\Database\Query\Expression) &&
                        str_contains((string) $attribute, '.'),
                        // '.'(リレーション)を含む場合
                        function (Builder $query) use ($attribute, $condition) {
                            // $attributeを'.'でリレーションとカラム名に分割
                            $attrs = explode('.', (string) $attribute);
                            $relatedAttribute = array_pop($attrs);
                            $relation = implode('.', $attrs);
                            $query->orWhereRelation($relation, $relatedAttribute, 'LIKE', $condition);
                        },
                        // '.'(リレーション)を含まない場合
                        function (Builder $query) use ($attribute, $condition) {
                            $query->orWhere($attribute, 'LIKE', $condition);
                        }
                    );
                }
            }, boolean: $boolean);  // and・or 条件
        };
    }

    public function orWhereLike()
    {
        /**
         * あいまい検索(or)
         *
         * @param  array|string  $attributes 検索対象とするカラム名。リレーション先のカラム名を指定する場合は、テーブル名とカラム名を`.`で連結する。
         * @param  array|string  $keywords 検索値
         * @param  int  $position `-1` => 前方一致 `1` => 後方一致 `0` => 部分一致(デフォルトは`0`)
         * @return \Illuminate\Database\Eloquent\Builder
         */
        return function (array|string $attributes, array|string $keywords, int $position = 0) {
            /** @var Builder $this */
            return $this->whereLike($attributes, $keywords, $position, 'or');
        };
    }
}

ServiceProviderの継承クラスを作成して、bootメソッド内でBuilderクラスにメソッドを追加します。

WhereLikeServiceProvider.php
<?php

namespace Kazuki\LaravelWhereLike;

use Illuminate\Support\ServiceProvider;
use Illuminate\Database\Eloquent\Builder;

class WhereLikeServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Builder::mixin(new BuilderMixin());  // Builderにメソッドを追加
    }
}

Composer.json

上の参考記事で作成したファイルに依存関係とServicePoroviderを追記。

composer.json
{
    "name": "kazuki/laravel-where-like",
    "description": "Add where like query to laravel eloquent builder.",
    "type": "library",
    "license": "MIT",
    
    "autoload": {
        "psr-4": {
            "Kazuki\\LaravelWhereLike\\": "src/"
        }
    },
    "authors": [
        {
            "name": "oor30",
            "email": "kazuki.w.0920@gmail.com"
        }
    ],
    "minimum-stability": "stable",
    
    // 以下を追記(※実際にJSONにコメントを記載してはいけません)
    "require": {
        "php": ">=8.0",
        "illuminate/database": "^9.0 || ^10.0"
    },
    "extra": {
        "laravel": {
            "providers": [
                "Kazuki\\LaravelWhereLike\\WhereLikeServiceProvider"
            ]
        }
    }
}
2
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
2
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?