PHPackages                             haikara/sequel-builder - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. haikara/sequel-builder

ActiveLibrary[Database &amp; ORM](/categories/database)

haikara/sequel-builder
======================

0.1.0(1y ago)08PHPPHP &gt;=8.0.0 &lt;8.4.0

Since Aug 17Pushed 1y ago1 watchersCompare

[ Source](https://github.com/HaikaraSakura/sequel-builder)[ Packagist](https://packagist.org/packages/haikara/sequel-builder)[ RSS](/packages/haikara-sequel-builder/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (1)DependenciesVersions (2)Used By (0)

sequel-builder
==============

[](#sequel-builder)

SQL文の組み立てをおこなうライブラリ。
プレースホルダーを含むSQL文を組み立て、埋め込む値を保持する。
SQLの文法に近い書き心地を目標とする。

利用方法
----

[](#利用方法)

以下のサンプルではPHP8.0以上を前提とする。

### ビルダーのインスタンス化と、SQL文の取得

[](#ビルダーのインスタンス化とsql文の取得)

基本クラスとして、Select、Insert、Update、Deleteが存在する。
いずれもBuilderクラスを継承しており、以下のように用いる。

```
// コンストラクタの引数にテーブル名を渡す。
$builder = SQL::select('items');

 // SQL文の組み立てを実行
$builder->build();

// SELECT items.* FROM items という文字列を取得できる。
$builder->getQuery();
```

### カラムの指定

[](#カラムの指定)

SELECT文で取得するカラムを指定したい場合、columnsメソッドに可変長引数で指定する。

```
// メソッドチェーンで記述する場合、PHPの構文上、インスタンス化部分を()で囲む必要がる。
$builder = SQL::select('items')
    ->columns('id', 'item_code', 'item_name', 'category_id');

/*
    SELECT
        id, item_code, item_name, category_id
    FROM items
*/
```

### WHERE

[](#where)

WHERE句による絞り込みは、whereメソッドにcallableな値を渡す形で記述する。
callable値の実行、引数にRulesクラスのインスタンスが渡されるので、必要なメソッドを呼び出したうえでreturnする。

Rulesクラスに備わるメソッドは様々なので、以下では簡易な例を紹介する。

```
// item_codeが00001で、deleted_flagがNULLではないレコードを抽出
$builder = SQL::select('items')
    ->where(fn (Rules $rules) => $rules
        ->equals('item_code', '00001')
        ->isNotNull('deleted_flag'));

/*
    SELECT items.*
    FROM items
    WHERE
        item_code = ?
        AND deleted_flag IS NOT NULL
*/
```

### テーブルの結合

[](#テーブルの結合)

LEFT JOINなど、テーブル結合の記述。
ON句での結合条件の記述は、WHEREと同じくcallableとRulesクラスを用いる。

```
/* equalsの第二引数に、テーブル名.カラム名の文字列をそのまま渡すと、エスケープされてしまい、
テーブル名.カラム名として扱われなくなってしまうので、Rawクラスを渡す必要がある。 */
$builder = SQL::select('items')
    ->columns('items.id', 'items.item_code', 'items.item_name', 'categories.category_name')
    ->leftJoin('categories', fn (Rules $rules) => $rules
        ->equals('categories.id', new Raw('items.category_id')))

/*
    SELECT
        items.id, items.item_code, items.item_name, categories.category_name
    FROM items
    LEFT JOIN categories
        ON categories.id = items.category_id
*/
```

### GROUP BYとHAVING

[](#group-byとhaving)

HAVINGの使い方はWHEREとまったく同じになる。

```
// 取引先ごとの月間売上テーブルを検索。年間の売上が1000000以上の取引先、という条件で絞り込む。
// customer_code = 取引先コード、amount = 売上金額とする。
$builder = SQL::select('monthly_sales')
    ->columns('customer_code', 'SUM(amount) AS yearly_sales')
    ->groupBy('customer_code')
    ->having(fn (Rules $rules) => $rules
        ->compare('yearly_sales', '>=', 1000000));

/*
    SELECT
        customer_code, SUM(amount) AS yearly_sales
    FROM monthly_sales
    GROUP BY customer_code
    HAVING
        yearly_sales >= 1000000
*/
```

### ORDER BY

[](#order-by)

```
// 月間売上テーブルを集計し、年間売上の多い順に取得
$builder = SQL::select('monthly_sales')
    ->columns('customer_code', 'SUM(amount) AS yearly_sales')
    ->groupBy('customer_code')
    ->orderByDesc('yearly_sales');

/*
    SELECT
        customer_code, SUM(amount) AS yearly_sales
    FROM monthly_sales
    GROUP BY customer_code
    ORDER BY yearly_sales DESC
    */
```

### LIMITとOFFSET

[](#limitとoffset)

limitメソッドとoffsetメソッドがあり、別々に指定できるが、
OFFSET値は、limitの第二引数にまとめて渡すこともできる。

```
// 月間売上テーブルを集計し、年間売上の多い順に取得
$current_page = 5;
$builder = SQL::select('items')
    ->limit(25)
    ->offset(25 * ($current_page - 1));

/*
    SELECT items.*
    FROM items
    LIMIT 25 OFFSET 100
*/

$builder = SQL::select('items')
    ->limit(25, 25 * ($current_page - 1));

/*
    SELECT items.*
    FROM items
    LIMIT 25, 100
*/
```

### ページング

[](#ページング)

ページネーションに必要なLIMITとOFFSETの値を算出して指定する。

```
$builder = SQL::select('items')
    ->paging(current_page: 3, limit: 25);
```

### 全件数のカウント

[](#全件数のカウント)

MySQLのFOUND\_ROWS関数は非推奨になったので、COUNT関数を用いて全件数を取得する必要があるが、
レコードを取得するするSQLと全件数を取得するSQLは、似たような記述ではあるものの共通化が手間なので、
手軽に全件数を取得するSQLを組み立てる方法として`Select::buildFoundRows`を用意している。

```
$builder = SQL::select('customers')
    ->where(fn(Rules $rules) => $rules
        ->when(
            is_string($get['customer_code']) && $get['customer_code'] !== '',
            fn(Rules $rules) => $rules->equals('customer_code', $get['customer_code'])
        )
        ->when(
            is_string($get['customer_name']) && $get['customer_name'] !== '',
            fn(Rules $rules) => $rules->equals('customer_name', $get['customer_name'])
        )
    )
    ->paging(current_page: $get['page'], limit: 25);

/*
     SELECT *
     FROM customers
     WHERE customer_code = ? AND customer_name = ?
     LIMIT ?, ?
*/

// WHERE句などの記述はそのままに、COUNT関数を実行するSQLを組み立てるSelectクラスのオブジェクトを取得する。
$count_builder = $builder->buildFoundRows();

/*
     SELECT COUNT(*) OVER()
     FROM customers
     WHERE customer_code = ? AND customer_name = ?
     LIMIT ?

     ※LIMITは常に1が指定される
*/
```

Rulesクラスの詳細
-----------

[](#rulesクラスの詳細)

whereメソッドなどで用いるRulesクラスのメソッドを詳しく紹介する。

### 条件分岐

[](#条件分岐)

whenメソッドで条件分岐を記述できる。これはSQLの構文ではなく、
PHPのif文によって動的に検索条件を組み変える際の、より良い代替手段として用意されている。

whenの第一引数に条件式（bool値）を渡し、第二引数にcallableを渡す。

```
// ユーザーが検索フォームで入力した値
$_GET = [
    'item_name' => '商品名',
    'category_id' => ''
];

$builder = SQL::select('items')
    ->where(fn (Rules $rules) => $rules
        ->isNotNull('deleted_flag')
        ->when(
            isset($_GET['name']) && $_GET['item_name'] !== '', // この式がtrueなら第二引数が有効になり、WHERE句に追記される
            fn (Rules $rules) => $rules->like('item_name', $_GET['item_name'])
        )
        ->when(
            isset($_GET['category_id']) && $_GET['category_id'] !== '',
            fn (Rules $rules) => $rules->equals('category_id', $_GET['category_id'])
        ));

/*
    category_idは条件を満たさないので含まれない

    SELECT *
    FROM items
    WHERE
        deleted_flag IS NOT NULL
        AND item_name LIKE ?
*/
```

### OR

[](#or)

Rulesクラスの同じオブジェクトに対して指定した条件は、すべて並列にANDで繋がれる。
ORを書きたい場合は、anyメソッドを用いて、別のRulesオブジェクトに指定する形になる。Rulesのネスト。

```
// category_idが1か2の商品情報を取得する

$builder = SQL::select('items')
    ->where(fn (Rules $rules) => $rules
        ->isNotNull('deleted_flag')
        ->any(fn (Rules $rules) => $rules
            ->equals('category_id', 1)
            ->equals('category_id', 2)));

/*
    SELECT items.*
    FROM items
    WHERE
        deleted_flag IS NOT NULL
        AND (
            category_id = ?
            OR category_id = ?
        )
*/
```

### INとサブクエリ

[](#inとサブクエリ)

```
$builder = SQL::select('items')
    ->where(fn (Rules $rules) => $rules
        ->isNotNull('deleted_flag')
        ->in('category_id', [1, 2]));

/*
    SELECT items.*
    FROM items
    WHERE
        deleted_flag IS NOT NULL
        AND category_id IN (?, ?)
*/
```

サブクエリ
-----

[](#サブクエリ)

サブクエリが静的なSQLなら文字列で記述しても問題ない。
値のバインドを伴う動的なSQLではSQL::selectを使う必要がある。

### WHERE ... INにサブクエリを用いる

[](#where--inにサブクエリを用いる)

inメソッドの第二引数に配列ではなくSelectクラスのオブジェクトを渡すことで、サブクエリを用いた絞り込みができる。

```
// 何らかの申請情報を持つrequestsテーブルと、承認された申請のidを保持するapproved_requestsテーブルを想定。
// サブクエリを用いて、承認済みの申請の情報のみを取得する。

$builder = SQL::select('requests')
    ->where(fn (Rules $rules) => $rules
        ->in('id', SQL::select('approved_requests')->columns('request_id')));

/*
    SELECT requests.*
    FROM requests
    WHERE
        id IN (SELECT request_id FROM approved_requests)
*/
```

### 比較にサブクエリを用いる

[](#比較にサブクエリを用いる)

サブクエリの結果を値の比較に用いる。

```
// 商品全体の平均価格より価格が高い商品を抽出するSQL
$builder = SQL::select('items')
    ->where(fn (Rules $rules) => $rules
        ->compare('price', '>', SQL::select('items')->columns('AVG(price)')));
```

テーブル結合にサブクエリを用いる
----------------

[](#テーブル結合にサブクエリを用いる)

```
$builder = SQL::select('items')
    ->columns('id', 'item_name')
    ->leftJoin(
        SQL::alias(
            // サブクエリ
            SQL::select('item_categories')->where(
                fn (Rules $rules) => $rules->isNotNull('deleted_flag')
            ),
            // 別名
            'item_categories'
        ),
        // 結合条件
        SQL::rules()->equals('items.category_id', SQL::raw('item_categories.id'))
    );

// 下記でも同じ結果になる
$builder = SQL::select('items')
    ->columns('id', 'item_name')
    ->leftJoinSubQuery(
        // サブクエリ
        SQL::select('item_categories')->where(
            fn (Rules $rules) => $rules->->isNotNull('deleted_flag')
        ),
        // 別名
        'item_categories',
        // 結合条件
        SQL::rules()->equals('items.category_id', SQL::raw('item_categories.id'))
    );
```

### カラム指定にサブクエリを用いる

[](#カラム指定にサブクエリを用いる)

```
// 商品テーブルを集計し、カテゴリごとの平均価格と、全体の平均価格を取得するSQLを想定
$builder = SQL::select('items')
    ->columns(
        'id',
        'item_name',
        'AVG(price) AS category_avg_price' // カテゴリごとの平均
        [SQL::select('items')->columns('AVG(price)'), 'all_avg_price'] // 全体の平均
    )
    ->groupBy('category_id');
```

CASE文
-----

[](#case文)

下記のようなクエリの組み立てを想定する。

```
SELECT
    id,
    category_name,
    CASE
        WHEN id = ? THEN 'selected'
        ELSE ''
    END AS selected
FROM categories
```

### CaseStatementを用いてCASE文を記述する例

[](#casestatementを用いてcase文を記述する例)

ビルダーの機能だけで組み立てるので安全だが、可読性に難がある。

```
// CASEを使う場合、別名が必要なので、Aliasを利用する。
$builder = SQL::select('categories')
    ->columns(
        'id',
        'category_name',
        // CaseStatementと別名の文字列をAliasに渡す。
        SQL::alias(
            SQL::case()
                ->whenThen(SQL::rules()->equals('id', $post['category_id']), 'selected')
                ->else(''),
            'selected' // ここが別名（AS句）になる
        )
    );

// 下記でも同じ結果になる
$builder = SQL::select('categories')
    ->columns(
        'id',
        'category_name',
        // CaseStatementと別名の文字列をセットの配列として渡す。
        [
            SQL::case()
                ->whenThen(SQL::rules()->equals('id', $post['category_id']), 'selected')
                ->else(''),
            'selected' // ここが別名（AS句）になる
        ]
    );
```

### プレースホルダを含むCASE文の文字列を渡すパターン

[](#プレースホルダを含むcase文の文字列を渡すパターン)

```
$builder = SQL::select('categories')
    ->columns(
        'id',
        'category_name',
        SQL::raw("CASE WHEN id = ? THEN 'selected' ELSE '' END AS selected")
            ->bindValue($post['category_id']) // 値をバインド
    );
```

INSERT
------

[](#insert)

### 通常のINSERT文

[](#通常のinsert文)

```
// 取引先ごとの月間予算を保持するmonthly_budgetsテーブルを想定
$query = SQL::insert('monthly_budgets')
    ->values([
        'customer_code' => '00001',
        'year' => 2022,
        'month' => 1,
        'amount' => 300000
    ]);

/*
    INSERT INTO monthly_budgets (
        customer_code, year, month, amount
    ) VALUES (
        ?, ?, ?, ?
    )
*/
```

### ON DUPLICATE KEY UPDATE

[](#on-duplicate-key-update)

ユニークキーなどの制約に引っかかることでINSERTに失敗したら、代わりにUPDATEを実行したい場合がある。
SELECTで存在チェックをしてPHP側で条件分岐を書いてもいいが、ON DUPLICATE KEY UPDATEを用いる方法もある。
これにより「あればINSERT，なければUPDATE」の動きを、ひとつのSQLで済ませることができる。

多対多の中間テーブルに対して繰り返し登録・更新をおこなうような処理で役に立つかもしれない。

```
// customer_code、year、monthの3カラムで複合ユニーク制約をかけている前提。
// 制約に引っかかったら、同じ値でのUPDATEが実行される。
$query = SQL::insert('monthly_budgets')
    ->values([
        'customer_code' => '00001',
        'year' => 2022,
        'month' => 1,
        'amount' => 300000
    ])
    ->onDuplicateKeyUpdate();

/*
    INSERT INTO monthly_budgets (
        customer_code, year, month, amount
    ) VALUES (
        ?, ?, ?, ?
    ) ON DUPLICATE KEY UPDATE (
        customer_code = ?, year = ?, month = ?, amount = ?
    )
*/
```

### BULK INSERT

[](#bulk-insert)

Insert::valuesを続けて呼び出すことで、複数のレコードを一括登録できる。

```
$query = SQL::insert('monthly_budgets')
    ->values([
        'customer_code' => '00001',
        'year' => 2022,
        'month' => 1,
        'amount' => 300000
    ])
    ->values([
        'customer_code' => '00002',
        'year' => 2022,
        'month' => 2,
        'amount' => 310000
    ]);
```

UPDATE
------

[](#update)

UPDATEの条件はSELECTと同様、whereメソッドを呼び出すことで記述する。

```
// item_codeが1000000かつ、deleted_flagがNULLではないレコードを更新
$record = [
    'item_name' => '商品3',
    'category_id' => 2
];

$query = SQL::update('items')
    ->sets($record)
    ->where(fn(Rules $rules) => $rules
        ->isNotNull('deleted_flag')
        ->equals('item_code', '1000000'));

/*
    UPDATE items
    SET
        item_name = ?,
        category_id = ?
    WHERE
        deleted_flag IS NOT NULL
        AND id = ?
*/
```

DELETE
------

[](#delete)

DELETEの条件もwhereメソッドにcallableを渡し、Rulesクラスを用いる。

```
$query = SQL::delete('approved_requests')->where(
    fn(Rules $rules) => $rules->equals('id', 1)
);

/*
    DELETE FROM approved_requests
    WHERE id = ?
*/
```

###  Health Score

25

—

LowBetter than 37% of packages

Maintenance34

Infrequent updates — may be unmaintained

Popularity4

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity47

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

639d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/e2d62cb7d653d3982799fb210ad6d95b2d49ed74bfe137a335ff3793bdf164e6?d=identicon)[HaikaraSakura](/maintainers/HaikaraSakura)

---

Top Contributors

[![HaikaraSakura](https://avatars.githubusercontent.com/u/87011031?v=4)](https://github.com/HaikaraSakura "HaikaraSakura (1 commits)")

### Embed Badge

![Health badge](/badges/haikara-sequel-builder/health.svg)

```
[![Health](https://phpackages.com/badges/haikara-sequel-builder/health.svg)](https://phpackages.com/packages/haikara-sequel-builder)
```

###  Alternatives

[doctrine/orm

Object-Relational-Mapper for PHP

10.2k285.3M6.2k](/packages/doctrine-orm)[jdorn/sql-formatter

a PHP SQL highlighting library

3.9k115.1M102](/packages/jdorn-sql-formatter)[illuminate/database

The Illuminate Database package.

2.8k52.4M9.4k](/packages/illuminate-database)[mongodb/mongodb

MongoDB driver library

1.6k64.0M546](/packages/mongodb-mongodb)[ramsey/uuid-doctrine

Use ramsey/uuid as a Doctrine field type.

90340.3M211](/packages/ramsey-uuid-doctrine)[reliese/laravel

Reliese Components for Laravel Framework code generation.

1.7k3.4M16](/packages/reliese-laravel)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
