From 4dacf7a00e0832ba1fa7dd598583808e0c836fb9 Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Sat, 27 Nov 2021 05:33:04 +0530 Subject: [PATCH 1/2] Add chunk method to query builder --- README.md | 44 ++++++++++++++++-------- src/QueryBuilder/QueryBuilderHandler.php | 27 +++++++++++++-- 2 files changed, 54 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 7a988fc..ad0b4e2 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ ___ ## Connection -WP Fluent supports multiple database connections but you can use alias +WP Fluent supports multiple database connections but you can use alias for only one connection at a time. Just pass the global wpdb and necessary configurations during connection. @@ -120,12 +120,12 @@ When you create a connection: new \WpFluent\Connection($wpdb, ['prefix' => $wpdb->prefix], 'MyAlias'); ``` `MyAlias` is the name for the class alias you want to use e.g. `MyAlias::table(...)` - + You can use any 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. -When not using an alias you can instantiate the QueryBuilder handler +When not using an alias you can instantiate the QueryBuilder handler separately, helpful for Dependency Injection and Testing. ```PHP @@ -152,7 +152,7 @@ The query below returns the first row where id = 3, `null` if no rows. ```PHP $row = DB::table('my_table')->find(3); ``` -Access your row like, `echo $row->name`. If your field name is not `id` then +Access your row like, `echo $row->name`. If your field name is not `id` then pass the field name as second parameter `DB::table('my_table')->find(3, 'person_id');` The query below returns all the rows where `name = 'Frost'`, `null` if no rows. @@ -173,7 +173,7 @@ $query = DB::table('my_table')->select('*'); ->select(array('mytable.myfield1', 'mytable.myfield2', 'another_table.myfield3')); ``` -Using select method multiple times `select('a')->select('b')` will also select `a` +Using select method multiple times `select('a')->select('b')` will also select `a` and `b`. It can be useful if you want to do conditional selects (within a PHP `if`). @@ -205,6 +205,20 @@ Returns the first row, or `null` if there is no record. Using this method you ca make sure if a record exists. Access it like `$row->name` +#### Chunk large tables +```PHP +$file = fopen("php://output", 'w'); +$query = DB::table('my_table')->chunk(100, function ($records) use ($file) { + foreach($records as $record) { + fputcsv($file, $record); + } +}); +fclose($file); +``` +Returns the first row, or `null` if there is no record. Using this method you can also +make sure if a record exists. Access it like `$row->name` + + #### Get Rows Count ```PHP $query = DB::table('my_table')->where('name', '=', 'admin'); @@ -262,7 +276,7 @@ DB::table('my_table') ->where('my_table.age', 10) ->where(function ($q) { $q->where('name', 'LIKE', '%najrul%'); - + // You can provide a closure on these wheres too, to nest further. $q->orWhere('description', 'LIKE', '%frost%'); }); @@ -485,7 +499,7 @@ $queryObj->getRawSql(); ``` ### Sub Queries and Nested Queries -Rarely but you may need to run sub queries or nested queries. WP Fluent is powerful +Rarely but you may need to run sub queries or nested queries. WP Fluent is powerful enough to do this for you. You can create different query objects and use the `DB::subQuery()` method. @@ -543,7 +557,7 @@ so banned users don't get access. The syntax is `registerEvent('event type', 'table name', action in a closure)`. -If you want the event to be performed when **any table is being queried**, provide +If you want the event to be performed when **any table is being queried**, provide `':any'` as table name. **Other examples:** @@ -552,12 +566,12 @@ After inserting data into `my_table`, details will be inserted into another tabl ```PHP DB::registerEvent('after-insert', 'my_table', function ($queryBuilder, $insertId) { $data = array('person_id' => $insertId, 'details' => 'Meh', 'age' => 5); - + $queryBuilder->table('person_details')->insert($data); }); ``` -Whenever data is inserted into `person_details` table, set the timestamp field +Whenever data is inserted into `person_details` table, set the timestamp field `created_at`, so we don't have to specify it everywhere: ```PHP DB::registerEvent('after-insert', 'person_details', function ($queryBuilder, $insertId) { @@ -573,21 +587,21 @@ After deleting from `my_table` delete the relations: ```PHP DB::registerEvent('after-delete', 'my_table', function ($queryBuilder, $queryObject) { $bindings = $queryObject->getBindings(); - + $queryBuilder->table('person_details')->where('person_id', $binding[0])->delete(); }); ``` WP Fluent passes the current instance of query builder as first parameter of your -closure so you can build queries with this object, you can do anything like usual +closure so you can build queries with this object, you can do anything like usual query builder (`DB`). If something other than `null` is returned from the `before-*` query handler, the value will be result of execution and DB will not be actually queried (and thus, corresponding `after-*` handler will not be called either). -Only on `after-*` events you get three parameters: **first** is the query builder, +Only on `after-*` events you get three parameters: **first** is the query builder, **third** is the execution time as float and **the second** varies: - On `after-select` you get the `results` obtained from `select`. @@ -615,8 +629,8 @@ Here are some cases where Query Events can be extremely helpful: - Add/edit created_at and updated _at data after each entry. #### Notes - - Query Events go recursively, for example after inserting into `table_a` your event - inserts into `table_b`, now you can have another event registered with `table_b` + - Query Events go recursively, for example after inserting into `table_a` your event + inserts into `table_b`, now you can have another event registered with `table_b` which inserts into `table_c`. - Of course Query Events don't work with raw queries. - **This is forked from awesome @usmanhalalit vai's [Pixie](https://github.com/usmanhalalit/pixie) diff --git a/src/QueryBuilder/QueryBuilderHandler.php b/src/QueryBuilder/QueryBuilderHandler.php index 9d5ca0d..f29f650 100644 --- a/src/QueryBuilder/QueryBuilderHandler.php +++ b/src/QueryBuilder/QueryBuilderHandler.php @@ -100,7 +100,8 @@ public function setFetchMode($mode) */ public function asObject($className, $constructorArgs = array()) { - var_dump('need to implement this'); die(); + var_dump('need to implement this'); + die(); return $this->setFetchMode(\PDO::FETCH_CLASS, $className, $constructorArgs); } @@ -282,7 +283,7 @@ public function getQuery($type = 'select', $dataToBePassed = array()) } $queryArr = $this->adapterInstance->$type($this->statements, $dataToBePassed); - + return $this->container->build( '\\WpFluent\\QueryBuilder\\QueryObject', array($queryArr['sql'], $queryArr['bindings']) @@ -1151,4 +1152,26 @@ public function when($value, $callback, $default = null) return $this; } + + /** + * Chunk query by given no of records and apply the callback with the records + * + * @param int $limit + * @param callable $callback + + * @return mixed + */ + public function chunk($limit, $callback, $page = 1) + { + $offset = ($page - 1) * $limit; + $this->limit($limit)->offset($offset); + + $records = $this->get(); + if (count($records)) { + $callback($records); + return $this->chunk($limit, $callback, ++$page); + } + + return $page; + } } From d96a15e5c59e3cc1b06b383b71b6cf27adb061fe Mon Sep 17 00:00:00 2001 From: Mohan Raj Date: Sat, 27 Nov 2021 05:45:14 +0530 Subject: [PATCH 2/2] Update README.md --- README.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index ad0b4e2..a5bc425 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ There are so many advanced options documented below. Sold? Let's install. - [Multiple Selects](#multiple-selects) - [Select Distinct](#select-distinct) - [Get All](#get-all) + - [Chunk](#chunk) - [Get First Row](#get-first-row) - [Get Rows Count](#get-rows-count) - [**Where**](#where) @@ -196,16 +197,10 @@ foreach ($result as $row) { } ``` -#### Get First Row -```PHP -$query = DB::table('my_table')->where('name', '=', 'admin'); -$row = $query->first(); -``` -Returns the first row, or `null` if there is no record. Using this method you can also -make sure if a record exists. Access it like `$row->name` +#### Chunk +What if you have thousands of rows in your database? Using [Get All](#get-all), can fill your memory up when you load all that data. +Here is the chunk mehtod. You can provide a number of rows to fetch at once, and a callback to run on the result. - -#### Chunk large tables ```PHP $file = fopen("php://output", 'w'); $query = DB::table('my_table')->chunk(100, function ($records) use ($file) { @@ -215,6 +210,12 @@ $query = DB::table('my_table')->chunk(100, function ($records) use ($file) { }); fclose($file); ``` + +#### Get First Row +```PHP +$query = DB::table('my_table')->where('name', '=', 'admin'); +$row = $query->first(); +``` Returns the first row, or `null` if there is no record. Using this method you can also make sure if a record exists. Access it like `$row->name`