From cf581ebc0850f55ca20022f7fbc21359302c7fe8 Mon Sep 17 00:00:00 2001 From: Gary Kovar Date: Sat, 11 Nov 2023 11:16:15 -0500 Subject: [PATCH 01/12] adds a failing test that upsert should insert --- tests/wpunit/QueryBuilder/CRUDTest.php | 27 ++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/wpunit/QueryBuilder/CRUDTest.php b/tests/wpunit/QueryBuilder/CRUDTest.php index b554790..a30983a 100644 --- a/tests/wpunit/QueryBuilder/CRUDTest.php +++ b/tests/wpunit/QueryBuilder/CRUDTest.php @@ -129,4 +129,31 @@ public function testDeleteShouldDeleteRowInDatabase() $this->assertNull($post); } + /** + * Tests if upsert() adds a row to the database. + * + * @return void + */ + public function testUpsertShouldAddRowToDatabase() + { + $data = [ + 'post_title' => 'Query Builder CRUD test', + 'post_type' => 'crud_test', + 'post_content' => 'Hello World!', + ]; + + DB::table('posts')->upsert($data); + + $id = DB::last_insert_id(); + + $post = DB::table('posts') + ->select('post_title', 'post_type', 'post_content') + ->where('ID', $id) + ->get(); + + $this->assertEquals($data['post_title'], $post->post_title); + $this->assertEquals($data['post_type'], $post->post_type); + $this->assertEquals($data['post_content'], $post->post_content); + } + } From 9445ddd51cbece5c83c09d0cfb39bef30a821ab7 Mon Sep 17 00:00:00 2001 From: Gary Kovar Date: Sat, 11 Nov 2023 11:23:57 -0500 Subject: [PATCH 02/12] upsert inserts without checking for existence --- src/DB/QueryBuilder/Concerns/CRUD.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/DB/QueryBuilder/Concerns/CRUD.php b/src/DB/QueryBuilder/Concerns/CRUD.php index 43bbcf8..bafd81f 100644 --- a/src/DB/QueryBuilder/Concerns/CRUD.php +++ b/src/DB/QueryBuilder/Concerns/CRUD.php @@ -48,6 +48,10 @@ public function update( $data, $format = null ) { ); } + public function upsert( $data, $match = null, $format = null ) { + return $this->insert( $data, $format ); + } + /** * @since 1.0.0 * From 083cabeaaefd3453383683a8f5ea7f6d67ac4033 Mon Sep 17 00:00:00 2001 From: Gary Kovar Date: Sat, 11 Nov 2023 11:33:54 -0500 Subject: [PATCH 03/12] add upsert update test --- tests/wpunit/QueryBuilder/CRUDTest.php | 35 ++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/wpunit/QueryBuilder/CRUDTest.php b/tests/wpunit/QueryBuilder/CRUDTest.php index a30983a..429a958 100644 --- a/tests/wpunit/QueryBuilder/CRUDTest.php +++ b/tests/wpunit/QueryBuilder/CRUDTest.php @@ -156,4 +156,39 @@ public function testUpsertShouldAddRowToDatabase() $this->assertEquals($data['post_content'], $post->post_content); } + /** + * Tests if upsert() updates a row in the database. + * + * @return void + */ + public function testUpsertShouldUpdateRowInDatabase() + { + $data = [ + 'post_title' => 'Query Builder CRUD test - upsert update', + 'post_type' => 'crud_test', + 'post_content' => 'Hello World from upsert!', + ]; + + DB::table('posts')->insert($data); + + $original_id = DB::last_insert_id(); + + $updated_data = [ + 'post_content' => 'Hello World from upsert! - updated', + ]; + + $match = [ + 'post_title' => 'Query Builder CRUD test - upsert update', + ]; + + DB::table('posts')->upsert($updated_data, $match); + + $post = DB::table('posts') + ->select('post_title', 'post_type', 'post_content') + ->where('ID', $original_id) + ->get(); + + $this->assertEquals($updated_data['post_content'], $post->post_content); + } + } From 147f02225eecbdccb281e673ed9072af575c2f65 Mon Sep 17 00:00:00 2001 From: Gary Kovar Date: Sat, 11 Nov 2023 11:34:24 -0500 Subject: [PATCH 04/12] implement code to pass upsert update test --- src/DB/QueryBuilder/Concerns/CRUD.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/DB/QueryBuilder/Concerns/CRUD.php b/src/DB/QueryBuilder/Concerns/CRUD.php index bafd81f..c27a471 100644 --- a/src/DB/QueryBuilder/Concerns/CRUD.php +++ b/src/DB/QueryBuilder/Concerns/CRUD.php @@ -48,7 +48,22 @@ public function update( $data, $format = null ) { ); } - public function upsert( $data, $match = null, $format = null ) { + /** + * @param $data + * @param $match + * @param $format + * + * @return false|int + */ + public function upsert( $data, $match = [], $format = null ) { + foreach( $match as $column => $value ) { + $this->where( $column, $value ); + } + $exists = $this->get(); + + if ( $exists ) { + return $this->update( $data, $format ); + } return $this->insert( $data, $format ); } From 2e2971217b2a9b2a17f56b7d978fd68bc588840e Mon Sep 17 00:00:00 2001 From: Gary Kovar Date: Sat, 11 Nov 2023 11:37:52 -0500 Subject: [PATCH 05/12] code cleanup --- src/DB/QueryBuilder/Concerns/CRUD.php | 4 ++-- tests/wpunit/QueryBuilder/CRUDTest.php | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/DB/QueryBuilder/Concerns/CRUD.php b/src/DB/QueryBuilder/Concerns/CRUD.php index c27a471..2fdfeed 100644 --- a/src/DB/QueryBuilder/Concerns/CRUD.php +++ b/src/DB/QueryBuilder/Concerns/CRUD.php @@ -59,11 +59,11 @@ public function upsert( $data, $match = [], $format = null ) { foreach( $match as $column => $value ) { $this->where( $column, $value ); } - $exists = $this->get(); - if ( $exists ) { + if ( $this->get() ) { return $this->update( $data, $format ); } + return $this->insert( $data, $format ); } diff --git a/tests/wpunit/QueryBuilder/CRUDTest.php b/tests/wpunit/QueryBuilder/CRUDTest.php index 429a958..2171fe5 100644 --- a/tests/wpunit/QueryBuilder/CRUDTest.php +++ b/tests/wpunit/QueryBuilder/CRUDTest.php @@ -174,6 +174,8 @@ public function testUpsertShouldUpdateRowInDatabase() $original_id = DB::last_insert_id(); $updated_data = [ + 'post_title' => 'Query Builder CRUD test - upsert update', + 'post_type' => 'crud_test', 'post_content' => 'Hello World from upsert! - updated', ]; @@ -181,7 +183,7 @@ public function testUpsertShouldUpdateRowInDatabase() 'post_title' => 'Query Builder CRUD test - upsert update', ]; - DB::table('posts')->upsert($updated_data, $match); + DB::table('posts')->upsert( $updated_data, $match ); $post = DB::table('posts') ->select('post_title', 'post_type', 'post_content') From e6e81ef5671e0fecaf5634f99e174a03ff7a4f24 Mon Sep 17 00:00:00 2001 From: Gary Kovar Date: Sat, 11 Nov 2023 11:44:32 -0500 Subject: [PATCH 06/12] update the test to indicate a more sane approach to matching --- tests/wpunit/QueryBuilder/CRUDTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/wpunit/QueryBuilder/CRUDTest.php b/tests/wpunit/QueryBuilder/CRUDTest.php index 2171fe5..688c281 100644 --- a/tests/wpunit/QueryBuilder/CRUDTest.php +++ b/tests/wpunit/QueryBuilder/CRUDTest.php @@ -180,7 +180,7 @@ public function testUpsertShouldUpdateRowInDatabase() ]; $match = [ - 'post_title' => 'Query Builder CRUD test - upsert update', + 'post_title', ]; DB::table('posts')->upsert( $updated_data, $match ); From fdd10be9b9d6d4895d760820ece96feb1f317262 Mon Sep 17 00:00:00 2001 From: Gary Kovar Date: Sat, 11 Nov 2023 11:46:26 -0500 Subject: [PATCH 07/12] update logic to match simplified structure --- src/DB/QueryBuilder/Concerns/CRUD.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/DB/QueryBuilder/Concerns/CRUD.php b/src/DB/QueryBuilder/Concerns/CRUD.php index 2fdfeed..8cb5f7a 100644 --- a/src/DB/QueryBuilder/Concerns/CRUD.php +++ b/src/DB/QueryBuilder/Concerns/CRUD.php @@ -56,14 +56,17 @@ public function update( $data, $format = null ) { * @return false|int */ public function upsert( $data, $match = [], $format = null ) { - foreach( $match as $column => $value ) { - $this->where( $column, $value ); + // Build the where clause(s). + foreach( $match as $column ) { + $this->where( $column, $data[$column] ); } + // If the row exists, update it. if ( $this->get() ) { return $this->update( $data, $format ); } + // Otherwise, insert it. return $this->insert( $data, $format ); } From 22038048b42f7ed8eb03f1f0a07b79f34ad6c1cf Mon Sep 17 00:00:00 2001 From: Gary Kovar Date: Sat, 11 Nov 2023 11:58:29 -0500 Subject: [PATCH 08/12] adds test on multiple match columns --- tests/wpunit/QueryBuilder/CRUDTest.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/wpunit/QueryBuilder/CRUDTest.php b/tests/wpunit/QueryBuilder/CRUDTest.php index 688c281..894e692 100644 --- a/tests/wpunit/QueryBuilder/CRUDTest.php +++ b/tests/wpunit/QueryBuilder/CRUDTest.php @@ -191,6 +191,27 @@ public function testUpsertShouldUpdateRowInDatabase() ->get(); $this->assertEquals($updated_data['post_content'], $post->post_content); + + // Test multiple match columns + $further_updated_data = [ + 'post_title' => 'Query Builder CRUD test - upsert update', + 'post_type' => 'crud_test', + 'post_content' => 'Hello World from upsert! - updated even more!', + ]; + + $match = [ + 'post_title', + 'post_type', + ]; + + DB::table('posts')->upsert( $further_updated_data, $match ); + + $post = DB::table('posts') + ->select('post_title', 'post_type', 'post_content') + ->where('ID', $original_id) + ->get(); + + $this->assertEquals($further_updated_data['post_content'], $post->post_content); } } From 8af1c6d3d29c50f4148f29068ea7ce96374b5ecb Mon Sep 17 00:00:00 2001 From: Gary Kovar Date: Sat, 11 Nov 2023 16:32:37 -0500 Subject: [PATCH 09/12] updates readme --- README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/README.md b/README.md index d10a34c..696ff70 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ composer require stellarwp/db - [CRUD](#crud) - [Insert](#insert) - [Update](#update) + - [Upsert](#upsert) - [Delete](#delete) - [Get](#get) @@ -847,6 +848,28 @@ DB::table('posts') ]); ``` +### Upsert + +The `QueryBuilder::upsert` method may be used to update an existing record or create a new record if it doesn't exist. + +```php +// 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 The `QueryBuilder::delete` method may be used to delete records from the table. From 06038245cf309de8b1ad9241983534d289ae8221 Mon Sep 17 00:00:00 2001 From: Gary Kovar Date: Mon, 13 Nov 2023 18:14:59 +0000 Subject: [PATCH 10/12] addressses CR conversations --- src/DB/QueryBuilder/Concerns/CRUD.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/DB/QueryBuilder/Concerns/CRUD.php b/src/DB/QueryBuilder/Concerns/CRUD.php index 8cb5f7a..775512d 100644 --- a/src/DB/QueryBuilder/Concerns/CRUD.php +++ b/src/DB/QueryBuilder/Concerns/CRUD.php @@ -49,15 +49,19 @@ public function update( $data, $format = null ) { } /** - * @param $data - * @param $match - * @param $format + * Upsert allows for inserting or updating a row depending on whether it already exists. + * + * @since 1.0.8 + * + * @param array $data The data to insert or update. + * @param array $match The columns to match on. + * @param string|null $format * * @return false|int */ public function upsert( $data, $match = [], $format = null ) { // Build the where clause(s). - foreach( $match as $column ) { + foreach ( $match as $column ) { $this->where( $column, $data[$column] ); } From 1cc88e10c3527d3b80f694078fff071366be076e Mon Sep 17 00:00:00 2001 From: Matthew Batchelder Date: Tue, 14 Nov 2023 07:27:34 -0500 Subject: [PATCH 11/12] Adjust spacing Co-authored-by: Nikolay Strikhar --- src/DB/QueryBuilder/Concerns/CRUD.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DB/QueryBuilder/Concerns/CRUD.php b/src/DB/QueryBuilder/Concerns/CRUD.php index 775512d..40f81ca 100644 --- a/src/DB/QueryBuilder/Concerns/CRUD.php +++ b/src/DB/QueryBuilder/Concerns/CRUD.php @@ -62,7 +62,7 @@ public function update( $data, $format = null ) { public function upsert( $data, $match = [], $format = null ) { // Build the where clause(s). foreach ( $match as $column ) { - $this->where( $column, $data[$column] ); + $this->where( $column, $data[ $column ] ); } // If the row exists, update it. From 6b2daf4098ea470f45389c6c578d2a8001f36af5 Mon Sep 17 00:00:00 2001 From: Matthew Batchelder Date: Tue, 14 Nov 2023 07:36:18 -0500 Subject: [PATCH 12/12] Add comments in docblocks --- src/DB/QueryBuilder/Concerns/CRUD.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/DB/QueryBuilder/Concerns/CRUD.php b/src/DB/QueryBuilder/Concerns/CRUD.php index 40f81ca..269ef5f 100644 --- a/src/DB/QueryBuilder/Concerns/CRUD.php +++ b/src/DB/QueryBuilder/Concerns/CRUD.php @@ -53,11 +53,11 @@ public function update( $data, $format = null ) { * * @since 1.0.8 * - * @param array $data The data to insert or update. + * @param array $data The data to insert or update. * @param array $match The columns to match on. - * @param string|null $format + * @param string|array|null $format Array of formats to be mapped to each value in $data. If string, the format will be used for all values in $data. * - * @return false|int + * @return false|int Number of rows updated/inserted, false on error. */ public function upsert( $data, $match = [], $format = null ) { // Build the where clause(s).