jpi/query 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

jpi/query

Composer 安装命令:

composer require jpi/query

包简介

A simple and lightweight SQL query builder

README 文档

README

CodeFactor Latest Stable Version Total Downloads Latest Unstable Version License GitHub last commit (branch)

A simple and lightweight SQL query builder library to make querying a database easier. It works as a middleman between your application and the database.

This has been kept very simple stupid (KISS), with minimal validation (PHP type errors only) to reduce complexity in the library and maximize performance for consumer developers. Therefore, please make sure to add your own validation if using user inputs in these queries.

Features

  • Fluent, chainable query builder with simple, expressive syntax
  • Support for SELECT, INSERT, UPDATE, and DELETE queries
  • Support for columns, joins (INNER, LEFT, and RIGHT), where clauses, ordering, limiting, and paging
  • Returns convenient collections for SELECT queries

Dependencies

Installation

Use Composer

$ composer require jpi/query 

Usage

To create an instance, you will need an instance of \JPI\Database (which is an extension of PDO - you can find out more here) which is the first parameter, and the database table name as the second parameter. The same instance can be used multiple times as long as it's for the same database.

$queryBuilder = new \JPI\Database\Query\Builder($database, $table);

Action Methods:

These are the methods to call to end with select, count, insert, update & delete, all are pretty self-explanatory.

Builder methods

These are all fluent methods, so you can chain them together.

table

If you want to change to another table or didn't set when creating the instance.

table(string $table, string|null $alias): static

column

To select a particular column. Call this method multiple times to select multiple columns. This method is also used to add aggregate functions. If not called, all columns will be selected.

column(string $column, string|null $alias): static

join

join(): static

By default will be a INNER join, use rightJoin or leftJoin methods instead if you want those.

// Join with a single expression, but can add more to the 2nd parameter
$queryBuilder->join("orders", "users.id = orders.user_id");

// Nicer syntax adding multiple expressions
$queryBuilder->join(
    $queryBuilder->newJoinClause("orders")
        ->on("users.id = orders.user_id")
        ->on("orders.status = 'completed'")
);

where

Adds an expression to the WHERE clause. This method is very flexible and supports multiple calling patterns:

Note: By default, multiple where() calls on the builder are combined with AND logic. Also note parameters will be keyed by the column, so if you use the same column for 2 different values, it will use the last value added.

Raw expression: Pass a complete expression as the first parameter only

$queryBuilder->where("status = 'active'");
$queryBuilder->where("created_at > NOW()");

Column, operator, value: Pass column name, operator, and value separately (recommended for security as it uses parameterised queries)

Supported operators: =, !=, <>, <, >, <=, >=, LIKE, IN, NOT IN, BETWEEN

Note: All values (except raw SQL expressions) are automatically parameterised to prevent SQL injection.

$queryBuilder->where("status", "=", "active");
$queryBuilder->where("age", ">", 18);
$queryBuilder->where("name", "LIKE", "%john%");

// If you need to control the parameter name yourself (for example, to reuse it across multiple
// conditions), prefix the placeholder with `:` and then bind it explicitly using `param()`:
$queryBuilder->where("status", "=", ":status_value");
$queryBuilder->param("status_value", "active");

IN/NOT IN:

$queryBuilder->where("status", "IN", ["active", "pending"]);
$queryBuilder->where("id", "NOT IN", [1, 2, 3]);

Note: If there is just one value, it will automatically optimise and switch to = or <> operator.

BETWEEN operator: Pass an array with exactly 2 values for the BETWEEN operator

// age BETWEEN 18 AND 65
$queryBuilder->where("age", "BETWEEN", [18, 65]);

IS NULL / IS NOT NULL: For checking NULL values, use special two-parameter syntax

$queryBuilder->where("deleted_at", "IS NULL");
$queryBuilder->where("email", "IS NOT NULL");

Subqueries: Pass a Builder instance as the value to use a subquery

// id IN (SELECT customer_id FROM orders WHERE status = 'completed')
$subQuery = new \JPI\Database\Query\Builder($database, "orders");
$subQuery
    ->column("customer_id")
    ->where("status", "=", "completed");
$queryBuilder->where("id", "IN", $subQuery);

orderBy

orderBy(string $column, bool $ascDirection = true): static

limit

Add a limit to the query, and optionally set the page at the same time - this sets the OFFSET.

limit(int $limit, int|null $page): static

page

Used to change the offset, only used if limit set.

page(int $page): static

Complex WHERE Conditions

For more complex WHERE clauses that require OR logic or nested conditions, you can use AndCondition and OrCondition classes.

AndCondition

AndCondition groups multiple conditions together with AND logic. Create one using $queryBuilder->newAndCondition().

// (status = "active" AND age > 18)
$andCondition = $queryBuilder->newAndCondition()
    ->where("status", "=", "active")
    ->where("age", ">", 18);
OrCondition

OrCondition groups multiple conditions together with OR logic. Create one using $queryBuilder->newOrCondition().

// (status = "active" OR role = "admin")
$orCondition = $queryBuilder->newOrCondition()
    ->where("status", "=", "active")
    ->where("role", "=", "admin");
Combining AND and OR Conditions

You can nest AndCondition and OrCondition to create complex logic:

// status = 'active' AND (role = 'admin' OR type = 'premium')
$queryBuilder
    ->where("status", "=", "active")
    ->where(
        $queryBuilder->newOrCondition()
            ->where("role", "=", "admin")
            ->where("type", "=", "premium")
    );

// ((status = 'active' AND age > 18) OR type = 'premium')
$queryBuilder
    ->where(
        $queryBuilder->newOrCondition()
            ->where(
                $queryBuilder->newAndCondition()
                    ->where("status", "=", "active")
                    ->where("age", ">", 18)
            )
            ->where("type", "=", "premium")
    );

Examples

Assuming a \JPI\Database\Query\Builder instance has been created for the users database table and set to a variable named $queryBuilder.

select

This has 4 return types depending on how you use it:

  • if you've set limit of 1 this will return an instance of \JPI\Database\Query\Result\Row or null if not found. The Row class can be used as an associative array of key (column) value pairs
  • if paged/limited and the withPagination param (first param) isn't false then \JPI\Database\Query\Result\PaginatedCollection
  • else \JPI\Database\Query\Result\Collection

PaginatedCollection & Collection work like a normal array just with some extra methods, see https://github.com/jahidulpabelislam/utils?tab=readme-ov-file#collection for more details. Both of these contain multiple instances of Row. PaginatedCollection has meta data on the limit used, page number and total count if not limited, and the collection is immutable.

// SELECT * FROM users;
$collection = $queryBuilder->select();
/**
$collection = [
    [
        "id" => 1,
        "first_name" => "Jahidul",
        "last_name" => "Islam",
        "email" => "jahidul@jahidulpabelislam.com",
        "password" => "password123",
        ...
    ],
    [
        "id" => 2,
        "first_name" => "Test",
        "last_name" => "Example",
        "email" => "test@example.com",
        "password" => "password123",
        ...
    ],
    ...
];
*/

// SELECT first_name, last_name FROM users;
$collection = $queryBuilder
    ->column("first_name")
    ->column("last_name")
    ->select();
/**
$collection = [
    [
        "first_name" => "Jahidul",
        "last_name" => "Islam",
    ],
    [
        "first_name" => "Test",
        "last_name" => "Example",
    ],
    ...
];
*/

// SELECT * FROM users WHERE status = "active";
$collection = $queryBuilder
    ->where("status", "=", "active")
    ->select();
/**
$collection = [
    [
        "id" => 1,
        "first_name" => "Jahidul",
        "last_name" => "Islam",
        "email" => "jahidul@jahidulpabelislam.com",
        "password" => "password123",
        "status" => "active",
        ...
    ],
    [
        "id" => 3,
        "first_name" => "Test",
        "last_name" => "Example",
        "email" => "test@example.com",
        "password" => "password123",
        "status" => "active",
        ...
    ],
    ...
];
*/

// SELECT * FROM users WHERE status = "active" ORDER BY last_name ASC;
$collection = $queryBuilder
    ->where("status", "=", "active")
    ->orderBy("last_name")
    ->select();
/**
$collection = [
    [
        "id" => 3,
        "first_name" => "Test",
        "last_name" => "Example",
        "email" => "test@example.com",
        "password" => "password123",
        "status" => "active",
        ...
    ],
    [
        "id" => 1,
        "first_name" => "Jahidul",
        "last_name" => "Islam",
        "email" => "jahidul@jahidulpabelislam.com",
        "password" => "password123",
        "status" => "active",
        ...
    ],
    ...
];
*/

// SELECT * FROM users WHERE status = "active" ORDER BY first_name ASC LIMIT 10 OFFSET 20;
$collection = $queryBuilder
    ->where("status", "=", "active")
    ->orderBy("first_name")
    ->limit(10, 3)
    ->select();
/**
$collection = [
    [
        "id" => 31,
        "first_name" => "Jahidul",
        "last_name" => "Islam",
        "email" => "jahidul@jahidulpabelislam.com",
        "password" => "password123",
        "status" => "active",
        ...
    ],
    [
        "id" => 30,
        "first_name" => "Test",
        "last_name" => "Example",
        "email" => "test@example.com",
        "password" => "password123",
        "status" => "active",
        ...
    ],
    ...
];
*/

// SELECT * FROM users WHERE first_name LIKE "%jahidul%" LIMIT 1;
$row = $queryBuilder
    ->where("first_name", "LIKE", "%jahidul%")
    ->limit(1)
    ->select();
/**
$row = [
    "id" => 1,
    "first_name" => "Jahidul",
    "last_name" => "Islam",
    "email" => "jahidul@jahidulpabelislam.com",
    "password" => "password",
    ...
];
*/

/**
SELECT * FROM users
INNER JOIN user_logins ON user_id = login_user_user_id;
*/
$queryBuilder->join("user_logins", "user_id = login_user_user_id");
$collection = $queryBuilder->select();
/**
$collection = [
    [
        "id" => 1,
        "first_name" => "Jahidul",
        "login_user_id" => 1,
        "login_user_user_id" => 1,
        "login_user_date" => "2025-10-29 10:00:00",
        ...
    ],
    [
        "id" => 1,
        "first_name" => "Jahidul",
        "login_user_id" => 2,
        "login_user_user_id" => 1,
        "login_user_date" => "2025-11-01 12:00:00",
        ...
    ],
];

/**
SELECT * FROM users
INNER JOIN user_logins ON user_id = login_user_user_id AND login_user_date > '2025-11-01';
 */
$queryBuilder->join(
    $queryBuilder->newJoinClause("user_logins")
        ->on("user_id = login_user_user_id")
        ->on("login_user_date > '2025-11-01'")
);
$queryBuilder->select();
/**
$collection = [
    [
        "id" => 1,
        "first_name" => "Jahidul",
        "login_user_id" => 2,
        "login_user_user_id" => 1,
        "login_user_date" => "2025-11-01 12:00:00",
        ...
    ],
];

count

As the name implies this method will just return the count as an integer. By default it will do COUNT(*), but you can pass a column name or expression as the first parameter to count non-NULL values in a specific column or use expressions like DISTINCT.

For obvious reasons only the table, join & where builder methods are supported for this action.

// SELECT COUNT(*) as count FROM users;
$count = $queryBuilder->count();
// $count = 10;

// SELECT COUNT(*) as count FROM users WHERE status = "active";
$count = $queryBuilder
    ->where("status", "=", "active")
    ->count();
// $count = 5;

// SELECT COUNT(email) as count FROM users;
// Using column parameter to count non-NULL values in the email column
$count = $queryBuilder->count("email");
// $count = 10;

// SELECT COUNT(DISTINCT status) as count FROM users;
// Can use expressions in the column parameter
$count = $queryBuilder->count("DISTINCT status");
// $count = 2;

insert

This method supports inserting one or more rows into the table.

Single Row Insert: When inserting a single row successfully, the method returns the last inserted ID, or null if it fails.

Multi-Row Insert: When inserting multiple rows, the method returns the number of rows affected.

Only the table builder method is supported for this action.

// Single row insert
// INSERT INTO users (first_name, last_name, email, password) VALUES ("Jahidul", "Islam", "jahidul@jahidulpabelislam.com", "password");
$id = $queryBuilder->insert([
    "first_name" => "Jahidul",
    "last_name" => "Islam",
    "email" => "jahidul@jahidulpabelislam.com",
    "password" => "password",
]);
// $id = 3;

// Multi-row insert
// INSERT INTO users (first_name, last_name, email, password) VALUES ("Jahidul", "Islam", "jahidul@jahidulpabelislam.com", "password"), ("Test", "User", "test@example.com", "password123");
$rowCount = $queryBuilder->insert([
    [
        "first_name" => "Jahidul",
        "last_name" => "Islam",
        "email" => "jahidul@jahidulpabelislam.com",
        "password" => "password",
    ],
    [
        "first_name" => "Test",
        "last_name" => "User",
        "email" => "test@example.com",
        "password" => "password123",
    ],
]);
// $rowCount = 2 (number of rows inserted)

update

This method will return the count of how many rows have been updated by the query.

column, join & page builder methods aren't supported for this action.

// UPDATE users SET status = "inactive";
$numberOrRowsUpdated = $queryBuilder->update([
    "status" => "inactive",
]);
// $numberOrRowsUpdated = 10;

// UPDATE users SET first_name = "Pabel" WHERE id = 1;
$numberOrRowsUpdated = $queryBuilder
    ->where("id", "=", 1)
    ->update([
        "first_name" => "Pabel",
    ]);
// $numberOrRowsUpdated = 1;

delete

This method will return the count of how many rows have been deleted by the query.

column, join & page builder methods aren't supported for this action.

// DELETE FROM users;
$numberOrRowsDeleted = $queryBuilder->delete();
// $numberOrRowsDeleted = 10;

// DELETE FROM users WHERE id = 1;
$numberOrRowsDeleted = $queryBuilder
    ->where("id", "=", 1)
    ->delete();
// $numberOrRowsDeleted = 1;

Support

If you found this library interesting or useful please spread the word about this library: share on your socials, star on GitHub, etc.

If you find any issues or have any feature requests, you can open an issue or email me @ jahidulpabelislam.com 😏.

Authors

Licence

This module is licensed under the General Public Licence - see the licence file for details.

jpi/query 适用场景与选型建议

jpi/query 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 3.03k 次下载、GitHub Stars 达 0, 最近一次更新时间为 2021 年 10 月 12 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「database」 「sql」 「db」 「mysql」 「query」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 jpi/query 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 jpi/query 我们能提供哪些服务?
定制开发 / 二次开发

基于 jpi/query 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 3.03k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 15
  • 依赖项目数: 1
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 1
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: GPL-3.0-only
  • 更新时间: 2021-10-12