├── .gitignore ├── LICENSE ├── composer.json ├── readme.md └── src ├── Collection.php ├── Interfaces ├── Arrayable.php ├── JSONable.php └── Stringable.php └── Query.php /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /vendor 3 | 4 | # Generated 5 | /tests/.tmp 6 | composer.lock -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Sultan Nasir Uddin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sultann/wp-query-builder", 3 | "description": "WP Query Builder is a lightweight and efficient SQL Query Builder based on wpdb for WordPress. It supports complicated query generation.", 4 | "license": "MIT", 5 | "type": "library", 6 | "keywords": [ 7 | "wordpress", 8 | "query builder", 9 | "database" 10 | ], 11 | "authors": [ 12 | { 13 | "name": "MD Sultan Nasir Uddin", 14 | "email": "manikdrmc@gmail.com" 15 | } 16 | ], 17 | "require": { 18 | "php": ">=5.4" 19 | }, 20 | "autoload": { 21 | "psr-4": { 22 | "PluginEver\\QueryBuilder\\": "src" 23 | } 24 | }, 25 | "minimum-stability": "dev" 26 | } 27 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

6 | 7 | # WP Query Builder 8 | WP Query Builder is a lightweight and efficient SQL Query Builder based on wpdb for WordPress. It supports complicated query generation. 9 | 10 | [![Build Status](https://travis-ci.org/sultann/wp-query-builder.svg?branch=master)](https://travis-ci.org/sultann/wp-query-builder) 11 | [![Packagist](https://img.shields.io/packagist/dt/sultann/wp-query-builder.svg)](https://packagist.org/packages/sultann/wp-query-builder) 12 | [![Packagist](https://img.shields.io/packagist/l/sultann/wp-query-builder.svg)](https://github.com/sultann/wp-query-builder/blob/master/LICENSE) 13 | [![GitHub release](https://img.shields.io/github/release/sultann/wp-query-builder.svg)](https://github.com/sultann/wp-query-builder/releases) 14 | 15 | ## Installation 16 | WP Query Builder follows `PSR-4` autoloading and can be installed using composer: 17 | 18 | ``` 19 | $ composer require sultann/wp-query-builder 20 | ``` 21 | 22 | ## Documentation 💡 23 | ### Initialize 24 | Initialize query builder. init method takes a string argument using that later you can do action/filter based on your requirement. 25 | without argument. 26 | ```php 27 | $query = \PluginEver\QueryBuilder\Query::init(); 28 | ``` 29 | with argument 30 | ```php 31 | $query = \PluginEver\QueryBuilder\Query::init('query_users'); 32 | ``` 33 | 34 | ### Select 35 | This will build the query, execute and returns all users from users table with applying table prefix automatically. 36 | by default it select all(*) but you can define what to select from the query; If you are selecting all then you can omit the select statement. 37 | ```php 38 | $results = $query->select('*') 39 | ->from('users') 40 | ->get(); 41 | ``` 42 | Select specific column 43 | ```php 44 | $results = $query->select('ID') 45 | ->from('users') 46 | ->get(); 47 | ``` 48 | Select multiple column 49 | ```php 50 | $results = $query->select('user_login, user_email') 51 | ->from('users') 52 | ->get(); 53 | ``` 54 | 55 | 56 | ### Where conditions 57 | For the next few examples, lets assume a larger dataset so that the queries make sense. 58 | ```php 59 | $results = $query->select('*') 60 | ->from('users') 61 | ->where('user_email', 'like', '%gmail.com') 62 | ->get(); 63 | ``` 64 | Notice how omitting the operator in the first condition ->where('user_url', '') makes this default to =. 65 | By default all where conditions are defined with the and operator. 66 | 67 | Different where operators: 68 | 69 | ```php 70 | $results = $query->select('*') 71 | ->from('users') 72 | ->where('user_email', 'like', '%gmail.com') 73 | ->orWhere('user_email', 'like', '%yahoo.com') 74 | ->get(); 75 | ``` 76 | 77 | There are few more builtin Where conditions available 78 | - `andWhere()` 79 | - `whereIn()` 80 | - `whereNotIn()` 81 | - `whereNull()` 82 | - `whereNotNull()` 83 | - `orWhereNull()` 84 | - `orWhereNotNull()` 85 | - `whereBetween()` 86 | - `whereNotBetween()` 87 | - `whereDateBetween()` 88 | - `whereRaw()` 89 | 90 | #### Where scopes 91 | Allow you to group conditions: 92 | 93 | ```php 94 | $results = $query->select('*') 95 | ->from('posts') 96 | ->where('post_status', 'publish') 97 | ->where(function($q) 98 | { 99 | $q->where('menu_order', '>', 21); 100 | $q->where('menu_order', '<', 99); 101 | }) 102 | ->orWhere('post_type', 'page') 103 | ->get(); 104 | ``` 105 | 106 | Where Between 107 | ```php 108 | $results = $query->select('*') 109 | ->from('posts') 110 | ->whereBetween('menu_order', 1, 20) 111 | ->get(); 112 | ``` 113 | 114 | Where Not Between 115 | ```php 116 | $results = $query->select('*') 117 | ->from('posts') 118 | ->whereNotBetween('menu_order', 20, 30) 119 | ->get(); 120 | ``` 121 | 122 | Where Date Between 123 | ```php 124 | $results = $query->select('*') 125 | ->from('posts') 126 | ->whereDateBetween('post_date', '2010-04-22 10:16:21', '2020-05-04') 127 | ->get(); 128 | ``` 129 | 130 | ### Joins 131 | By default, all joins are Left Join. Available join types 'LEFT', 'RIGHT', 'INNER', 'CROSS', 'LEFT OUTER', 'RIGHT OUTER' 132 | Joining tables: 133 | ```php 134 | $results = $query->select( '*' ) 135 | ->from( 'posts p' ) 136 | ->join( 'users u', 'u.ID', '=','p.post_author' ) 137 | ->get(); 138 | ``` 139 | 140 | #### Joins scopes 141 | Allow you to group conditions: 142 | ```php 143 | $results = $query->select( '*' ) 144 | ->from( 'posts p' ) 145 | ->join( 'users u', 'u.ID', '=','p.post_author' ) 146 | ->join('usermeta um', function($q) { 147 | $q->where('um.meta_key', 'first_name'); 148 | $q->where('um.met_value', 'like', '%sultan%'); 149 | }) 150 | ->get(); 151 | ``` 152 | There are few more builtin join conditions available 153 | - `leftJoin()` 154 | - `rightJoin()` 155 | - `innerJoin()` 156 | - `outerJoin()` 157 | 158 | ### Grouping 159 | 160 | Grouping data: 161 | ```php 162 | $results = $query->select('*') 163 | ->from('posts') 164 | ->group_by('post_status') 165 | ->get(); 166 | ``` 167 | 168 | ### Having 169 | Group by with having data: 170 | ```php 171 | $results = $query->select('*') 172 | ->from('posts') 173 | ->group_by('post_status') 174 | ->having('count(ID)>1') 175 | ->get(); 176 | ``` 177 | 178 | ### Ordering 179 | Ordering data: 180 | ```php 181 | $results = $query->select('*') 182 | ->from('posts') 183 | ->order_by('post_title', 'DESC') 184 | ->get(); 185 | ``` 186 | 187 | ### Limiting data 188 | Limit and offset: 189 | ```php 190 | $results = $query->select('*') 191 | ->from('posts') 192 | ->limit(20, 10) 193 | ->get(); 194 | ``` 195 | Only limit 196 | ```php 197 | $results = $query->select('*') 198 | ->from('posts') 199 | ->limit(20) 200 | ->get(); 201 | ``` 202 | Offset as separate 203 | ```php 204 | $results = $query->select('*') 205 | ->from('posts') 206 | ->limit(20) 207 | ->offset(10) 208 | ->get(); 209 | ``` 210 | 211 | ### Pagination 212 | shortcut of limit and offset 213 | ```php 214 | $results = $query->select('*') 215 | ->from('posts') 216 | ->page(1, 20)//page number & page size 217 | ->get(); 218 | ``` 219 | 220 | ### Find 221 | find item with column value 222 | ```php 223 | $results = $query->select('*') 224 | ->from('posts') 225 | ->find(1, 'ID'); 226 | ``` 227 | 228 | 229 | ### First 230 | Get first item from the posts table 231 | ```php 232 | $results = $query->select('*') 233 | ->from('posts') 234 | ->first(); 235 | ``` 236 | 237 | ### Last 238 | Get last item from the posts table 239 | ```php 240 | $results = $query->select('*') 241 | ->from('posts') 242 | ->last(); 243 | ``` 244 | 245 | ### Counting 246 | count total rows 247 | ```php 248 | $results = $query->select('*') 249 | ->from('posts') 250 | ->count(); 251 | ``` 252 | 253 | ### toSql 254 | Out the query instead of executing 255 | ```php 256 | $results = $query->from('posts as p') 257 | ->join('users as u', 'p.post_author', 'u.ID') 258 | ->join('usermeta um', function($q) { 259 | $q->where('um.meta_key', 'first_name'); 260 | $q->where('um.met_value', 'like', '%sultan%'); 261 | }) 262 | ->toSql(); 263 | ``` 264 | 265 | ### Update 266 | Update a row 267 | ```php 268 | $results = $query->table('posts') 269 | ->where('ID', 20) 270 | ->update(['post_title' => 'updated']); 271 | ``` 272 | 273 | ### Delete 274 | Delete a row 275 | ```php 276 | $results = $query->from('posts') 277 | ->where('ID', 20) 278 | ->delete(); 279 | ``` 280 | 281 | ### Search 282 | Search a value from columns 283 | ```php 284 | $results = $query->from('posts') 285 | ->search('Hello Word', array('post_title', 'post_content')) // it will search Hello & World both 286 | ->delete(); 287 | ``` 288 | 289 | ## License 290 | 291 | The MIT License (MIT). Please see [License File](https://github.com/sultann/wp-query-builder/blob/master/LICENSE) for more information. -------------------------------------------------------------------------------- /src/Collection.php: -------------------------------------------------------------------------------- 1 | getArrayableItems( $items ); 29 | 30 | $this->items = (array) $items; 31 | } 32 | 33 | /** 34 | * Create a new collection instance if the value isn't one already. 35 | * 36 | * @param mixed $items 37 | * 38 | * @return static 39 | */ 40 | public static function make( $items = null ) { 41 | return new static( $items ); 42 | } 43 | 44 | /** 45 | * Get all of the items in the collection. 46 | * 47 | * @return array 48 | */ 49 | public function all() { 50 | return $this->items; 51 | } 52 | 53 | 54 | /** 55 | * Diff the collection with the given items. 56 | * 57 | * @param Arrayable|array $items 58 | * 59 | * @return static 60 | */ 61 | public function diff( $items ) { 62 | return new static( array_diff( $this->items, $this->getArrayableItems( $items ) ) ); 63 | } 64 | 65 | /** 66 | * Execute a callback over each item. 67 | * 68 | * @param callable $callback 69 | * 70 | * @return static 71 | */ 72 | public function each( callable $callback ) { 73 | return new static( array_map( $callback, $this->items ) ); 74 | } 75 | 76 | /** 77 | * Run a filter over each of the items. 78 | * 79 | * @param callable $callback 80 | * 81 | * @return static 82 | */ 83 | public function filter( callable $callback ) { 84 | return new static( array_filter( $this->items, $callback ) ); 85 | } 86 | 87 | /** 88 | * Filter items by the given key value pair. 89 | * 90 | * @param string $key 91 | * @param mixed $value 92 | * @param bool $strict 93 | * 94 | * @return static 95 | */ 96 | public function where( $key, $value, $strict = true ) { 97 | return $this->filter( function ( $item ) use ( $key, $value, $strict ) { 98 | return $strict ? self::data_get( $item, $key ) === $value 99 | : self::data_get( $item, $key ) == $value; 100 | } ); 101 | } 102 | 103 | /** 104 | * Filter items by the given key value pair using loose comparison. 105 | * 106 | * @param string $key 107 | * @param mixed $value 108 | * 109 | * @return static 110 | */ 111 | public function whereLoose( $key, $value ) { 112 | return $this->where( $key, $value, false ); 113 | } 114 | 115 | /** 116 | * Flip the items in the collection. 117 | * 118 | * @return static 119 | */ 120 | public function flip() { 121 | return new static( array_flip( $this->items ) ); 122 | } 123 | 124 | /** 125 | * Remove an item from the collection by key. 126 | * 127 | * @param mixed $key 128 | * 129 | * @return void 130 | */ 131 | public function forget( $key ) { 132 | $this->offsetUnset( $key ); 133 | } 134 | 135 | /** 136 | * Get an item from the collection by key. 137 | * 138 | * @param mixed $key 139 | * @param mixed $default 140 | * 141 | * @return mixed 142 | */ 143 | public function get( $key, $default = null ) { 144 | if ( $this->offsetExists( $key ) ) { 145 | return $this->items[ $key ]; 146 | } 147 | 148 | return $default instanceof \Closure ? $default() : $default; 149 | } 150 | 151 | /** 152 | * Determine if an item exists in the collection by key. 153 | * 154 | * @param mixed $key 155 | * 156 | * @return bool 157 | */ 158 | public function has( $key ) { 159 | return $this->offsetExists( $key ); 160 | } 161 | 162 | /** 163 | * Intersect the collection with the given items. 164 | * 165 | * @param Arrayable|array $items 166 | * 167 | * @return static 168 | */ 169 | public function intersect( $items ) { 170 | return new static( array_intersect( $this->items, $this->getArrayableItems( $items ) ) ); 171 | } 172 | 173 | /** 174 | * Determine if the collection is empty or not. 175 | * 176 | * @return bool 177 | */ 178 | public function isEmpty() { 179 | return empty( $this->items ); 180 | } 181 | 182 | /** 183 | * Determine if the given value is callable, but not a string. 184 | * 185 | * @param mixed $value 186 | * 187 | * @return bool 188 | */ 189 | protected function useAsCallable( $value ) { 190 | return ! is_string( $value ) && is_callable( $value ); 191 | } 192 | 193 | /** 194 | * Get the keys of the collection items. 195 | * 196 | * @return static 197 | */ 198 | public function keys() { 199 | return new static( array_keys( $this->items ) ); 200 | } 201 | 202 | /** 203 | * Get the last item from the collection. 204 | * 205 | * @return mixed|null 206 | */ 207 | public function last() { 208 | return count( $this->items ) > 0 ? end( $this->items ) : null; 209 | } 210 | 211 | /** 212 | * Run a map over each of the items. 213 | * 214 | * @param callable $callback 215 | * 216 | * @return static 217 | */ 218 | public function map( callable $callback ) { 219 | return new static( array_map( $callback, $this->items, array_keys( $this->items ) ) ); 220 | } 221 | 222 | /** 223 | * Merge the collection with the given items. 224 | * 225 | * @param Arrayable|array $items 226 | * 227 | * @return static 228 | */ 229 | public function merge( $items ) { 230 | return new static( array_merge( $this->items, $this->getArrayableItems( $items ) ) ); 231 | } 232 | 233 | /** 234 | * Get and remove the last item from the collection. 235 | * 236 | * @return mixed|null 237 | */ 238 | public function pop() { 239 | return array_pop( $this->items ); 240 | } 241 | 242 | /** 243 | * Push an item onto the beginning of the collection. 244 | * 245 | * @param mixed $value 246 | * 247 | * @return void 248 | */ 249 | public function prepend( $value ) { 250 | array_unshift( $this->items, $value ); 251 | } 252 | 253 | /** 254 | * Push an item onto the end of the collection. 255 | * 256 | * @param mixed $value 257 | * 258 | * @return void 259 | */ 260 | public function push( $value ) { 261 | $this->offsetSet( null, $value ); 262 | } 263 | 264 | /** 265 | * Put an item in the collection by key. 266 | * 267 | * @param mixed $key 268 | * @param mixed $value 269 | * 270 | * @return void 271 | */ 272 | public function put( $key, $value ) { 273 | $this->offsetSet( $key, $value ); 274 | } 275 | 276 | /** 277 | * Get one or more items randomly from the collection. 278 | * 279 | * @param int $amount 280 | * 281 | * @return mixed 282 | */ 283 | public function random( $amount = 1 ) { 284 | if ( $this->isEmpty() ) { 285 | return; 286 | } 287 | 288 | $keys = array_rand( $this->items, $amount ); 289 | 290 | return is_array( $keys ) ? array_intersect_key( $this->items, array_flip( $keys ) ) : $this->items[ $keys ]; 291 | } 292 | 293 | /** 294 | * Reduce the collection to a single value. 295 | * 296 | * @param callable $callback 297 | * @param mixed $initial 298 | * 299 | * @return mixed 300 | */ 301 | public function reduce( callable $callback, $initial = null ) { 302 | return array_reduce( $this->items, $callback, $initial ); 303 | } 304 | 305 | /** 306 | * Create a collection of all elements that do not pass a given truth test. 307 | * 308 | * @param callable|mixed $callback 309 | * 310 | * @return static 311 | */ 312 | public function reject( $callback ) { 313 | if ( $this->useAsCallable( $callback ) ) { 314 | return $this->filter( function ( $item ) use ( $callback ) { 315 | return ! $callback( $item ); 316 | } ); 317 | } 318 | 319 | return $this->filter( function ( $item ) use ( $callback ) { 320 | return $item != $callback; 321 | } ); 322 | } 323 | 324 | /** 325 | * Reverse items order. 326 | * 327 | * @return static 328 | */ 329 | public function reverse() { 330 | return new static( array_reverse( $this->items ) ); 331 | } 332 | 333 | /** 334 | * Search the collection for a given value and return the corresponding key if successful. 335 | * 336 | * @param mixed $value 337 | * @param bool $strict 338 | * 339 | * @return mixed 340 | */ 341 | public function search( $value, $strict = false ) { 342 | if ( ! $this->useAsCallable( $value ) ) { 343 | return array_search( $value, $this->items, $strict ); 344 | } 345 | 346 | foreach ( $this->items as $key => $item ) { 347 | if ( $value( $item, $key ) ) { 348 | return $key; 349 | } 350 | } 351 | 352 | return false; 353 | } 354 | 355 | /** 356 | * Get and remove the first item from the collection. 357 | * 358 | * @return mixed|null 359 | */ 360 | public function shift() { 361 | return array_shift( $this->items ); 362 | } 363 | 364 | /** 365 | * Shuffle the items in the collection. 366 | * 367 | * @return $this 368 | */ 369 | public function shuffle() { 370 | shuffle( $this->items ); 371 | 372 | return $this; 373 | } 374 | 375 | /** 376 | * Slice the underlying collection array. 377 | * 378 | * @param int $offset 379 | * @param int $length 380 | * @param bool $preserveKeys 381 | * 382 | * @return static 383 | */ 384 | public function slice( $offset, $length = null, $preserveKeys = false ) { 385 | return new static( array_slice( $this->items, $offset, $length, $preserveKeys ) ); 386 | } 387 | 388 | /** 389 | * Chunk the underlying collection array. 390 | * 391 | * @param int $size 392 | * @param bool $preserveKeys 393 | * 394 | * @return static 395 | */ 396 | public function chunk( $size, $preserveKeys = false ) { 397 | $chunks = []; 398 | 399 | foreach ( array_chunk( $this->items, $size, $preserveKeys ) as $chunk ) { 400 | $chunks[] = new static( $chunk ); 401 | } 402 | 403 | return new static( $chunks ); 404 | } 405 | 406 | /** 407 | * Sort through each item with a callback. 408 | * 409 | * @param callable $callback 410 | * 411 | * @return $this 412 | */ 413 | public function sort( callable $callback ) { 414 | uasort( $this->items, $callback ); 415 | 416 | return $this; 417 | } 418 | 419 | /** 420 | * Splice portion of the underlying collection array. 421 | * 422 | * @param int $offset 423 | * @param int $length 424 | * @param mixed $replacement 425 | * 426 | * @return static 427 | */ 428 | public function splice( $offset, $length = 0, $replacement = [] ) { 429 | return new static( array_splice( $this->items, $offset, $length, $replacement ) ); 430 | } 431 | 432 | /** 433 | * Take the first or last {$limit} items. 434 | * 435 | * @param int $limit 436 | * 437 | * @return static 438 | */ 439 | public function take( $limit = null ) { 440 | if ( $limit < 0 ) { 441 | return $this->slice( $limit, abs( $limit ) ); 442 | } 443 | 444 | return $this->slice( 0, $limit ); 445 | } 446 | 447 | /** 448 | * Transform each item in the collection using a callback. 449 | * 450 | * @param callable $callback 451 | * 452 | * @return $this 453 | */ 454 | public function transform( callable $callback ) { 455 | $this->items = array_map( $callback, $this->items ); 456 | 457 | return $this; 458 | } 459 | 460 | /** 461 | * Return only unique items from the collection array. 462 | * 463 | * @return static 464 | */ 465 | public function unique() { 466 | return new static( array_unique( $this->items ) ); 467 | } 468 | 469 | /** 470 | * Reset the keys on the underlying array. 471 | * 472 | * @return static 473 | */ 474 | public function values() { 475 | return new static( array_values( $this->items ) ); 476 | } 477 | 478 | /** 479 | * Count the number of items in the collection. 480 | * 481 | * @return int 482 | */ 483 | public function count() { 484 | return count( $this->items ); 485 | } 486 | 487 | /** 488 | * Determine if an item exists at an offset. 489 | * 490 | * @param mixed $key 491 | * 492 | * @return bool 493 | */ 494 | public function offsetExists( $key ) { 495 | return array_key_exists( $key, $this->items ); 496 | } 497 | 498 | /** 499 | * Get an item at a given offset. 500 | * 501 | * @param mixed $key 502 | * 503 | * @return mixed 504 | */ 505 | public function offsetGet( $key ) { 506 | return $this->items[ $key ]; 507 | } 508 | 509 | /** 510 | * Set the item at a given offset. 511 | * 512 | * @param mixed $key 513 | * @param mixed $value 514 | * 515 | * @return void 516 | */ 517 | public function offsetSet( $key, $value ) { 518 | if ( is_null( $key ) ) { 519 | $this->items[] = $value; 520 | } else { 521 | $this->items[ $key ] = $value; 522 | } 523 | } 524 | 525 | /** 526 | * Unset the item at a given offset. 527 | * 528 | * @param string $key 529 | * 530 | * @return void 531 | */ 532 | public function offsetUnset( $key ) { 533 | unset( $this->items[ $key ] ); 534 | } 535 | 536 | 537 | /** 538 | * Get an iterator for the items. 539 | * 540 | * @return \ArrayIterator 541 | */ 542 | public function getIterator() { 543 | return new \ArrayIterator( $this->items ); 544 | } 545 | 546 | /** 547 | * Returns collection as pure array. 548 | * Does depth array casting. 549 | * @return array 550 | * @since 1.0.2 551 | * 552 | */ 553 | public function __toArray() { 554 | $output = []; 555 | $value = null; 556 | foreach ( $this as $key => $value ) { 557 | $output[ $key ] = ! is_object( $value ) 558 | ? $value 559 | : ( method_exists( $value, '__toArray' ) 560 | ? $value->__toArray() 561 | : (array) $value 562 | ); 563 | } 564 | 565 | return $output; 566 | } 567 | 568 | /** 569 | * Returns collection as pure array. 570 | * Does depth array casting. 571 | * @return array 572 | * @since 1.0.2 573 | * 574 | */ 575 | public function toArray() { 576 | return $this->__toArray(); 577 | } 578 | 579 | /** 580 | * Returns collection as a string. 581 | * 582 | * @param string 583 | * 584 | * @since 1.0.2 585 | * 586 | */ 587 | public function __toString() { 588 | return json_encode( $this->__toArray() ); 589 | } 590 | 591 | /** 592 | * Returns object as JSON string. 593 | * 594 | * @param int $options JSON encoding options. See @link. 595 | * @param int $depth JSON encoding depth. See @link. 596 | * 597 | * @return string 598 | * @link http://php.net/manual/en/function.json-encode.php 599 | * 600 | * @since 1.0.2 601 | * 602 | */ 603 | public function __toJSON( $options = 0, $depth = 512 ) { 604 | return json_encode( $this->__toArray(), $options, $depth ); 605 | } 606 | 607 | /** 608 | * Returns object as JSON string. 609 | * 610 | * @param int $options JSON encoding options. See @link. 611 | * @param int $depth JSON encoding depth. See @link. 612 | * 613 | * @return string 614 | * @link http://php.net/manual/en/function.json-encode.php 615 | * 616 | * @since 1.0.2 617 | * 618 | */ 619 | public function toJSON( $options = 0, $depth = 512 ) { 620 | return $this->__toJSON( $options, $depth ); 621 | } 622 | 623 | /** 624 | * @param $target 625 | * @param $key 626 | * @param null $default 627 | * 628 | * @return array 629 | * @since 630 | */ 631 | public static function data_get( $target, $key, $default = null ) { 632 | if ( is_null( $key ) ) { 633 | return $target; 634 | } 635 | 636 | $key = is_array( $key ) ? $key : explode( '.', $key ); 637 | 638 | foreach ( $key as $i => $segment ) { 639 | unset( $key[ $i ] ); 640 | 641 | if ( is_null( $segment ) ) { 642 | return $target; 643 | } 644 | 645 | if ( $segment === '*' ) { 646 | if ( $target instanceof Collection ) { 647 | $target = $target->all(); 648 | } elseif ( ! is_array( $target ) ) { 649 | return $default instanceof \Closure ? $default() : $default; 650 | } 651 | $result = []; 652 | 653 | foreach ( $target as $item ) { 654 | $result[] = self::data_get( $item, $key ); 655 | } 656 | 657 | return in_array( '*', $key ) ? $result : $result; 658 | } 659 | 660 | if ( array_key_exists( $segment, $target ) ) { 661 | $target = $target[ $segment ]; 662 | } elseif ( is_object( $target ) && isset( $target->{$segment} ) ) { 663 | $target = $target->{$segment}; 664 | } else { 665 | return $default instanceof \Closure ? $default() : $default; 666 | } 667 | } 668 | 669 | return $target; 670 | } 671 | 672 | /** 673 | * Results array of items from Collection or Arrayable. 674 | * 675 | * @param $items 676 | * 677 | * @return mixed 678 | * @since 1.0.0 679 | */ 680 | protected function getArrayableItems( $items ) { 681 | if ( $items instanceof Collection ) { 682 | $items = $items->all(); 683 | } elseif ( $items instanceof Arrayable ) { 684 | $items = $items->toArray(); 685 | } 686 | 687 | return $items; 688 | } 689 | } -------------------------------------------------------------------------------- /src/Interfaces/Arrayable.php: -------------------------------------------------------------------------------- 1 | id = ! empty( $id ) ? $id : uniqid(); 68 | 69 | return $builder; 70 | } 71 | 72 | /** 73 | * Adds select statement. 74 | * 75 | * @param $statement 76 | * 77 | * @return $this 78 | * @since 1.0.0 79 | */ 80 | public function select( $statement ) { 81 | $this->select[] = $statement; 82 | 83 | return $this; 84 | } 85 | 86 | /** 87 | * Adds from statement. 88 | * 89 | * @param string $name 90 | * @param bool $add_prefix 91 | * 92 | * @since 1.0.0 93 | * 94 | */ 95 | public function table( $name, $add_prefix = true ) { 96 | global $wpdb; 97 | $table = ( $add_prefix ? $wpdb->prefix : '' ) . $name; 98 | $this->from = $table; 99 | 100 | return $this; 101 | } 102 | 103 | /** 104 | * Adds from statement. 105 | * 106 | * @param string $from 107 | * @param bool $add_prefix Should DB prefix be added. 108 | * 109 | * @return Query this for chaining. 110 | * @global object $wpdb 111 | * 112 | * @since 1.0.0 113 | * 114 | */ 115 | public function from( $from, $add_prefix = true ) { 116 | global $wpdb; 117 | $this->from = $this->from . ' ' . ( $add_prefix ? $wpdb->prefix : '' ) . $from; 118 | 119 | return $this; 120 | } 121 | 122 | /** 123 | * Adds search statement. 124 | * 125 | * @param $search 126 | * @param $columns 127 | * @param $joint 128 | * 129 | * @since 1.0.0 130 | */ 131 | public function search( $search, $columns, $joint = 'AND' ) { 132 | if ( ! empty( $search ) ) { 133 | global $wpdb; 134 | foreach ( explode( ' ', $search ) as $word ) { 135 | $word = '%' . $this->sanitize_value( true, $word ) . '%'; 136 | $this->where[] = [ 137 | 'joint' => $joint, 138 | 'condition' => '(' . implode( ' OR ', array_map( function ( $column ) use ( &$wpdb, &$word ) { 139 | return $wpdb->prepare( $column . ' LIKE %s', $word ); 140 | }, $columns ) ) . ')', 141 | ]; 142 | } 143 | } 144 | 145 | return $this; 146 | } 147 | 148 | /** 149 | * Create a where statement. 150 | * 151 | * ->where('name', 'sultan') 152 | * ->where('age', '>', 18) 153 | * ->where('name', 'in', array('ayaan', 'ayaash', 'anaan')) 154 | * ->where(function($q){ 155 | * $q->where('ID', '>', 21); 156 | * }) 157 | * 158 | * @param string $column The SQL column 159 | * @param mixed $param1 Operator or value depending if $param2 isset. 160 | * @param mixed $param2 The value if $param1 is an operator. 161 | * @param string $joint the where type ( and, or ) 162 | * 163 | * @return Query The current query builder. 164 | */ 165 | public function where( $column, $param1 = null, $param2 = null, $joint = 'and' ) { 166 | global $wpdb; 167 | if ( ! in_array( strtolower( $joint ), [ 'and', 'or', 'where' ] ) ) { 168 | $this->exception( 'Invalid where type "' . $joint . '"' ); 169 | } 170 | 171 | // when column is an array we assume to make a bulk and where. 172 | if ( is_array( $column ) ) { 173 | // create new query object 174 | $subquery = new Query(); 175 | foreach ( $column as $key => $val ) { 176 | $subquery->where( $key, $val, null, $joint ); 177 | } 178 | 179 | $this->where = array_merge( $this->where, $subquery->where ); 180 | 181 | return $this; 182 | } 183 | 184 | if ( is_object( $column ) && ( $column instanceof \Closure ) ) { 185 | // create new query object 186 | $subquery = new Query(); 187 | 188 | // run the closure callback on the sub query 189 | call_user_func_array( $column, array( &$subquery ) ); 190 | $condition = ''; 191 | for ( $i = 0; $i < count( $subquery->where ); ++ $i ) { 192 | $condition .= ( $i === 0 ? ' ' : ' ' . $subquery->where[ $i ]['joint'] . ' ' ) 193 | . $subquery->where[ $i ]['condition']; 194 | } 195 | 196 | $this->where = array_merge( $this->where, array( 197 | array( 198 | 'joint' => $joint, 199 | 'condition' => "($condition)" 200 | ) 201 | ) ); 202 | 203 | return $this; 204 | } 205 | 206 | // when param2 is null we replace param2 with param one as the 207 | // value holder and make param1 to the = operator. 208 | // However, if param1 is either 'is' or 'is not' we need to leave param2 as null. 209 | // This is used for the whereNull() and whereNotNull() methods. 210 | if ( is_null( $param2 ) && ! in_array( $param1, [ 'is', 'is not' ], true ) ) { 211 | $param2 = $param1; 212 | $param1 = '='; 213 | } 214 | 215 | // if the param2 is an array we filter it. when param2 is an array we probably 216 | // have an "in" or "between" statement which has no need for duplicates. 217 | if ( is_array( $param2 ) ) { 218 | $param2 = array_unique( $param2 ); 219 | } 220 | 221 | // Between? 222 | if ( is_array( $param2 ) && strpos( $param1, 'BETWEEN' ) !== false ) { 223 | $min = isset( $param2[0] ) ? $param2[0] : false; 224 | $max = isset( $param2[1] ) ? $param2[1] : false; 225 | if ( ! $min || ! $max ) { 226 | $this->exception( "BETWEEN min or max is missing" ); 227 | } 228 | 229 | $min = $wpdb->prepare( is_numeric( $min ) ? '%d' : '%s', $min ); 230 | $max = $wpdb->prepare( is_numeric( $max ) ? '%d' : '%s', $max ); 231 | 232 | $this->where[] = [ 233 | 'joint' => $joint, 234 | 'condition' => "($column BETWEEN $min AND $max)", 235 | ]; 236 | 237 | return $this; 238 | } 239 | 240 | // Not Between? 241 | if ( is_array( $param2 ) && strpos( $param1, 'NOT BETWEEN' ) !== false ) { 242 | $min = isset( $param2[0] ) ? $param2[0] : false; 243 | $max = isset( $param2[1] ) ? $param2[1] : false; 244 | if ( ! $min || ! $max ) { 245 | $this->exception( "NOT BETWEEN min or max is missing" ); 246 | } 247 | 248 | $min = $wpdb->prepare( is_numeric( $min ) ? '%d' : '%s', $min ); 249 | $max = $wpdb->prepare( is_numeric( $max ) ? '%d' : '%s', $max ); 250 | 251 | $this->where[] = [ 252 | 'joint' => $joint, 253 | 'condition' => "($column NOT BETWEEN $min AND $max)", 254 | ]; 255 | 256 | return $this; 257 | } 258 | 259 | 260 | //first check if is array if so then make a string out of array 261 | //if not array but null then set value as null 262 | //if not null does it contains . it could be column so dont parse as string 263 | //If not column then use wpdb prepare 264 | //if contains $prefix 265 | $contain_join = preg_replace( '/^(\s?AND ?|\s?OR ?)|\s$/i', '', $param2 ); 266 | 267 | $param2 = is_array( $param2 ) ? ( '("' . implode( '","', $param2 ) . '")' ) : ( $param2 === null 268 | ? 'null' 269 | : ( strpos( $param2, '.' ) !== false || strpos( $param2, $wpdb->prefix ) !== false ? $param2 : $wpdb->prepare( is_numeric( $param2 ) ? '%d' : '%s', $param2 ) ) 270 | ); 271 | 272 | $this->where[] = [ 273 | 'joint' => $joint, 274 | 'condition' => implode( ' ', [ $column, $param1, $param2 ] ), 275 | ]; 276 | 277 | return $this; 278 | } 279 | 280 | 281 | /** 282 | * Create an or where statement 283 | * 284 | * This is the same as the normal where just with a fixed type 285 | * 286 | * @param string $column The SQL column 287 | * @param mixed $param1 288 | * @param mixed $param2 289 | * 290 | * @return Query The current query builder. 291 | */ 292 | public function orWhere( $column, $param1 = null, $param2 = null ) { 293 | return $this->where( $column, $param1, $param2, 'or' ); 294 | } 295 | 296 | /** 297 | * Create an and where statement 298 | * 299 | * This is the same as the normal where just with a fixed type 300 | * 301 | * @param string $column The SQL column 302 | * @param mixed $param1 303 | * @param mixed $param2 304 | * 305 | * @return Query The current query builder. 306 | */ 307 | public function andWhere( $column, $param1 = null, $param2 = null ) { 308 | return $this->where( $column, $param1, $param2, 'and' ); 309 | } 310 | 311 | /** 312 | * Creates a where in statement 313 | * 314 | * ->whereIn('id', [42, 38, 12]) 315 | * 316 | * @param string $column 317 | * @param array $options 318 | * 319 | * @return Query The current query builder. 320 | */ 321 | public function whereIn( $column, array $options = array() ) { 322 | // when the options are empty we skip 323 | if ( empty( $options ) ) { 324 | return $this; 325 | } 326 | 327 | return $this->where( $column, 'in', $options ); 328 | } 329 | 330 | /** 331 | * Creates a where not in statement 332 | * 333 | * ->whereNotIn('id', [42, 38, 12]) 334 | * 335 | * @param string $column 336 | * @param array $options 337 | * 338 | * @return Query The current query builder. 339 | */ 340 | public function whereNotIn( $column, array $options = array() ) { 341 | // when the options are empty we skip 342 | if ( empty( $options ) ) { 343 | return $this; 344 | } 345 | 346 | return $this->where( $column, 'not in', $options ); 347 | } 348 | 349 | /** 350 | * Creates a where something is null statement 351 | * 352 | * ->whereNull('modified_at') 353 | * 354 | * @param string $column 355 | * 356 | * @return Query The current query builder. 357 | */ 358 | public function whereNull( $column ) { 359 | return $this->where( $column, 'is', null ); 360 | } 361 | 362 | /** 363 | * Creates a where something is not null statement 364 | * 365 | * ->whereNotNull('created_at') 366 | * 367 | * @param string $column 368 | * 369 | * @return Query The current query builder. 370 | */ 371 | public function whereNotNull( $column ) { 372 | return $this->where( $column, 'is not', null ); 373 | } 374 | 375 | /** 376 | * Creates a or where something is null statement 377 | * 378 | * ->orWhereNull('modified_at') 379 | * 380 | * @param string $column 381 | * 382 | * @return Query The current query builder. 383 | */ 384 | public function orWhereNull( $column ) { 385 | return $this->orWhere( $column, 'is', null ); 386 | } 387 | 388 | /** 389 | * Creates a or where something is not null statement 390 | * 391 | * ->orWhereNotNull('modified_at') 392 | * 393 | * @param string $column 394 | * 395 | * @return Query The current query builder. 396 | */ 397 | public function orWhereNotNull( $column ) { 398 | return $this->orWhere( $column, 'is not', null ); 399 | } 400 | 401 | 402 | /** 403 | * Creates a where between statement 404 | * 405 | * ->whereBetween('user_id', 1, 2000) 406 | * 407 | * @param string $column 408 | * 409 | * @return Query The current query builder. 410 | */ 411 | public function whereBetween( $column, $min, $max ) { 412 | return $this->where( $column, 'BETWEEN', array( $min, $max ) ); 413 | } 414 | 415 | /** 416 | * Creates a where not between statement 417 | * 418 | * ->whereNotBetween('user_id', 1, 2000) 419 | * 420 | * @param string $column 421 | * 422 | * @return Query The current query builder. 423 | */ 424 | public function whereNotBetween( $column, $min, $max ) { 425 | return $this->where( $column, 'NOT BETWEEN', array( $min, $max ) ); 426 | } 427 | 428 | /** 429 | * Creates a where date between statement 430 | * 431 | * ->whereDateBetween('date', '2014-02-01', '2014-02-28') 432 | * 433 | * @param string $column 434 | * 435 | * @return Query The current query builder. 436 | */ 437 | public function whereDateBetween( $column, $start, $end ) { 438 | global $wpdb; 439 | $stat_date = $wpdb->get_var( $wpdb->prepare( 'SELECT CAST(%s as DATE)', $start ) ); 440 | $end_date = $wpdb->get_var( $wpdb->prepare( 'SELECT CAST(%s as DATE)', $end ) ); 441 | 442 | return $this->where( $column, 'BETWEEN', array( $stat_date, $end_date ) ); 443 | } 444 | 445 | 446 | /** 447 | * 448 | * @param $query 449 | * @param string $joint 450 | * 451 | * @since 1.0.1 452 | */ 453 | public function whereRaw( $query, $joint = 'AND' ) { 454 | $this->where[] = [ 455 | 'joint' => $joint, 456 | 'condition' => $query, 457 | ]; 458 | 459 | return $this; 460 | } 461 | 462 | 463 | /** 464 | * Add a join statement to the current query 465 | * 466 | * ->join('avatars', 'users.id', '=', 'avatars.user_id') 467 | * 468 | * @param array|string $table The table to join. (can contain an alias definition.) 469 | * @param string $localKey 470 | * @param string $operator The operator (=, !=, <, > etc.) 471 | * @param string $referenceKey 472 | * @param string $type The join type (inner, left, right, outer) 473 | * @param string $joint The join AND or Or 474 | * @param bool $add_prefix Add table prefix or not 475 | * 476 | * @return Query The current query builder. 477 | */ 478 | public function join( $table, $localKey, $operator = null, $referenceKey = null, $type = 'left', $joint = 'AND', $add_prefix = true ) { 479 | global $wpdb; 480 | $type = is_string( $type ) ? strtoupper( trim( $type ) ) : ( $type ? 'LEFT' : '' ); 481 | if ( ! in_array( $type, [ '', 'LEFT', 'RIGHT', 'INNER', 'CROSS', 'LEFT OUTER', 'RIGHT OUTER' ] ) ) { 482 | $this->exception( "Invalid join type." ); 483 | } 484 | 485 | $join = [ 486 | 'table' => ( $add_prefix ? $wpdb->prefix : '' ) . $table, 487 | 'type' => $type, 488 | 'on' => [], 489 | ]; 490 | 491 | // to make nested joins possible you can pass an closure 492 | // which will create a new query where you can add your nested where 493 | if ( is_object( $localKey ) && ( $localKey instanceof \Closure ) ) { 494 | //create new query object 495 | $subquery = new Query(); 496 | // run the closure callback on the sub query 497 | call_user_func_array( $localKey, array( &$subquery ) ); 498 | 499 | $join['on'] = array_merge( $join['on'], $subquery->where ); 500 | $this->join = array_merge( $this->join, array( $join ) ); 501 | 502 | return $this; 503 | } 504 | 505 | // when param2 is null we replace param2 with param one as the 506 | // value holder and make param1 to the = operator. 507 | if ( is_null( $referenceKey ) ) { 508 | $referenceKey = $operator; 509 | $operator = '='; 510 | } 511 | 512 | $referenceKey = is_array( $referenceKey ) ? ( '(\'' . implode( '\',\'', $referenceKey ) . '\')' ) 513 | : ( $referenceKey === null 514 | ? 'null' 515 | : ( strpos( $referenceKey, '.' ) !== false || strpos( $referenceKey, $wpdb->prefix ) !== false ? $referenceKey : $wpdb->prepare( is_numeric( $referenceKey ) ? '%d' : '%s', $referenceKey ) ) 516 | ); 517 | 518 | $join['on'][] = [ 519 | 'joint' => $joint, 520 | 'condition' => implode( ' ', [ $localKey, $operator, $referenceKey ] ), 521 | ]; 522 | 523 | $this->join[] = $join; 524 | 525 | return $this; 526 | } 527 | 528 | /** 529 | * Left join same as join with special type 530 | * 531 | * @param array|string $table The table to join. (can contain an alias definition.) 532 | * @param string $localKey 533 | * @param string $operator The operator (=, !=, <, > etc.) 534 | * @param string $referenceKey 535 | * 536 | * @return Query The current query builder. 537 | */ 538 | public function leftJoin( $table, $localKey, $operator = null, $referenceKey = null ) { 539 | return $this->join( $table, $localKey, $operator, $referenceKey, 'left' ); 540 | } 541 | 542 | /** 543 | * Alias of the `join` method with join type right. 544 | * 545 | * @param array|string $table The table to join. (can contain an alias definition.) 546 | * @param string $localKey 547 | * @param string $operator The operator (=, !=, <, > etc.) 548 | * @param string $referenceKey 549 | * 550 | * @return Query The current query builder. 551 | */ 552 | public function rightJoin( $table, $localKey, $operator = null, $referenceKey = null ) { 553 | return $this->join( $table, $localKey, $operator, $referenceKey, 'right' ); 554 | } 555 | 556 | /** 557 | * Alias of the `join` method with join type inner. 558 | * 559 | * @param array|string $table The table to join. (can contain an alias definition.) 560 | * @param string $localKey 561 | * @param string $operator The operator (=, !=, <, > etc.) 562 | * @param string $referenceKey 563 | * 564 | * @return Query The current query builder. 565 | */ 566 | public function innerJoin( $table, $localKey, $operator = null, $referenceKey = null ) { 567 | return $this->join( $table, $localKey, $operator, $referenceKey, 'inner' ); 568 | } 569 | 570 | /** 571 | * Alias of the `join` method with join type outer. 572 | * 573 | * @param array|string $table The table to join. (can contain an alias definition.) 574 | * @param string $localKey 575 | * @param string $operator The operator (=, !=, <, > etc.) 576 | * @param string $referenceKey 577 | * 578 | * @return Query The current query builder. 579 | */ 580 | public function outerJoin( $table, $localKey, $operator = null, $referenceKey = null ) { 581 | return $this->join( $table, $localKey, $operator, $referenceKey, 'outer' ); 582 | } 583 | 584 | /** 585 | * 586 | * @param $query 587 | * @param string $joint 588 | * 589 | * @since 1.0.1 590 | */ 591 | public function joinRaw( $query, $joint = 'AND' ) { 592 | $this->join['on'][] = [ 593 | 'joint' => $joint, 594 | 'condition' => $query, 595 | ]; 596 | 597 | return $this; 598 | } 599 | 600 | 601 | /** 602 | * Adds group by statement. 603 | * ->groupBy('category') 604 | * ->gorupBy(['category', 'price']) 605 | * 606 | * @param string $field 607 | * 608 | * @return Query this for chaining. 609 | * @since 1.0.0 610 | * 611 | */ 612 | public function group_by( $field ) { 613 | if ( empty( $field ) ) { 614 | return $this; 615 | } 616 | 617 | if ( is_array( $field ) ) { 618 | foreach ( $field as $groupby ) { 619 | $this->group[] = $groupby; 620 | } 621 | } else { 622 | $this->group[] = $field; 623 | } 624 | 625 | return $this; 626 | } 627 | 628 | /** 629 | * Adds having statement. 630 | * 631 | * ->group_by('user.id') 632 | * ->having('count(user.id)>1') 633 | * 634 | * @param string $statement 635 | * 636 | * @return Query this for chaining. 637 | * @since 1.0.0 638 | * 639 | */ 640 | public function having( $statement ) { 641 | if ( ! empty( $statement ) ) { 642 | $this->having = $statement; 643 | } 644 | 645 | return $this; 646 | } 647 | 648 | /** 649 | * Adds order by statement. 650 | * 651 | * ->orderBy('created_at') 652 | * ->orderBy('modified_at', 'desc') 653 | * 654 | * @param string $key 655 | * @param string $direction 656 | * 657 | * @return Query this for chaining. 658 | * @throws Exception 659 | * @since 1.0.0 660 | * 661 | */ 662 | public function order_by( $key, $direction = 'ASC' ) { 663 | $direction = trim( strtoupper( $direction ) ); 664 | if ( $direction !== 'ASC' && $direction !== 'DESC' ) { 665 | $this->exception( 'Invalid direction value.' ); 666 | } 667 | if ( ! empty( $key ) ) { 668 | $this->order[] = $key . ' ' . $direction; 669 | } 670 | 671 | return $this; 672 | } 673 | 674 | /** 675 | * Set the query limit 676 | * 677 | * // limit() 678 | * ->limit(20) 679 | * 680 | * // limit(, ) 681 | * ->limit(60, 20) 682 | * 683 | * @param int $limit 684 | * @param int $limit2 685 | * 686 | * @return Query The current query builder. 687 | */ 688 | public function limit( $limit, $limit2 = null ) { 689 | if ( ! is_null( $limit2 ) ) { 690 | $this->offset = (int) $limit; 691 | $this->limit = (int) $limit2; 692 | } else { 693 | $this->limit = (int) $limit; 694 | } 695 | 696 | return $this; 697 | } 698 | 699 | /** 700 | * Adds offset statement. 701 | * 702 | * ->offset(20) 703 | * 704 | * @param int $offset 705 | * 706 | * @return Query this for chaining. 707 | * 708 | */ 709 | public function offset( $offset ) { 710 | $this->offset = $offset; 711 | 712 | return $this; 713 | } 714 | 715 | /** 716 | * Create a query limit based on a page and a page size 717 | * 718 | * //page(, ) 719 | * ->page(2, 20) 720 | * 721 | * @param int $page 722 | * @param int $size 723 | * 724 | * @return Query The current query builder. 725 | * @since 1.0.0 726 | */ 727 | public function page( $page, $size = 20 ) { 728 | if ( ( $page = (int) $page ) <= 1 ) { 729 | $page = 0; 730 | } 731 | 732 | $this->limit = (int) $size; 733 | $this->offset = (int) $size * $page; 734 | 735 | return $this; 736 | } 737 | 738 | /** 739 | * Find something, means select one item by key 740 | * 741 | * ->find('manikdrmc@gmail.com', 'email') 742 | * 743 | * @param int $id 744 | * @param string $key 745 | * 746 | * @return mixed 747 | */ 748 | public function find( $id, $key = 'id' ) { 749 | return $this->where( $key, $id )->one(); 750 | } 751 | 752 | /** 753 | * Get the first result ordered by the given key. 754 | * 755 | * @param string $key By what should the first item be selected? Default is: 'id' 756 | * 757 | * @return mixed The first result. 758 | */ 759 | public function first( $key = 'id' ) { 760 | return $this->order_by( $key, 'asc' )->one(); 761 | } 762 | 763 | /** 764 | * Get the last result by key 765 | * 766 | * @param string $key 767 | * 768 | * @return mixed the last result. 769 | */ 770 | public function last( $key = 'id' ) { 771 | return $this->order_by( $key, 'desc' )->one(); 772 | } 773 | 774 | /** 775 | * Pluck item. 776 | * ->find('post_title') 777 | * @return Object 778 | * @since 1.0.1 779 | */ 780 | public function pluck() { 781 | $selects = func_get_args(); 782 | $this->select = $selects; 783 | 784 | return $this->get(); 785 | } 786 | 787 | 788 | /** 789 | * Returns results from builder statements. 790 | * 791 | * @param int $output WPDB output type. 792 | * @param callable $row_map Function callable to filter or map results to. 793 | * @param bool $calc_rows Flag that indicates to SQL if rows should be calculated or not. 794 | * 795 | * @return Object || Array 796 | * @since 1.0.0 797 | * 798 | * @global object $wpdb 799 | * 800 | */ 801 | public function get( $output = OBJECT, $row_map = null, $calc_rows = false ) { 802 | global $wpdb; 803 | do_action( 'wp_query_builder_get_builder', $this ); 804 | do_action( 'wp_query_builder_get_builder_' . $this->id, $this ); 805 | 806 | $query = ''; 807 | $this->_query_select( $query, $calc_rows ); 808 | $this->_query_from( $query ); 809 | $this->_query_join( $query ); 810 | $this->_query_where( $query ); 811 | $this->_query_group( $query ); 812 | $this->_query_having( $query ); 813 | $this->_query_order( $query ); 814 | $this->_query_limit( $query ); 815 | $this->_query_offset( $query ); 816 | 817 | // Process 818 | $query = apply_filters( 'wp_query_builder_get_query', $query ); 819 | $query = apply_filters( 'wp_query_builder_get_query_' . $this->id, $query ); 820 | 821 | $results = $wpdb->get_results( $query, $output ); 822 | if ( $row_map ) { 823 | $results = array_map( function ( $row ) use ( &$row_map ) { 824 | return call_user_func_array( $row_map, [ $row ] ); 825 | }, $results ); 826 | } 827 | 828 | return $results; 829 | } 830 | 831 | /** 832 | * Sets the limit to 1, executes and returns the first result using get. 833 | * 834 | * @param string $output 835 | * 836 | * @return mixed The single result. 837 | */ 838 | public function one( $output = OBJECT ) { 839 | global $wpdb; 840 | do_action( 'wp_query_builder_one_builder', $this ); 841 | do_action( 'wp_query_builder_one_builder_' . $this->id, $this ); 842 | 843 | $this->_query_select( $query ); 844 | $this->_query_from( $query ); 845 | $this->_query_join( $query ); 846 | $this->_query_where( $query ); 847 | $this->_query_group( $query ); 848 | $this->_query_having( $query ); 849 | $this->_query_order( $query ); 850 | $query .= ' LIMIT 1'; 851 | $this->_query_offset( $query ); 852 | 853 | $query = apply_filters( 'wp_query_builder_one_query', $query ); 854 | $query = apply_filters( 'wp_query_builder_one_query_' . $this->id, $query ); 855 | 856 | return $wpdb->get_row( $query, $output ); 857 | } 858 | 859 | /** 860 | * Just return the number of results 861 | * 862 | * @param string|int $column 863 | * 864 | * @return int 865 | */ 866 | public function count( $column = 1 ) { 867 | global $wpdb; 868 | do_action( 'wp_query_builder_count_builder', $this ); 869 | do_action( 'wp_query_builder_count_builder_' . $this->id, $this ); 870 | 871 | $query = 'SELECT count(' . $column . ') as `count`'; 872 | $this->_query_from( $query ); 873 | $this->_query_join( $query ); 874 | $this->_query_where( $query ); 875 | $this->_query_group( $query ); 876 | $this->_query_having( $query ); 877 | 878 | return intval( $wpdb->get_var( $query ) ); 879 | } 880 | 881 | /** 882 | * Just get a single value from the result 883 | * 884 | * @param string $column The index of the column. 885 | * @param bool $calc_rows Flag that indicates to SQL if rows should be calculated or not. 886 | * 887 | * @return mixed The columns value 888 | */ 889 | public function column( $column = 0, $calc_rows = false ) { 890 | global $wpdb; 891 | do_action( 'wp_query_builder_column_builder', $this ); 892 | do_action( 'wp_query_builder_column_builder_' . $this->id, $this ); 893 | 894 | $query = ''; 895 | $this->_query_select( $query, $calc_rows ); 896 | $this->_query_from( $query ); 897 | $this->_query_join( $query ); 898 | $this->_query_where( $query ); 899 | $this->_query_group( $query ); 900 | $this->_query_having( $query ); 901 | $this->_query_order( $query ); 902 | $this->_query_limit( $query ); 903 | $this->_query_offset( $query ); 904 | 905 | return $wpdb->get_col( $query, $column ); 906 | } 907 | 908 | /** 909 | * Returns a value. 910 | * 911 | * @param int $x Column of value to return. Indexed from 0. 912 | * @param int $y Row of value to return. Indexed from 0. 913 | * 914 | * @return mixed 915 | * @global object $wpdb 916 | * 917 | * @since 1.0.0 918 | * 919 | */ 920 | public function value( $x = 0, $y = 0 ) { 921 | global $wpdb; 922 | do_action( 'wp_query_builder_value_builder', $this ); 923 | do_action( 'wp_query_builder_value_builder_' . $this->id, $this ); 924 | 925 | // Build 926 | // Query 927 | $query = ''; 928 | $this->_query_select( $query ); 929 | $this->_query_from( $query ); 930 | $this->_query_join( $query ); 931 | $this->_query_where( $query ); 932 | $this->_query_group( $query ); 933 | $this->_query_having( $query ); 934 | $this->_query_order( $query ); 935 | $this->_query_limit( $query ); 936 | $this->_query_offset( $query ); 937 | 938 | return $wpdb->get_var( $query, $x, $y ); 939 | } 940 | 941 | 942 | /** 943 | * Update or insert. 944 | * 945 | * @param $data 946 | * 947 | * @return array|string 948 | */ 949 | public function updateOrInsert( $data ) { 950 | if ( $this->first() ) { 951 | return $this->update( $data ); 952 | } else { 953 | return $this->insert( $data ); 954 | } 955 | } 956 | 957 | /** 958 | * Find or insert. 959 | * 960 | * @param $data 961 | * 962 | * @return array|string 963 | */ 964 | public function findOrInsert( $data ) { 965 | if ( $this->first() ) { 966 | return $this->update( $data ); 967 | } else { 968 | return $this->insert( $data ); 969 | } 970 | } 971 | 972 | /** 973 | * Get max value. 974 | * 975 | * @param $column 976 | * 977 | * @return int 978 | * @since 1.0.1 979 | */ 980 | public function max( $column ) { 981 | global $wpdb; 982 | $query = 'SELECT MAX(' . $column . ')'; 983 | $this->_query_from( $query ); 984 | $this->_query_join( $query ); 985 | $this->_query_where( $query ); 986 | $this->_query_group( $query ); 987 | $this->_query_having( $query ); 988 | 989 | return intval( $wpdb->get_var( $query ) ); 990 | } 991 | 992 | /** 993 | * Get min value. 994 | * 995 | * @param $column 996 | * 997 | * @return int 998 | * @since 1.0.1 999 | */ 1000 | public function min( $column ) { 1001 | global $wpdb; 1002 | $query = 'SELECT MIN(' . $column . ')'; 1003 | $this->_query_from( $query ); 1004 | $this->_query_join( $query ); 1005 | $this->_query_where( $query ); 1006 | $this->_query_group( $query ); 1007 | $this->_query_having( $query ); 1008 | 1009 | return intval( $wpdb->get_var( $query ) ); 1010 | } 1011 | 1012 | /** 1013 | * Get avg value. 1014 | * 1015 | * @param $column 1016 | * 1017 | * @return int 1018 | * @since 1.0.1 1019 | */ 1020 | public function avg( $column ) { 1021 | global $wpdb; 1022 | $query = 'SELECT AVG(' . $column . ')'; 1023 | $this->_query_from( $query ); 1024 | $this->_query_join( $query ); 1025 | $this->_query_where( $query ); 1026 | $this->_query_group( $query ); 1027 | $this->_query_having( $query ); 1028 | 1029 | return intval( $wpdb->get_var( $query ) ); 1030 | } 1031 | 1032 | /** 1033 | * Get sum value. 1034 | * 1035 | * @param $column 1036 | * 1037 | * @return int 1038 | * @since 1.0.1 1039 | */ 1040 | public function sum( $column ) { 1041 | global $wpdb; 1042 | $query = 'SELECT SUM(' . $column . ')'; 1043 | $this->_query_from( $query ); 1044 | $this->_query_join( $query ); 1045 | $this->_query_where( $query ); 1046 | $this->_query_group( $query ); 1047 | $this->_query_having( $query ); 1048 | 1049 | return intval( $wpdb->get_var( $query ) ); 1050 | } 1051 | 1052 | /** 1053 | * Returns flag indicating if query has been executed. 1054 | * 1055 | * @param string $sql 1056 | * 1057 | * @return bool 1058 | * @since 1.0.0 1059 | * 1060 | * @global object $wpdb 1061 | * 1062 | */ 1063 | public function query( $sql = '' ) { 1064 | global $wpdb; 1065 | $query = $sql; 1066 | if ( empty( $query ) ) { 1067 | $this->_query_select( $query, false ); 1068 | $this->_query_from( $query ); 1069 | $this->_query_join( $query ); 1070 | $this->_query_where( $query ); 1071 | $this->_query_group( $query ); 1072 | $this->_query_having( $query ); 1073 | $this->_query_order( $query ); 1074 | $this->_query_limit( $query ); 1075 | $this->_query_offset( $query ); 1076 | } 1077 | 1078 | return $wpdb->query( $query ); 1079 | } 1080 | 1081 | /** 1082 | * Returns query from builder statements. 1083 | * 1084 | * @return string 1085 | * @since 1.0.0 1086 | */ 1087 | public function toSql() { 1088 | $query = ''; 1089 | $this->_query_select( $query ); 1090 | $this->_query_from( $query ); 1091 | $this->_query_join( $query ); 1092 | $this->_query_where( $query ); 1093 | $this->_query_group( $query ); 1094 | $this->_query_having( $query ); 1095 | $this->_query_order( $query ); 1096 | $this->_query_limit( $query ); 1097 | $this->_query_offset( $query ); 1098 | 1099 | return $query; 1100 | } 1101 | 1102 | /** 1103 | * Returns found rows in last query, if SQL_CALC_FOUND_ROWS is used and is supported. 1104 | * @return array 1105 | * @global object $wpdb 1106 | * 1107 | * @since 1.0.0 1108 | * 1109 | */ 1110 | public function rows_found() { 1111 | global $wpdb; 1112 | $query = 'SELECT FOUND_ROWS()'; 1113 | // Process 1114 | $query = apply_filters( 'wp_query_builder_found_rows_query', $query ); 1115 | $query = apply_filters( 'wp_query_builder_found_rows_query_' . $this->id, $query ); 1116 | 1117 | return $wpdb->get_var( $query ); 1118 | } 1119 | 1120 | /** 1121 | * Returns flag indicating if delete query has been executed. 1122 | * @return bool 1123 | * @global object $wpdb 1124 | * 1125 | * @since 1.0.0 1126 | * 1127 | */ 1128 | public function delete() { 1129 | global $wpdb; 1130 | do_action( 'wp_query_builder_delete_builder', $this ); 1131 | do_action( 'wp_query_builder_delete_builder_' . $this->id, $this ); 1132 | 1133 | $query = ''; 1134 | $this->_query_delete( $query ); 1135 | $this->_query_from( $query ); 1136 | $this->_query_join( $query ); 1137 | $this->_query_where( $query ); 1138 | 1139 | return $wpdb->query( $query ); 1140 | } 1141 | 1142 | /** 1143 | * Update 1144 | * @return bool 1145 | * @global object $wpdb 1146 | * 1147 | * @since 1.0.0 1148 | * 1149 | */ 1150 | public function update( $data ) { 1151 | global $wpdb; 1152 | $conditions = ''; 1153 | $this->_query_where( $conditions ); 1154 | 1155 | if ( empty( trim( $conditions ) ) ) { 1156 | return false; 1157 | } 1158 | 1159 | $fields = array(); 1160 | foreach ( $data as $column => $value ) { 1161 | 1162 | if ( is_null( $value ) ) { 1163 | $fields[] = "`$column` = NULL"; 1164 | continue; 1165 | } 1166 | 1167 | $fields[] = "`$column` = " . $wpdb->prepare( is_numeric( $value ) ? '%d' : '%s', $value ); 1168 | } 1169 | 1170 | $table = trim( $this->from ); 1171 | $fields = implode( ', ', $fields ); 1172 | 1173 | $query = "UPDATE `$table` SET $fields $conditions"; 1174 | 1175 | return $wpdb->query( $query ); 1176 | } 1177 | 1178 | /** 1179 | * Insert data. 1180 | * 1181 | * @param $data 1182 | * @param array $format 1183 | * 1184 | * @return bool|int 1185 | * @since 1.0.1 1186 | */ 1187 | public function insert( $data, $format = array() ) { 1188 | global $wpdb; 1189 | 1190 | if ( false !== $wpdb->insert( trim( $this->from ), $data, $format ) ) { 1191 | return $wpdb->insert_id; 1192 | }; 1193 | 1194 | return false; 1195 | } 1196 | 1197 | /** 1198 | * Return a cloned object from current builder. 1199 | * 1200 | * @return Query 1201 | * @since 1.0.0 1202 | */ 1203 | public function copy() { 1204 | return clone( $this ); 1205 | } 1206 | 1207 | /** 1208 | * Builds query's select statement. 1209 | * 1210 | * @param string &$query 1211 | * @param bool $calc_rows 1212 | * 1213 | * @since 1.0.0 1214 | * 1215 | */ 1216 | private function _query_select( &$query, $calc_rows = false ) { 1217 | $query = 'SELECT ' . ( $calc_rows ? 'SQL_CALC_FOUND_ROWS ' : '' ) . ( 1218 | is_array( $this->select ) && count( $this->select ) 1219 | ? implode( ',', $this->select ) 1220 | : '*' 1221 | ); 1222 | } 1223 | 1224 | /** 1225 | * Builds query's from statement. 1226 | * 1227 | * @param string &$query 1228 | * 1229 | * @since 1.0.0 1230 | * 1231 | */ 1232 | private function _query_from( &$query ) { 1233 | $query .= ' FROM ' . $this->from; 1234 | } 1235 | 1236 | /** 1237 | * Builds query's join statement. 1238 | * 1239 | * @param string &$query 1240 | * 1241 | * @since 1.0.0 1242 | * 1243 | */ 1244 | private function _query_join( &$query ) { 1245 | foreach ( $this->join as $join ) { 1246 | $query .= ( ! empty( $join['type'] ) ? ' ' . $join['type'] . ' JOIN ' : ' JOIN ' ) . $join['table']; 1247 | for ( $i = 0; $i < count( $join['on'] ); ++ $i ) { 1248 | $query .= ( $i === 0 ? ' ON ' : ' ' . $join['on'][ $i ]['joint'] . ' ' ) 1249 | . $join['on'][ $i ]['condition']; 1250 | } 1251 | } 1252 | } 1253 | 1254 | /** 1255 | * Builds query's where statement. 1256 | * 1257 | * @param string &$query 1258 | * 1259 | * @since 1.0.0 1260 | * 1261 | */ 1262 | public function _query_where( &$query ) { 1263 | for ( $i = 0; $i < count( $this->where ); ++ $i ) { 1264 | $query .= ( $i === 0 ? ' WHERE ' : ' ' . $this->where[ $i ]['joint'] . ' ' ) 1265 | . $this->where[ $i ]['condition']; 1266 | } 1267 | } 1268 | 1269 | /** 1270 | * Builds query's group by statement. 1271 | * 1272 | * @param string &$query 1273 | * 1274 | * @since 1.0.0 1275 | * 1276 | */ 1277 | private function _query_group( &$query ) { 1278 | if ( count( $this->group ) ) { 1279 | $query .= ' GROUP BY ' . implode( ',', $this->group ); 1280 | } 1281 | } 1282 | 1283 | /** 1284 | * Builds query's having statement. 1285 | * 1286 | * @param string &$query 1287 | * 1288 | * @since 1.0.0 1289 | * 1290 | */ 1291 | private function _query_having( &$query ) { 1292 | if ( $this->having ) { 1293 | $query .= ' HAVING ' . $this->having; 1294 | } 1295 | } 1296 | 1297 | /** 1298 | * Builds query's order by statement. 1299 | * 1300 | * @param string &$query 1301 | * 1302 | * @since 1.0.0 1303 | * 1304 | */ 1305 | private function _query_order( &$query ) { 1306 | if ( count( $this->order ) ) { 1307 | $query .= ' ORDER BY ' . implode( ',', $this->order ); 1308 | } 1309 | } 1310 | 1311 | /** 1312 | * Builds query's limit statement. 1313 | * 1314 | * @param string &$query 1315 | * 1316 | * @global object $wpdb 1317 | * 1318 | * @since 1.0.0 1319 | * 1320 | */ 1321 | private function _query_limit( &$query ) { 1322 | global $wpdb; 1323 | if ( $this->limit ) { 1324 | $query .= $wpdb->prepare( ' LIMIT %d', $this->limit ); 1325 | } 1326 | } 1327 | 1328 | /** 1329 | * Builds query's offset statement. 1330 | * 1331 | * @param string &$query 1332 | * 1333 | * @global object $wpdb 1334 | * 1335 | * @since 1.0.0 1336 | * 1337 | */ 1338 | private function _query_offset( &$query ) { 1339 | global $wpdb; 1340 | if ( $this->offset ) { 1341 | $query .= $wpdb->prepare( ' OFFSET %d', $this->offset ); 1342 | } 1343 | } 1344 | 1345 | /** 1346 | * Builds query's delete statement. 1347 | * 1348 | * @param string &$query 1349 | * 1350 | * @since 1.0.0 1351 | * 1352 | */ 1353 | private function _query_delete( &$query ) { 1354 | $query .= trim( 'DELETE ' . ( count( $this->join ) 1355 | ? preg_replace( '/\s[aA][sS][\s\S]+.*?/', '', $this->from ) 1356 | : '' 1357 | ) ); 1358 | } 1359 | 1360 | /** 1361 | * Sanitize value. 1362 | * 1363 | * @param string|bool $callback Sanitize callback. 1364 | * @param mixed $value 1365 | * 1366 | * @return mixed 1367 | * @since 1.0.0 1368 | * 1369 | */ 1370 | private function sanitize_value( $callback, $value ) { 1371 | if ( $callback === true ) { 1372 | $callback = ( is_numeric( $value ) && strpos( $value, '.' ) !== false ) 1373 | ? 'floatval' 1374 | : ( is_numeric( $value ) 1375 | ? 'intval' 1376 | : ( is_string( $value ) 1377 | ? 'sanitize_text_field' 1378 | : null 1379 | ) 1380 | ); 1381 | } 1382 | if ( strpos( $callback, '_builder' ) !== false ) { 1383 | $callback = [ &$this, $callback ]; 1384 | } 1385 | if ( is_array( $value ) ) { 1386 | for ( $i = count( $value ) - 1; $i >= 0; -- $i ) { 1387 | $value[ $i ] = $this->sanitize_value( true, $value[ $i ] ); 1388 | } 1389 | } 1390 | 1391 | return $callback && is_callable( $callback ) ? call_user_func_array( $callback, [ $value ] ) : $value; 1392 | } 1393 | 1394 | /** 1395 | * @param $message 1396 | * 1397 | * @throws \Exception 1398 | * @since 1.0.0 1399 | */ 1400 | private function exception( $message ) { 1401 | throw new \Exception( $message ); 1402 | } 1403 | } 1404 | --------------------------------------------------------------------------------