PHPackages                             jpi/query - 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. jpi/query

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

jpi/query
=========

A simple and lightweight SQL query builder

v2.5.1(4mo ago)03.0k↓88.9%[8 issues](https://github.com/jahidulpabelislam/query/issues)1GPL-3.0-onlyPHPPHP ^8.0CI passing

Since Oct 12Pushed 2mo ago1 watchersCompare

[ Source](https://github.com/jahidulpabelislam/query)[ Packagist](https://packagist.org/packages/jpi/query)[ RSS](/packages/jpi-query/feed)WikiDiscussions 2.x Synced 1w ago

READMEChangelog (10)Dependencies (4)Versions (31)Used By (1)

Query
=====

[](#query)

[![CodeFactor](https://camo.githubusercontent.com/cbfb31ae6798156e3f8623daf8b2dff51947b02275bd68896dd2c98725dd0ad9/68747470733a2f2f7777772e636f6465666163746f722e696f2f7265706f7369746f72792f6769746875622f6a61686964756c706162656c69736c616d2f71756572792f6261646765)](https://www.codefactor.io/repository/github/jahidulpabelislam/query)[![Latest Stable Version](https://camo.githubusercontent.com/09d81c168274bed92a0ecb387869158bb9329ac5fec8274a547f3ba040be47f6/68747470733a2f2f706f7365722e707567782e6f72672f6a70692f71756572792f762f737461626c65)](https://packagist.org/packages/jpi/query)[![Total Downloads](https://camo.githubusercontent.com/4b1bb6ddd0eec2144822d667d43cdb88118d38e1c11bf75a87859dfae16563e7/68747470733a2f2f706f7365722e707567782e6f72672f6a70692f71756572792f646f776e6c6f616473)](https://packagist.org/packages/jpi/query)[![Latest Unstable Version](https://camo.githubusercontent.com/45fb2155013e406fd741457c91b78baa7f3b1dbb537805943c48a7d08fb62ef9/68747470733a2f2f706f7365722e707567782e6f72672f6a70692f71756572792f762f756e737461626c65)](https://packagist.org/packages/jpi/query)[![License](https://camo.githubusercontent.com/73bcdb1e847c9e1e5086709270b87367e6c95e23c6acf128bef02e052f37794c/68747470733a2f2f706f7365722e707567782e6f72672f6a70692f71756572792f6c6963656e7365)](https://packagist.org/packages/jpi/query)[![GitHub last commit (branch)](https://camo.githubusercontent.com/acf9d5ff7702d3f7ab8d605af046c40e398414a51f4bf0998ed8179a6bf6591e/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6173742d636f6d6d69742f6a61686964756c706162656c69736c616d2f71756572792f322e782e7376673f6c6162656c3d6c6173742532306163746976697479)](https://camo.githubusercontent.com/acf9d5ff7702d3f7ab8d605af046c40e398414a51f4bf0998ed8179a6bf6591e/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6173742d636f6d6d69742f6a61686964756c706162656c69736c616d2f71756572792f322e782e7376673f6c6162656c3d6c6173742532306163746976697479)

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

[](#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
------------

[](#dependencies)

- PHP 8.0+
- Composer
- PHP PDO
- MySQL 5+
- [jpi/database](https://packagist.org/packages/jpi/database) v2

Installation
------------

[](#installation)

Use [Composer](https://getcomposer.org/)

```
$ composer require jpi/query
```

Usage
-----

[](#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](https://packagist.org/packages/jpi/database)) 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:

[](#action-methods)

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

### Builder methods

[](#builder-methods)

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

#### `table`

[](#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`

[](#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)

```
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`

[](#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)

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

#### `limit`

[](#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`

[](#page)

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

```
page(int $page): static
```

#### Complex WHERE Conditions

[](#complex-where-conditions)

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

##### AndCondition

[](#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)

`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

[](#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

[](#examples)

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

#### select

[](#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` &amp; `Collection` work like a normal array just with some extra methods, see  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

[](#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` &amp; `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

[](#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

[](#update)

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

`column`, `join` &amp; `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

[](#delete)

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

`column`, `join` &amp; `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
-------

[](#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](https://github.com/jahidulpabelislam/query/issues) or email [me @ jahidulpabelislam.com](mailto:me@jahidulpabelislam.com) 😏.

Authors
-------

[](#authors)

- [Jahidul Pabel Islam](https://jahidulpabelislam.com/) [](mailto:me@jahidulpabelislam.com)

Licence
-------

[](#licence)

This module is licensed under the General Public Licence - see the [licence](LICENSE.md) file for details.

###  Health Score

46

—

FairBetter than 93% of packages

Maintenance80

Actively maintained with recent releases

Popularity16

Limited adoption so far

Community11

Small or concentrated contributor base

Maturity66

Established project with proven stability

 Bus Factor1

Top contributor holds 92.3% 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

Every ~53 days

Recently: every ~12 days

Total

31

Last Release

85d ago

Major Versions

v1.0.2 → v2.0.0-beta.12023-10-05

PHP version history (3 changes)v1.0.0PHP ^7.1

v1.0.2PHP ^7.1 || ^8.0

v2.0.0-beta.1PHP ^8.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/3eb53275639d93b5fdae3b835527fdb958dbc1214e58a97d10fa50fe66a836cd?d=identicon)[jahidulpabelislam](/maintainers/jahidulpabelislam)

---

Top Contributors

[![jahidulpabelislam](https://avatars.githubusercontent.com/u/15434150?v=4)](https://github.com/jahidulpabelislam "jahidulpabelislam (156 commits)")[![Copilot](https://avatars.githubusercontent.com/in/1143301?v=4)](https://github.com/Copilot "Copilot (13 commits)")

---

Tags

databasemysqlphpquery-builderdatabasemysqlsqlquerydb

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/jpi-query/health.svg)

```
[![Health](https://phpackages.com/badges/jpi-query/health.svg)](https://phpackages.com/packages/jpi-query)
```

###  Alternatives

[aura/sqlquery

Object-oriented query builders for MySQL, Postgres, SQLite, and SQLServer; can be used with any database connection library.

4572.9M34](/packages/aura-sqlquery)[illuminated/db-profiler

Database Profiler for Laravel Web and Console Applications.

168237.4k](/packages/illuminated-db-profiler)[fpdo/fluentpdo

FluentPDO is a quick and light PHP library for rapid query building. It features a smart join builder, which automatically creates table joins.

921244.9k7](/packages/fpdo-fluentpdo)[atlas/query

Object-oriented query builders and performers for MySQL, Postgres, SQLite, and SQLServer.

41249.0k7](/packages/atlas-query)[rah/danpu

Zero-dependency MySQL dump library for easily exporting and importing databases

64401.8k10](/packages/rah-danpu)[opis/database

A database abstraction layer over PDO, that provides a powerful and intuitive query builder, bundled with an easy to use schema builder

10184.2k3](/packages/opis-database)

PHPackages © 2026

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