Skip to content

Commit 13bb95d

Browse files
committed
Merge branch 'release/0.0.2'
2 parents 197bed1 + 1841c8b commit 13bb95d

4 files changed

Lines changed: 162 additions & 78 deletions

File tree

README.md

Lines changed: 115 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,100 +1,161 @@
11
# Pixie WPDB Query Builder for WordPress
22

3-
4-
![alt text](https://img.shields.io/badge/Current_Version-0.0.1-yellow.svg?style=flat " ")
5-
[![Open Source Love](https://badges.frapsoft.com/os/mit/mit.svg?v=102)]()
3+
[![GitHub issues](https://img.shields.io/github/release/gin0115/pixie-wpdb)](https://github.com/gin0115/pixie-wpdb/releases)
64
![](https://github.com/gin0115/pixie-wpdb/workflows/GitHub_CI/badge.svg " ")
75
[![codecov](https://codecov.io/gh/gin0115/pixie-wpdb/branch/master/graph/badge.svg?token=4yEceIaSFP)](https://codecov.io/gh/gin0115/pixie-wpdb)
86
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/gin0115/pixie-wpdb/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/gin0115/pixie-wpdb/?branch=master)
7+
[![GitHub issues](https://img.shields.io/github/issues/gin0115/pixie-wpdb)](https://github.com/gin0115/pixie-wpdb/issues)
8+
[![Open Source Love](https://badges.frapsoft.com/os/mit/mit.svg?v=102)]()
99

10+
An expressive, query builder for WordPRess it can also be referred as a Database Abstraction Layer. Pixie WPDB supports WPDB ONLY and it takes care of query sanitization, table prefixing and many other things with a unified API.
11+
12+
> **Pixie WPDB** is an adaption of `pixie` originally written by [usmanhalalit](https://github.com/usmanhalalit). [Pixie](https://github.com/usmanhalalit/pixie) is no longer under active development.
13+
14+
# Features
15+
* [Fluent API](https://github.com/gin0115/pixie-wpdb/wiki/Query%20Methods)
16+
* [Nested Queries](https://github.com/gin0115/pixie-wpdb/wiki/Sub%20&%20Nested%20Queries)
17+
* [Multiple Connections](https://github.com/gin0115/pixie-wpdb/wiki/Home#setup-connection)
18+
* [Sub Queries](https://github.com/gin0115/pixie-wpdb/wiki/Sub%20&%20Nested%20Queries)
19+
* [JSON Support](https://github.com/gin0115/pixie-wpdb/wiki/Json%20Methods)
20+
* [Model Hydration](https://github.com/gin0115/pixie-wpdb/wiki/Result%20Hydration)
21+
* [Custom Alias Facade](https://github.com/gin0115/pixie-wpdb/wiki/Home#connection-alias)
22+
* [Raw SQL Expressions](https://github.com/gin0115/pixie-wpdb/wiki/Bindings%20&%20Raw%20Expressions)
23+
* [Value Type Binding](https://github.com/gin0115/pixie-wpdb/wiki/Bindings%20&%20Raw%20Expressions)
24+
* [Transaction Support](https://github.com/gin0115/pixie-wpdb/wiki/Transactions)
25+
* [Query Events](https://github.com/gin0115/pixie-wpdb/wiki/Query%20Events)
26+
27+
```php
28+
$thing = QB::table('someTable')->where('something','=', 'something else')->first();
29+
```
1030

11-
A lightweight, expressive, query builder for WordPRess it can also be referred as a Database Abstraction Layer. Pixie WPDB supports WPDB ONLY and it takes care of query sanitization, table prefixing and many other things with a unified API.
31+
# Install
1232

13-
> **Pixie WPDB** is an adaption of `pixie` originally written by [usmanhalalit](https://github.com/usmanhalalit). [Pixie is not longer under active development ](https://github.com/usmanhalalit/pixie)
33+
## Perquisites
1434

15-
## Requirements
16-
- PHP 7.1+
17-
- MySql 5.7+ or MariaDB 10.2+
35+
* WordPress 5.7+ (tested upto 5.9)
36+
* PHP 7.1+ (includes support for PHP8)
37+
* MySql 5.7+ or MariaDB 10.2+
38+
* Composer (optional)
1839

19-
> Tested all combinations of PHP 7.1, 7.2, 7.3, 7.4, 8.0, 8.1 with MySql 5.7 & MariaBD 10.2, 10.3, 10.4, 10.5, 10.6, 10.7
40+
## Using Composer
2041

42+
The easiest way to include Pixie in your project is to use [composer](http://getcomposer.org/doc/00-intro.md#installation-nix).
2143

22-
It has some advanced features like:
44+
```bash
45+
composer require gin0115/pixie-wpdb
46+
```
2347

24-
- Query Events
25-
- Nested Criteria
26-
- Sub Queries
27-
- Multiple Database Connections.
48+
## Static Loader
2849

29-
Additional features added to this version of Pixie
30-
- JSON Support (Select, Where)
31-
- Aggregation methods (Min, Max, Average & Sum)
32-
- Custom Model Hydration
33-
- Date based Where (Month, Day, Year & Date)
50+
If you are planning to just inlcude Pixie direct in your plugin, you can extract the `src` directory and add this to your `functions.php` or similar.
3451

35-
The syntax is quite similar to Laravel's query builder.
52+
```php
53+
require_once '/path/to/src/loader.php';
3654

37-
## Example
38-
```PHP
39-
// Make sure you have Composer's autoload file included
40-
require 'vendor/autoload.php';
55+
```
56+
> Each class is checked if already loaded, to avoid conflicts if used on multiple plugins.
4157
42-
// Create a connection, once only.
43-
$config = [
44-
Connection::PREFIX => 'cb_', // Table prefix, optional
45-
];
58+
# Setup Connection
59+
60+
If you are only planning on having a single connection, you will only need to configure the connection once.
4661

47-
// Get the current (gloabl) WPDB instance, or create a custom one
62+
```php
63+
# Basic setup
64+
65+
// Access the global WPDB or a custom instance for additional tables.
4866
global $wpdb;
4967

50-
// Give this instance its own custom class alias (for calling statically);
51-
$alias = 'QB';
68+
// Configure the builder and/or internal WPDB instance
69+
$connection_config = [Connection::PREFIX => 'gin0115_'];
70+
71+
// Give a *single* Alias
72+
$builder_alias = 'Gin0115\\DB';
5273

53-
new \Pixie\Connection($wpdb, $config, $alias);
74+
new Connection( $wpdb, $connection_config, $builder_alias );
5475
```
5576

56-
**Simple Query:**
77+
This would then give access to an instance of the QueryBuilder using this connection, via the alias defined `Gin0115\DB`
5778

58-
The query below returns the row where id = 3, null if no rows.
59-
```PHP
60-
$row = QB::table('my_table')->find(3);
79+
```php
80+
$foos = Gin0115\DB::table('foo')->where('column', 'red')->get();
6181
```
6282

63-
**Full Queries:**
83+
> Generated & executed query :: "SELECT * FROM gin0115_foo WHERE column = 'red'; "
6484
65-
```PHP
66-
$query = QB::table('my_table')->where('name', '=', 'Sana');
85+
## Connection Config
86+
87+
It is possible to configure the connection used by your instance of the query builder.
88+
89+
Values
6790

68-
// Get result
69-
$query->get();
91+
| Key | Constant | Value | Description |
92+
| ----------- | ----------- |----------- |----------- |
93+
| prefix | Connection:: PREFIX | STRING | Custom table prefix (will ignore WPDB prefix)|
94+
| use_wpdb_prefix | Connection:: USE_WPDB_PREFIX | BOOL | If true will use WPDB prefix and ignore custom prefix
95+
| clone_wpdb | Connection:: CLONE_WPDB | BOOL | If true, will clone WPDB to not use reference to the instance (usually the $GLOBAL)|
96+
| show_errors | Connection:: SHOW_ERRORS | BOOL | If set to true will configure WPDB to show/hide errors |
97+
98+
99+
```php
100+
$config = [
101+
Connection::PREFIX => 'acme_',
102+
Connection::USE_WPDB_PREFIX => true,
103+
Connection::CLONE_WPDB => true,
104+
Connection::SHOW_ERRORS => false,
105+
];
70106
```
71107

72-
**Query Events:**
108+
> [More details on the config](https://github.com/gin0115/pixie-wpdb/wiki#connection-config)
109+
110+
## Connection Alias
73111

74-
After the code below, every time a select query occurs on `users` table, it will add this where criteria, so banned users don't get access.
112+
When you create a connection:
75113

76114
```PHP
77-
QB::registerEvent('before-select', 'users', function($qb)
78-
{
79-
$qb->where('status', '!=', 'banned');
80-
});
115+
new Connection($wpdb, $config, 'MyAlias');
81116
```
82117

118+
`MyAlias` is the name for the class alias you want to use (like `MyAlias::table(...)` ), you can use whatever name (with Namespace also, `MyNamespace\\MyClass` ) you like or you may skip it if you don't need an alias. Alias gives you the ability to easily access the QueryBuilder class across your application.
119+
120+
# Usage
121+
122+
Once a connection is created, the builder can be accessed either directly using the Alias Facade or by creating an instance.
123+
124+
## Static Usage
125+
126+
The easiest way to use Pixie is to use the alias facade provided. This allows you to access a builder instance anywhere, much like WPDB.
83127

84-
There are many advanced options which are documented below. Sold? Let's install.
128+
```php
129+
// Create the connection early on.
130+
$connection = new Connection($wpdb, $config, 'Alias');
85131

86-
## Installation
132+
// Insert some data to bar.
133+
Alias::table('bar')->insert(['column'=>'value']);
134+
```
135+
136+
## None Static Usage
137+
138+
When not using an alias you can instantiate the QueryBuilder handler separately, helpful for Dependency Injection and Testing.
139+
140+
```PHP
141+
// Create connection and builder instance.
142+
$connection = new Connection($wpdb, $config);
143+
$qb = new QueryBuilderHandler($connection);
144+
145+
$query = $qb->table('my_table')->where('name', '=', 'Sana');
146+
$results = $query->get();
147+
```
87148

88-
Pixie uses [Composer](http://getcomposer.org/doc/00-intro.md#installation-nix) to make things easy.
149+
`$connection` here is optional, if not given it will always associate itself to the first connection, but it can be useful when you have multiple database connections.
89150

90-
To install run `composer require gin0115/pixie-wpdb`
151+
# Credits
91152

92-
Library on [Packagist](https://packagist.org/packages/gin0115/pixie-wpdb).
153+
This package began as a fork of [Pixie](https://github.com/usmanhalalit/pixie) originally written by [usmanhalalit](https://github.com/usmanhalalit)
154+
A few features have been inspired by the [Pecee-pixie](https://github.com/skipperbent/pecee-pixie/) fork and continuation, especially the extended aggregate methods.
93155

94-
## Full Usage API
95-
For the full usage docs, please see the wiki.
96156

97157
## Changelog
158+
* 0.0.2 - Improvements to the `updateOrInsert()` method
98159
* 0.0.1 - Various external and interal changes made to the initial code written by [Muhammad Usman](http://usman.it/)
99160
___
100161
If you find any typo then please edit and send a pull request.

src/QueryBuilder/QueryBuilderHandler.php

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -611,9 +611,9 @@ public function replace($data)
611611
/**
612612
* @param array<string, mixed> $data
613613
*
614-
* @return int|null
614+
* @return int|null Number of row effected, null for none.
615615
*/
616-
public function update($data)
616+
public function update(array $data): ?int
617617
{
618618
$eventResult = $this->fireEvents('before-update');
619619
if (!is_null($eventResult)) {
@@ -625,23 +625,30 @@ public function update($data)
625625
$this->dbInstance()->get_results($preparedQuery);
626626
$this->fireEvents('after-update', $queryObject, $executionTime);
627627

628-
return 0 !== $this->dbInstance()->rows_affected
629-
? $this->dbInstance()->rows_affected
628+
return 0 !== (int) $this->dbInstance()->rows_affected
629+
? (int) $this->dbInstance()->rows_affected
630630
: null;
631631
}
632632

633633
/**
634-
* @param array<string, mixed> $data
634+
* Update or Insert based on the attributes.
635+
*
636+
* @param array<string, mixed> $attributes Conditions to check
637+
* @param array<string, mixed> $values Values to add/update
635638
*
636-
* @return int|null will return row id for insert and bool for success/fail on update
639+
* @return int|int[]|null will return row id(s) for insert and null for success/fail on update
637640
*/
638-
public function updateOrInsert($data)
641+
public function updateOrInsert(array $attributes, array $values = [])
639642
{
640-
if ($this->first()) {
641-
return $this->update($data);
643+
// Check if existing post exists.
644+
$query = clone $this;
645+
foreach ($attributes as $column => $value) {
646+
$query->where($column, $value);
642647
}
643648

644-
return $this->insert($data);
649+
return null !== $query->first()
650+
? $this->update(array_merge($values, $attributes))
651+
: $this->insert(array_merge($values, $attributes));
645652
}
646653

647654
/**

tests/QueryBuilderHandler/TestIntegrationWithWPDB.php

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -407,21 +407,38 @@ public function testUpdate()
407407
/** @testdox [WPDB] It should be possible to use updateOrInsert/upsert to either add a unique dataset or update an existing one. */
408408
public function testUpsert()
409409
{
410+
$this->wpdb->insert('mock_foo', ['string' => 'first', 'number' => 12], ['%s', '%d']);
411+
410412
$builder = $this->queryBuilderProvider();
411413

412-
// INSERT
413-
$id = $builder->table('mock_foo')->updateOrInsert(['string' => 'first', 'number' => 24]);
414+
// UPDATE (was 12, now 24)
415+
$updated = $builder->table('mock_foo')->updateOrInsert(['string' => 'first' ], ['string' => 'first', 'number' => 24 ]);
416+
$this->assertNotNull($updated);
417+
$this->assertEquals(24, $builder->table('mock_foo')->find('first', 'string')->number);
418+
$this->assertCount(1, $builder->table('mock_foo')->get());
419+
// CREATE
420+
$updated = $builder->table('mock_foo')->updateOrInsert(['string' => 'second' ], ['string' => 'second', 'number' => 42 ]);
421+
$this->assertNotNull($updated);
422+
$this->assertEquals(42, $builder->table('mock_foo')->find('second', 'string')->number);
423+
424+
// Should now be 2 rows.
425+
$this->assertCount(2, $builder->table('mock_foo')->get());
426+
}
414427

415-
// Check we actually inserted the row.
416-
$this->assertTrue($id >= 1);
417-
$this->assertEquals('first', $builder->table('mock_foo')->find(24, 'number')->string);
428+
public function testUpsertWithAttributesMissingFromValues(): void
429+
{
430+
$this->wpdb->insert('mock_foo', ['string' => 'first', 'number' => 12], ['%s', '%d']);
431+
$builder = $this->queryBuilderProvider();
418432

419-
// UPDATE
420-
$updated = $builder->table('mock_foo')->updateOrInsert(['string' => 'second', 'number' => 24]);
433+
// UPDATE (was 12, now 24)
434+
$builder->table('mock_foo')->updateOrInsert(['string' => 'first' ], ['number' => 24 ]);
435+
$this->assertEquals(24, $builder->table('mock_foo')->find('first', 'string')->number);
436+
$this->assertCount(1, $builder->table('mock_foo')->get());
421437

422-
// Check we updated.
423-
$this->assertEquals(1, $updated);
424-
$this->assertEquals('second', $builder->table('mock_foo')->find(24, 'number')->string);
438+
// CREATE
439+
$builder->table('mock_foo')->updateOrInsert(['string' => 'second' ], ['number' => 42 ]);
440+
$this->assertEquals(42, $builder->table('mock_foo')->find('second', 'string')->number);
441+
$this->assertCount(2, $builder->table('mock_foo')->get());
425442
}
426443

427444
/** @testdox [WPDB] It should be possible to create a query which deletes all rows based on the criteria */
@@ -434,7 +451,7 @@ public function testDeleteWhere(): void
434451
$builder = $this->queryBuilderProvider();
435452

436453
// Remove all with a NUMBER of 2 or more.
437-
$r = $builder->table('mock_foo')->where('number', '>=', 2)->delete();
454+
$builder->table('mock_foo')->where('number', '>=', 2)->delete();
438455

439456
// Check we only have the first value.
440457
$rows = $builder->table('mock_foo')->get();
@@ -774,5 +791,4 @@ public function testOnDuplicateKeyOnPirmaryKey(): void
774791
$this->assertEquals('me@me.com', $rows[0]->email);
775792
$this->assertEquals('15', $rows[0]->counter);
776793
}
777-
778794
}

tests/Unit/TestEvents.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,11 +224,11 @@ public function testEventAfterInsert(): void
224224
public function testEventBeforeUpdateWillShortCircuitGet(): void
225225
{
226226
$events = $this->connection->getEventHandler();
227-
$events->registerEvent(Event::BEFORE_UPDATE, 'foo', $this->createClosure('This should skip the query being executed.'));
227+
$events->registerEvent(Event::BEFORE_UPDATE, 'foo', $this->createClosure(9999));
228228
$result = $this->queryBuilderProvider()->table('foo')->where('id', 1)->update(['bar' => 'baz']);
229229

230230
$this->assertEmpty($this->wpdb->usage_log);
231-
$this->assertEquals('This should skip the query being executed.', $result);
231+
$this->assertEquals(9999, $result);
232232
$this->assertContains('before-updatefoo', Objects::get_property($events, 'firedEvents'));
233233
}
234234

0 commit comments

Comments
 (0)