PHPackages                             stellarwp/db - 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. stellarwp/db

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

stellarwp/db
============

A WPDB wrapper and query builder library.

1.3.0(4mo ago)721.1M↑117.3%4[9 issues](https://github.com/stellarwp/db/issues)[2 PRs](https://github.com/stellarwp/db/pulls)3GPL-2.0PHPCI passing

Since Aug 28Pushed 4mo ago7 watchersCompare

[ Source](https://github.com/stellarwp/db)[ Packagist](https://packagist.org/packages/stellarwp/db)[ RSS](/packages/stellarwp-db/feed)WikiDiscussions main Synced 2d ago

READMEChangelog (10)Dependencies (26)Versions (15)Used By (3)

StellarWP DB
============

[](#stellarwp-db)

[![Tests](https://github.com/stellarwp/db/workflows/Tests/badge.svg)](https://github.com/stellarwp/db/actions?query=branch%3Amain) [![Static Analysis](https://github.com/stellarwp/db/actions/workflows/static-analysis.yml/badge.svg)](https://github.com/stellarwp/db/actions/workflows/static-analysis.yml)

A WPDB wrapper and query builder library. Authored by the development team at StellarWP and provided free for the WordPress community.

*Inspired and largely forked from the [GiveWP](https://github.com/impress-org) codebase!*

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

[](#installation)

It's recommended that you install DB as a project dependency via [Composer](https://getcomposer.org/):

```
composer require stellarwp/db
```

> We *actually* recommend that this library gets included in your project using [Strauss](https://github.com/BrianHenryIE/strauss).
>
> Luckily, adding Strauss to your `composer.json` is only slightly more complicated than adding a typical dependency, so checkout our [strauss docs](https://github.com/stellarwp/global-docs/blob/main/docs/strauss-setup.md).

Table of contents
-----------------

[](#table-of-contents)

- [Quick start](#quick-start)
- [Configuration](#configuration)
- [DB](#db)
- [Select statements](#select-statements)
- [From Clause](#from-clause)
- [Joins](#joins)

    - [LEFT Join](#left-join)
    - [RIGHT Join](#right-join)
    - [INNER Join](#inner-join)
    - [Join Raw](#join-raw)
    - [Advanced Join Clauses](#advanced-join-clauses)
- [Unions](#unions)
- [Where Clauses](#where-clauses)

    - [Where](#where-clauses)
    - [Where IN](#where-in-clauses)
    - [Where BETWEEN](#where-between-clauses)
    - [Where LIKE](#where-like-clauses)
    - [Where IS NULL](#where-is-null-clauses)
    - [Where EXISTS](#where-exists-clauses)
    - [Subquery Where Clauses](#subquery-where-clauses)
    - [Nested Where Clauses](#nested-where-clauses)
- [Ordering, Grouping, Limit &amp; Offset](#ordering-grouping-limit--offset)

    - [Ordering](#ordering)
    - [Grouping](#grouping)
    - [Limit &amp; Offset](#limit--offset)
- [Special methods for working with meta tables](#special-methods-for-working-with-meta-tables)

    - [attachMeta](#attachmeta)
    - [configureMetaTable](#configuremetatable)
- [CRUD](#crud)

    - [Insert](#insert)
    - [Update](#update)
    - [Upsert](#upsert)
    - [Delete](#delete)
    - [Get](#get)
- [Inherited from `$wpdb`](#inherited-from-wpdb)

    - [`get_var()`](#get_var)
    - [`get_col()`](#get_col)
    - [`esc_like()`](#esc_like)
    - [`remove_placeholder_escape()`](#remove_placeholder_escape)
- [Aggregate Functions](#aggregate-functions)

    - [Count](#count)
    - [Sum](#sum)
    - [Avg](#avg)
    - [Min](#min)
    - [Max](#max)

Quick start
-----------

[](#quick-start)

Getting up and running with this library is easy. You'll want to initialize the `DB` class. Doing so during the `plugins_loaded` action is a reasonable location, though you can do it anywhere that feels appropriate.

*For this example and all future ones, let's assume you have [included this library with Strauss](https://github.com/stellarwp/global-docs/blob/main/docs/strauss-setup.md) and your project's namespace is `Boom\Shakalaka`.*

```
use Boom\Shakalaka\StellarWP\DB\DB;

add_action( 'plugins_loaded', function() {
	DB::init();
}, 0 );
```

The two main classes that make up the core of this library are the `DB` class and the `QueryBuilder` class. Here are their namespaces:

```
# For DB, it is "StellarWP\DB\DB", but with your namespace prefix it'll be:
use Boom\Shakalaka\StellarWP\DB\DB;

# For QueryBuilder, it is "StellarWP\DB\QueryBuilder\QueryBuilder", but with your namespace prefix it'll be:
use Boom\Shakalaka\StellarWP\DB\QueryBuilder\QueryBuilder;
```

Configuration
-------------

[](#configuration)

This library provides default hooks and exceptions, however, if you have additional needs for your own application, you can override one or both via the `StellarWP\DB\Config` class:

```
use Boom\Shakalaka\StellarWP\DB\Config;

// Ensure hooks are prefixed with your project's prefix.
Config::setHookPrefix( 'boom_shakalaka' );

// Use your own exception class rather than the default Database\Exceptions\DatabaseQueryException class.
Config::setDatabaseQueryException( 'MyCustomException' );

// Fetch the hook prefix.
$prefix = Config::getHookPrefix();

// Fetch the database query exception class.
$class = Config::getDatabaseQueryException();
```

DB
--

[](#db)

`DB` class is a static decorator for the `$wpdb` class, but it has a few methods that are exceptions to that. Methods `DB::table()` and `DB::raw()`.

`DB::table()` is a static facade for the `QueryBuilder` class, and it accepts two string arguments, `$tableName`and `$tableAlias`.

Under the hood, `DB::table()` will create a new `QueryBuilder` instance, and it will use `QueryBuilder::from` method to set the table name. Calling `QueryBuilder::from` when using `DB::table` method will return an unexpected result. Basically, we are telling the `QueryBuilder` that we want to select data from two tables.

### Important

[](#important)

When using `DB::table(tableName)` method, the `tableName` is prefixed with `$wpdb->prefix`. To bypass that, you can use `DB::raw` method which will tell `QueryBuilder` not to prefix the table name.

```
DB::table(DB::raw('posts'));
```

Select statements
-----------------

[](#select-statements)

#### Available methods - select / selectRaw / distinct

[](#available-methods---select--selectraw--distinct)

By using the `QueryBuilder::select` method, you can specify a custom `SELECT` statement for the query.

```
DB::table('posts')->select('ID', 'post_title', 'post_date');
```

Generated SQL

```
SELECT ID, post_title, post_date FROM wp_posts
```

You can also specify the column alias by providing an array *\[column, alias\]* to the `QueryBuilder::select` method.

```
DB::table('posts')->select(
    ['ID', 'post_id'],
    ['post_status', 'status'],
    ['post_date', 'createdAt']
);
```

Generated SQL:

```
SELECT ID AS post_id, post_status AS status, post_date AS createdAt FROM wp_posts
```

The distinct method allows you to force the query to return distinct results:

```
DB::table('posts')->select('post_status')->distinct();
```

You can also specify a custom `SELECT` statement with `QueryBuilder::selectRaw` method. This method accepts an optional array of bindings as its second argument.

```
DB::table('posts')
    ->select('ID')
    ->selectRaw('(SELECT ID from wp_posts WHERE post_status = %s) AS subscriptionId', 'give_subscription');
```

Generated SQL

```
SELECT ID, (SELECT ID from wp_posts WHERE post_status = 'give_subscription') AS subscriptionId FROM wp_posts
```

By default, all columns will be selected from a database table.

```
DB::table('posts');
```

Generated SQL

```
SELECT * FROM wp_posts
```

From clause
-----------

[](#from-clause)

By using the `QueryBuilder::from()` method, you can specify a custom `FROM` clause for the query.

```
$builder = new QueryBuilder();
$builder->from('posts');
```

Set multiple `FROM` clauses

```
$builder = new QueryBuilder();
$builder->from('posts');
$builder->from('postmeta');
```

Generated SQL

```
SELECT * FROM wp_posts, wp_postmeta
```

### Important

[](#important-1)

Table name is prefixed with `$wpdb->prefix`. To bypass that, you can use `DB::raw` method which will tell `QueryBuilder` not to prefix the table name.

```
$builder = new QueryBuilder();
$builder->from(DB::raw('posts'));
```

Joins
-----

[](#joins)

The Query Builder may also be used to add `JOIN` clauses to your queries.

#### Available methods - leftJoin / rightJoin / innerJoin / joinRaw / join

[](#available-methods---leftjoin--rightjoin--innerjoin--joinraw--join)

### LEFT Join

[](#left-join)

`LEFT JOIN` clause.

```
DB::table('posts', 'donationsTable')
    ->select('donationsTable.*', 'metaTable.*')
    ->leftJoin('give_donationmeta', 'donationsTable.ID', 'metaTable.donation_id', 'metaTable');
```

Generated SQL

```
SELECT donationsTable.*, metaTable.* FROM wp_posts AS donationsTable LEFT JOIN wp_give_donationmeta metaTable ON donationsTable.ID = metaTable.donation_id
```

### RIGHT Join

[](#right-join)

`RIGHT JOIN` clause.

```
DB::table('posts', 'donationsTable')
    ->select('donationsTable.*', 'metaTable.*')
    ->rightJoin('give_donationmeta', 'donationsTable.ID', 'metaTable.donation_id', 'metaTable');
```

Generated SQL

```
SELECT donationsTable.*, metaTable.* FROM wp_posts AS donationsTable RIGHT JOIN wp_give_donationmeta metaTable ON donationsTable.ID = metaTable.donation_id
```

### INNER Join

[](#inner-join)

`INNER JOIN` clause.

```
DB::table('posts', 'donationsTable')
    ->select('donationsTable.*', 'metaTable.*')
    ->innerJoin('give_donationmeta', 'donationsTable.ID', 'metaTable.donation_id', 'metaTable');
```

Generated SQL

```
SELECT donationsTable.*, metaTable.* FROM wp_posts AS donationsTable INNER JOIN wp_give_donationmeta metaTable ON donationsTable.ID = metaTable.donation_id
```

### Join Raw

[](#join-raw)

Insert a raw expression into query.

```
DB::table('posts', 'donationsTable')
    ->select('donationsTable.*', 'metaTable.*')
    ->joinRaw('LEFT JOIN give_donationmeta metaTable ON donationsTable.ID = metaTable.donation_id');
```

Generated SQL

```
SELECT donationsTable.*, metaTable.* FROM wp_posts AS donationsTable LEFT JOIN give_donationmeta metaTable ON donationsTable.ID = metaTable.donation_id
```

### Advanced Join Clauses

[](#advanced-join-clauses)

**The closure will receive a `Give\Framework\QueryBuilder\JoinQueryBuilder` instance**

```
DB::table('posts')
    ->select('donationsTable.*', 'metaTable.*')
    ->join(function (JoinQueryBuilder $builder) {
        $builder
            ->leftJoin('give_donationmeta', 'metaTable')
            ->on('donationsTable.ID', 'metaTable.donation_id')
            ->andOn('metaTable.meta_key', 'some_key', $qoute = true);
    });
```

Generated SQL

```
SELECT donationsTable.*, metaTable.* FROM wp_posts LEFT JOIN wp_give_donationmeta metaTable ON donationsTable.ID = metaTable.donation_id AND metaTable.meta_key = 'some_key'
```

Unions
------

[](#unions)

The Query Builder also provides a convenient method to "union" two or more queries together.

#### Available methods - union / unionAll

[](#available-methods---union--unionall)

### Union

[](#union)

```
$donations = DB::table('give_donations')->where('author_id', 10);

DB::table('give_subscriptions')
    ->select('ID')
    ->where('ID', 100, '>')
    ->union($donations);
```

Generated SQL:

```
SELECT ID FROM wp_give_subscriptions WHERE ID > '100' UNION SELECT * FROM wp_give_donations WHERE author_id = '10'
```

Where Clauses
-------------

[](#where-clauses)

You may use the Query Builder's `where` method to add `WHERE` clauses to the query.

### Where

[](#where)

#### Available methods - where / orWhere

[](#available-methods---where--orwhere)

```
DB::table('posts')->where('ID', 5);
```

Generated SQL

```
SELECT * FROM wp_posts WHERE ID = '5'
```

Using `where` multiple times.

```
DB::table('posts')
    ->where('ID', 5)
    ->where('post_author', 10);
```

Generated SQL

```
SELECT * FROM wp_posts WHERE ID = '5' AND post_author = '10'
```

### Where IN Clauses

[](#where-in-clauses)

#### Available methods - whereIn / orWhereIn / whereNotIn / orWhereNotIn

[](#available-methods---wherein--orwherein--wherenotin--orwherenotin)

The `QueryBuilder::whereIn` method verifies that a given column's value is contained within the given array:

```
DB::table('posts')->whereIn('ID', [1, 2, 3]);
```

Generated SQL

```
SELECT * FROM wp_posts WHERE ID IN ('1','2','3')
```

You can also pass a closure as the second argument which will generate a subquery.

**The closure will receive a `Give\Framework\QueryBuilder\QueryBuilder` instance**

```
DB::table('posts')
    ->whereIn('ID', function (QueryBuilder $builder) {
        $builder
            ->select(['meta_value', 'donation_id'])
            ->from('give_donationmeta')
            ->where('meta_key', 'donation_id');
    });
```

Generated SQL

```
SELECT * FROM wp_posts WHERE ID IN (SELECT meta_value AS donation_id FROM wp_give_donationmeta WHERE meta_key = 'donation_id')
```

### Where BETWEEN Clauses

[](#where-between-clauses)

The `QueryBuilder::whereBetween` method verifies that a column's value is between two values:

#### Available methods - whereBetween / orWhereBetween / whereNotBetween / orWhereNotBetween

[](#available-methods---wherebetween--orwherebetween--wherenotbetween--orwherenotbetween)

```
DB::table('posts')->whereBetween('ID', 0, 100);
```

Generated SQL

```
SELECT * FROM wp_posts WHERE ID BETWEEN '0' AND '100'
```

### Where LIKE Clauses

[](#where-like-clauses)

The `QueryBuilder::whereLike` method searches for a specified pattern in a column.

#### Available methods - whereLike / orWhereLike / whereNotLike / orWhereNotLike

[](#available-methods---wherelike--orwherelike--wherenotlike--orwherenotlike)

```
DB::table('posts')->whereLike('post_title', 'Donation');
```

Generated SQL

```
SELECT * FROM wp_posts WHERE post_title LIKE '%Donation%'
```

### Where IS NULL Clauses

[](#where-is-null-clauses)

The `QueryBuilder::whereIsNull` method verifies that a column's value is `NULL`

#### Available methods - whereIsNull / orWhereIsNull / whereIsNotNull / orWhereIsNotNull

[](#available-methods---whereisnull--orwhereisnull--whereisnotnull--orwhereisnotnull)

```
DB::table('posts')->whereIsNull('post_author');
```

Generated SQL

```
SELECT * FROM wp_posts WHERE post_author IS NULL
```

### Where EXISTS Clauses

[](#where-exists-clauses)

The `QueryBuilder::whereExists` method allows you to write `WHERE EXISTS` SQL clauses. The `QueryBuilder::whereExists` method accepts a closure which will receive a `QueryBuilder` instance.

#### Available methods - whereExists / whereNotExists

[](#available-methods---whereexists--wherenotexists)

```
DB::table('give_donationmeta')
    ->whereExists(function (QueryBuilder $builder) {
        $builder
            ->select(['meta_value', 'donation_id'])
            ->where('meta_key', 'donation_id');
    });
```

Generated SQL

```
SELECT * FROM wp_give_donationmeta WHERE EXISTS (SELECT meta_value AS donation_id WHERE meta_key = 'donation_id')
```

### Subquery Where Clauses

[](#subquery-where-clauses)

Sometimes you may need to construct a `WHERE` clause that compares the results of a subquery to a given value.

```
DB::table('posts')
    ->where('post_author', function (QueryBuilder $builder) {
        $builder
            ->select(['meta_value', 'author_id'])
            ->from('postmeta')
            ->where('meta_key', 'donation_id')
            ->where('meta_value', 10);
    });
```

Generated SQL

```
SELECT * FROM wp_posts WHERE post_author = (SELECT meta_value AS author_id FROM wp_postmeta WHERE meta_key = 'donation_id' AND meta_value = '10')
```

### Nested Where Clauses

[](#nested-where-clauses)

Sometimes you may need to construct a `WHERE` clause that has nested WHERE clauses.

**The closure will receive a `Give\Framework\QueryBuilder\WhereQueryBuilder` instance**

```
DB::table('posts')
    ->where('post_author', 10)
    ->where(function (WhereQueryBuilder $builder) {
        $builder
            ->where('post_status', 'published')
            ->orWhere('post_status', 'donation')
            ->whereIn('ID', [1, 2, 3]);
    });
```

Generated SQL

```
SELECT * FROM wp_posts WHERE post_author = '10' AND ( post_status = 'published' OR post_status = 'donation' AND ID IN ('1','2','3'))
```

Ordering, Grouping, Limit &amp; Offset
--------------------------------------

[](#ordering-grouping-limit--offset)

### Ordering

[](#ordering)

The `QueryBuilder::orderBy` method allows you to sort the results of the query by a given column.

```
DB::table('posts')->orderBy('ID');
```

Generated SQL

```
SELECT * FROM wp_posts ORDER BY ID ASC
```

Sorting result by multiple columns

```
DB::table('posts')
    ->orderBy('ID')
    ->orderBy('post_date', 'DESC');
```

Generated SQL

```
SELECT * FROM wp_posts ORDER BY ID ASC, post_date DESC
```

### Grouping

[](#grouping)

The `QueryBuilder::groupBy` and `QueryBuilder::having*` methods are used to group the query results.

#### Available methods - groupBy / having / orHaving / havingCount / orHavingCount / havingMin / orHavingMin / havingMax / orHavingMax / havingAvg / orHavingAvg / havingSum / orHavingSum / havingRaw

[](#available-methods---groupby--having--orhaving--havingcount--orhavingcount--havingmin--orhavingmin--havingmax--orhavingmax--havingavg--orhavingavg--havingsum--orhavingsum--havingraw)

```
DB::table('posts')
    ->groupBy('id')
    ->having('id', '>', 10);
```

Generated SQL

```
SELECT * FROM wp_posts WHERE GROUP BY id HAVING 'id' > '10'
```

### Limit &amp; Offset

[](#limit--offset)

Limit the number of results returned from the query.

#### Available methods - limit / offset

[](#available-methods---limit--offset)

```
DB::table('posts')
    ->limit(10)
    ->offset(20);
```

Generated SQL

```
SELECT * FROM wp_posts LIMIT 10 OFFSET 20
```

Special methods for working with meta tables
--------------------------------------------

[](#special-methods-for-working-with-meta-tables)

Query Builder has a few special methods for abstracting the work with meta tables.

### attachMeta

[](#attachmeta)

`attachMeta` is used to include meta table *meta\_key* column values as columns in the `SELECT` statement.

Under the hood `QueryBuilder::attachMeta` will add join clause for each defined `meta_key` column. And each column will be added in select statement as well, which means the meta columns will be returned in query result. Aliasing meta columns is recommended when using `QueryBuilder::attachMeta` method.

```
DB::table('posts')
    ->select(
        ['ID', 'id'],
        ['post_date', 'createdAt'],
        ['post_modified', 'updatedAt'],
        ['post_status', 'status'],
        ['post_parent', 'parentId']
    )
    ->attachMeta('give_donationmeta', 'ID', 'donation_id',
        ['_give_payment_total', 'amount'],
        ['_give_payment_currency', 'paymentCurrency'],
        ['_give_payment_gateway', 'paymentGateway'],
        ['_give_payment_donor_id', 'donorId'],
        ['_give_donor_billing_first_name', 'firstName'],
        ['_give_donor_billing_last_name', 'lastName'],
        ['_give_payment_donor_email', 'donorEmail'],
        ['subscription_id', 'subscriptionId']
    )
    ->leftJoin('give_donationmeta', 'ID', 'donationMeta.donation_id', 'donationMeta')
    ->where('post_type', 'give_payment')
    ->where('post_status', 'give_subscription')
    ->where('donationMeta.meta_key', 'subscription_id')
    ->where('donationMeta.meta_value', 1)
    ->orderBy('post_date', 'DESC');
```

Generated SQL:

```
SELECT ID                                         AS id,
       post_date                                  AS createdAt,
       post_modified                              AS updatedAt,
       post_status                                AS status,
       post_parent                                AS parentId,
       give_donationmeta_attach_meta_0.meta_value AS amount,
       give_donationmeta_attach_meta_1.meta_value AS paymentCurrency,
       give_donationmeta_attach_meta_2.meta_value AS paymentGateway,
       give_donationmeta_attach_meta_3.meta_value AS donorId,
       give_donationmeta_attach_meta_4.meta_value AS firstName,
       give_donationmeta_attach_meta_5.meta_value AS lastName,
       give_donationmeta_attach_meta_6.meta_value AS donorEmail,
       give_donationmeta_attach_meta_7.meta_value AS subscriptionId
FROM wp_posts
         LEFT JOIN wp_give_donationmeta give_donationmeta_attach_meta_0
                   ON ID = give_donationmeta_attach_meta_0.donation_id AND
                      give_donationmeta_attach_meta_0.meta_key = '_give_payment_total'
         LEFT JOIN wp_give_donationmeta give_donationmeta_attach_meta_1
                   ON ID = give_donationmeta_attach_meta_1.donation_id AND
                      give_donationmeta_attach_meta_1.meta_key = '_give_payment_currency'
         LEFT JOIN wp_give_donationmeta give_donationmeta_attach_meta_2
                   ON ID = give_donationmeta_attach_meta_2.donation_id AND
                      give_donationmeta_attach_meta_2.meta_key = '_give_payment_gateway'
         LEFT JOIN wp_give_donationmeta give_donationmeta_attach_meta_3
                   ON ID = give_donationmeta_attach_meta_3.donation_id AND
                      give_donationmeta_attach_meta_3.meta_key = '_give_payment_donor_id'
         LEFT JOIN wp_give_donationmeta give_donationmeta_attach_meta_4
                   ON ID = give_donationmeta_attach_meta_4.donation_id AND
                      give_donationmeta_attach_meta_4.meta_key = '_give_donor_billing_first_name'
         LEFT JOIN wp_give_donationmeta give_donationmeta_attach_meta_5
                   ON ID = give_donationmeta_attach_meta_5.donation_id AND
                      give_donationmeta_attach_meta_5.meta_key = '_give_donor_billing_last_name'
         LEFT JOIN wp_give_donationmeta give_donationmeta_attach_meta_6
                   ON ID = give_donationmeta_attach_meta_6.donation_id AND
                      give_donationmeta_attach_meta_6.meta_key = '_give_payment_donor_email'
         LEFT JOIN wp_give_donationmeta give_donationmeta_attach_meta_7
                   ON ID = give_donationmeta_attach_meta_7.donation_id AND
                      give_donationmeta_attach_meta_7.meta_key = 'subscription_id'
         LEFT JOIN wp_give_donationmeta donationMeta ON ID = donationMeta.donation_id
WHERE post_type = 'give_payment'
  AND post_status = 'give_subscription'
  AND donationMeta.meta_key = 'subscription_id'
  AND donationMeta.meta_value = '1'
ORDER BY post_date DESC
```

Returned result:

```
stdClass Object
(
    [id] => 93
    [createdAt] => 2022-02-21 00:00:00
    [updatedAt] => 2022-01-21 11:08:09
    [status] => give_subscription
    [parentId] => 92
    [amount] => 100.000000
    [paymentCurrency] => USD
    [paymentGateway] => manual
    [donorId] => 1
    [firstName] => Ante
    [lastName] => Laca
    [donorEmail] => dev-email@flywheel.local
    [subscriptionId] => 1
)

```

#### Fetch multiple instances of the same meta key

[](#fetch-multiple-instances-of-the-same-meta-key)

Sometimes we need to fetch multiple instances of the same meta key. This is possible by setting the third parameter to `true`, example `['additional_email', 'additionalEmails', true]`

```
DB::table('give_donors')
  ->select(
      'id',
      'email',
      'name'
  )
  ->attachMeta(
      'give_donormeta',
      'id',
      'donor_id',
  	  ['additional_email', 'additionalEmails', true]
  );
```

Generated SQL:

```
SELECT id, email, name, GROUP_CONCAT(DISTINCT give_donormeta_attach_meta_0.meta_value) AS additionalEmails
FROM wp_give_donors
    LEFT JOIN wp_give_donormeta give_donormeta_attach_meta_0 ON id = give_donormeta_attach_meta_0.donor_id AND give_donormeta_attach_meta_0.meta_key = 'additional_email'
GROUP BY id
```

Returned result:

Instances with the same key, in this case `additional_email`, will be concatenated into JSON array string.

```
Array
(
    [0] => stdClass Object
        (
            [id] => 1
            [email] => bill@flywheel.local
            [name] => Bill Murray
            [additionalEmails] => ["email1@lywheel.local","email2@lywheel.local"]
        )

    [1] => stdClass Object
        (
            [id] => 2
            [email] => jon@flywheel.local
            [name] => Jon Waldstein
            [additionalEmails] => ["email3@lywheel.local","email4@lywheel.local","email5@lywheel.local"]
        )

    [2] => stdClass Object
        (
            [id] => 3
            [email] => ante@flywheel.local
            [name] => Ante laca
            [additionalEmails] =>
        )

)
```

### configureMetaTable

[](#configuremetatable)

By default, `QueryBuilder::attachMeta` will use `meta_key`, and `meta_value` as meta table column names, but that sometimes might not be the case.

With `QueryBuilder::configureMetaTable` you can define a custom `meta_key` and `meta_value` column names.

```
DB::table('posts')
    ->select(
        ['ID', 'id'],
        ['post_date', 'createdAt']
    )
    ->configureMetaTable(
        'give_donationmeta',
        'custom_meta_key',
        'custom_meta_value'
    )
    ->attachMeta(
        'give_donationmeta',
        'ID',
        'donation_id',
        ['_give_payment_total', 'amount']
    )
    ->leftJoin('give_donationmeta', 'ID', 'donationMeta.donation_id', 'donationMeta')
    ->where('post_type', 'give_payment')
    ->where('post_status', 'give_subscription')
    ->where('donationMeta.custom_meta_key', 'subscription_id')
    ->where('donationMeta.custom_meta_value', 1);
```

Generated SQL

```
SELECT ID AS id, post_date AS createdAt, give_donationmeta_attach_meta_0.custom_meta_value AS amount
FROM wp_posts
         LEFT JOIN wp_give_donationmeta give_donationmeta_attach_meta_0
                   ON ID = give_donationmeta_attach_meta_0.donation_id AND
                      give_donationmeta_attach_meta_0.custom_meta_key = '_give_payment_total'
         LEFT JOIN wp_give_donationmeta donationMeta ON ID = donationMeta.donation_id
WHERE post_type = 'give_payment'
  AND post_status = 'give_subscription'
  AND donationMeta.custom_meta_key = 'subscription_id'
  AND donationMeta.custom_meta_value = '1'
```

CRUD
----

[](#crud)

### Insert

[](#insert)

The QueryBuilder also provides `QueryBuilder::insert` method that may be used to insert records into the database table.

```
DB::table('posts')
    ->insert([
        'post_title'   => 'Post Title',
        'post_author'  => 1,
        'post_content' => 'Post Content'
    ]);
```

### Update

[](#update)

In addition to inserting records into the database, the QueryBuilder can also update existing records using the `QueryBuilder::update` method.

```
DB::table('posts')
    ->where('post_author', 1)
    ->update([
        'post_title'   => 'Post Title 2',
        'post_content' => 'Post Content 2'
    ]);
```

### Upsert

[](#upsert)

The `QueryBuilder::upsert` method may be used to update an existing record or create a new record if it doesn't exist.

```
// Would result in a new row - Oakland to San Diego for 100.
DB::table('table_name')
    ->upsert(
        ['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => '100'] ,
        ['departure','destination']
    );

// Would update the row that was just inserted - Oakland to San Diego for 99.
DB::table('table_name')
    ->upsert(
        ['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => '99'] ,
        ['departure','destination']
    );
```

### Delete

[](#delete)

The `QueryBuilder::delete` method may be used to delete records from the table.

Unlike WordPress's `$wpdb->delete()` method, this implementation generates and executes a DELETE SQL statement directly, which allows for advanced features like ORDER BY and LIMIT.

#### Basic delete with WHERE

[](#basic-delete-with-where)

```
DB::table('posts')
    ->where('post_author', 1)
    ->delete();
```

#### Delete with LIMIT

[](#delete-with-limit)

Limit the number of rows to delete:

```
// Delete only the first 10 draft posts
DB::table('posts')
    ->where('post_status', 'draft')
    ->limit(10)
    ->delete();
```

#### Delete with ORDER BY and LIMIT

[](#delete-with-order-by-and-limit)

Control which rows are deleted when using LIMIT:

```
// Delete the 100 oldest posts in trash
DB::table('posts')
    ->where('post_status', 'trash')
    ->orderBy('post_date', 'ASC')
    ->limit(100)
    ->delete();
```

#### Delete with LIKE patterns

[](#delete-with-like-patterns)

Use pattern matching to delete rows:

```
// Delete all posts with titles starting with "Draft:"
DB::table('posts')
    ->whereLike('post_title', 'Draft:%')
    ->delete();
```

#### Delete with complex WHERE conditions

[](#delete-with-complex-where-conditions)

Combine multiple WHERE clauses for precise deletion:

```
// Delete auto-draft pages with IDs between 1 and 1000
DB::table('posts')
    ->where('post_type', 'page')
    ->where('post_status', 'auto-draft')
    ->whereBetween('ID', 1, 1000)
    ->delete();

// Delete posts using whereIn
DB::table('posts')
    ->whereIn('ID', [5, 10, 15, 20])
    ->delete();
```

**Important restrictions:**

- Table aliases in the FROM clause may not be supported on older database versions (MySQL &lt; 8.0.24, MariaDB &lt; 11.6). Avoid using table aliases when calling `delete()`.
- JOINs are not supported in DELETE statements with this implementation.

### Get

[](#get)

#### Available methods - get / getAll

[](#available-methods---get--getall)

Get single row

```
$post = DB::table('posts')->where('post_author', 1)->get();
```

Get all rows

```
$posts = DB::table('posts')->where('post_status', 'published')->getAll();
```

Inherited from `$wpdb`
----------------------

[](#inherited-from-wpdb)

As this is a wrapper for `$wpdb`, you are able to call all of the methods that `$wpdb` exposes as well. You simply will need to match the signature of the `$wpdb` methods when doing so.

While all methods are supported, `get_var()`, `get_col()`, `esc_like()`, and `remove_placeholder_escape()` are likely of the most interest as there are not equilavents within the library itself.

### `get_var()`

[](#get_var)

Gets the single `meta_value` column for the given query.

```
$meta_value = DB::get_var(
	DB::table( 'postmeta' )
		->select( 'meta_value' )
		->where( 'post_id', 123 )
		->where( 'meta_key', 'some_key' )
		->getSQL()
);
```

### `get_col()`

[](#get_col)

Returns an array of values for the column for the given query.

```
$meta_values = DB::get_col(
	DB::table( 'postmeta' )
		->select( 'meta_value' )
		->where( 'meta_key', 'some_key' )
		->getSQL()
);
```

### `esc_like()`

[](#esc_like)

Escapes a string with a percent sign in it so it can be safely used with [Where LIKE](#where-like-clauses) without the percent sign being interpreted as a wildcard character.

```
$escaped_string = DB::esc_like( 'This string has a % in it that is not a wildcard character' );

$results = DB::table( 'posts' )
    ->whereLike( 'post_content', "%{$escaped_string}%" )
    ->getAll();
```

### `remove_placeholder_escape()`

[](#remove_placeholder_escape)

Removes the placeholder escape strings from a SQL query.

`$wpdb` generates placeholders such as `{abb19424319f69be9475708db0d2cbb780cb2dc2375bcb2657c701709ff71a9f}` that it escapes `%` with when generating a SQL query. This library, as a `$wpdb` wrapper, does that as well.

Using `DB::remove_placeholder_escape()` will swap those back out for `%`, which can be useful if you ever need to display the query in a more human-friendly format.

```
$escaped_sql = DB::table( 'postmeta' )
	->whereLike( 'meta_key', '%search string%' )
	->getSql();

$sql = DB::remove_placeholder_escape( $escaped_sql );
```

Aggregate Functions
-------------------

[](#aggregate-functions)

The Query Builder also provides a variety of methods for retrieving aggregate values like `count`, `sum`, `avg`, `min` and `max`.

### Count

[](#count)

```
$count = DB::table('posts')
    ->where('post_type', 'published')
    ->count();
```

Count rows where provided column is not null.

```
$count = DB::table('donations')->count('not_null_value_column');
```

### Sum

[](#sum)

```
$sum = DB::table('give_donationmeta')
    ->where('meta_key', 'donation_amount')
    ->sum('meta_value');
```

### Avg

[](#avg)

```
$avg = DB::table('give_donationmeta')
    ->where('meta_key', 'donation_amount')
    ->avg('meta_value');
```

### Min

[](#min)

```
$min = DB::table('give_donationmeta')
    ->where('meta_key', 'donation_amount')
    ->min('meta_value');
```

### Max

[](#max)

```
$max = DB::table('give_donationmeta')
    ->where('meta_key', 'donation_amount')
    ->max('meta_value');
```

Acknowledgements
----------------

[](#acknowledgements)

Props to the [GiveWP](https://github.com/impress-org) team for creating this library!

###  Health Score

55

—

FairBetter than 97% of packages

Maintenance74

Regular maintenance activity

Popularity52

Moderate usage in the ecosystem

Community27

Small or concentrated contributor base

Maturity55

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 63.5% 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 ~116 days

Recently: every ~214 days

Total

12

Last Release

128d ago

### Community

Maintainers

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

![](https://www.gravatar.com/avatar/70a2847a265444714b48c64eceb3ca742baa3a56757ce65b18bd7bbbbf910312?d=identicon)[dpanta94](/maintainers/dpanta94)

![](https://www.gravatar.com/avatar/97fd764aa710e8d8263a7e3b3fececdfd736b8aad8055227bf592ddf50ad15ba?d=identicon)[stellarwp](/maintainers/stellarwp)

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

---

Top Contributors

[![borkweb](https://avatars.githubusercontent.com/u/430385?v=4)](https://github.com/borkweb "borkweb (54 commits)")[![binarygary](https://avatars.githubusercontent.com/u/16279215?v=4)](https://github.com/binarygary "binarygary (10 commits)")[![defunctl](https://avatars.githubusercontent.com/u/1066195?v=4)](https://github.com/defunctl "defunctl (9 commits)")[![Camwyn](https://avatars.githubusercontent.com/u/929375?v=4)](https://github.com/Camwyn "Camwyn (6 commits)")[![d4mation](https://avatars.githubusercontent.com/u/7770631?v=4)](https://github.com/d4mation "d4mation (2 commits)")[![lucatume](https://avatars.githubusercontent.com/u/2749650?v=4)](https://github.com/lucatume "lucatume (2 commits)")[![nikolaystrikhar](https://avatars.githubusercontent.com/u/4025589?v=4)](https://github.com/nikolaystrikhar "nikolaystrikhar (1 commits)")[![shvlv](https://avatars.githubusercontent.com/u/3157352?v=4)](https://github.com/shvlv "shvlv (1 commits)")

---

Tags

wordpress-librarywordpress-php-library

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/stellarwp-db/health.svg)

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

###  Alternatives

[jdorn/sql-formatter

a PHP SQL highlighting library

3.9k117.2M118](/packages/jdorn-sql-formatter)[propel/propel1

Propel is an open-source Object-Relational Mapping (ORM) for PHP5.

8351.6M87](/packages/propel-propel1)[pgvector/pgvector

pgvector support for PHP

198741.5k12](/packages/pgvector-pgvector)[jfelder/oracledb

Oracle DB driver for Laravel

11518.4k](/packages/jfelder-oracledb)

PHPackages © 2026

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