From d4aea4a0ddea3bcc6628e7c55922d00edbf86a9b Mon Sep 17 00:00:00 2001 From: Dimitrios Pantazis Date: Thu, 16 Oct 2025 19:40:48 +0300 Subject: [PATCH 01/15] Ensures array values can be used --- .../Traits/Custom_Table_Query_Methods.php | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/Schema/Traits/Custom_Table_Query_Methods.php b/src/Schema/Traits/Custom_Table_Query_Methods.php index d7383b3..04fb0f7 100644 --- a/src/Schema/Traits/Custom_Table_Query_Methods.php +++ b/src/Schema/Traits/Custom_Table_Query_Methods.php @@ -336,6 +336,7 @@ public static function get_total_items( array $args = [] ): int { * Updates multiple rows into the table. * * @since 3.0.0 + * @since 3.1.4 Enabled unfolding the value if is an array. * * @param array $entries The entries to update. * @@ -372,7 +373,7 @@ public static function update_many( array $entries ): bool { [ $value, $placeholder ] = self::prepare_value_for_query( $column, $value ); - $set_statement[] = $database::prepare( "%i = {$placeholder}", ...array_filter( [ $column, $value ], static fn( $v ) => null !== $v ) ); + $set_statement[] = $database::prepare( "%i = {$placeholder}", ...array_filter( [ $column, ...self::ensure_array( $value ) ], static fn( $v ) => null !== $v ) ); } $set_statement = implode( ', ', $set_statement ); @@ -672,6 +673,7 @@ protected static function get_join_parts( string $join_table, string $join_condi * * @since 3.0.0 * @since 3.1.1 Added the $order_by parameter. + * @since 3.1.4 Enabled unfolding the value if is an array. * * @param string $column The column to get the models by. * @param mixed $value The value to get the models by. @@ -690,7 +692,7 @@ public static function get_all_by( string $column, $value, string $operator = '= $database = Config::get_db(); $results = []; - foreach ( static::fetch_all_where( $database::prepare( "WHERE %i {$operator} {$placeholder}", ...array_filter( [ $column, $value ], static fn( $v ) => null !== $v ) ), $limit, ARRAY_A, $order_by ) as $task_array ) { + foreach ( static::fetch_all_where( $database::prepare( "WHERE %i {$operator} {$placeholder}", ...array_filter( [ $column, ...self::ensure_array( $value ) ], static fn( $v ) => null !== $v ) ), $limit, ARRAY_A, $order_by ) as $task_array ) { if ( empty( $task_array[ static::uid_column() ] ) ) { continue; } @@ -705,6 +707,7 @@ public static function get_all_by( string $column, $value, string $operator = '= * Gets the first model by a column. * * @since 3.0.0 + * @since 3.1.4 Enabled unfolding the value if is an array. * * @param string $column The column to get the model by. * @param mixed $value The value to get the model by. @@ -717,7 +720,7 @@ public static function get_first_by( string $column, $value ) { [ $value, $placeholder ] = self::prepare_value_for_query( $column, $value ); $database = Config::get_db(); - $task_array = static::fetch_first_where( $database::prepare( "WHERE %i = {$placeholder}", ...array_filter( [ $column, $value ], static fn( $v ) => null !== $v ) ), ARRAY_A ); + $task_array = static::fetch_first_where( $database::prepare( "WHERE %i = {$placeholder}", ...array_filter( [ $column, ...self::ensure_array( $value ) ], static fn( $v ) => null !== $v ) ), ARRAY_A ); if ( empty( $task_array[ static::uid_column() ] ) ) { return null; @@ -918,4 +921,17 @@ public static function cast_value_based_on_type( string $type, $value ) { throw new InvalidArgumentException( "Unsupported column type: {$type}." ); } } + + /** + * Ensures the value is an array. + * + * @since 3.1.4 + * + * @param mixed $value The value to ensure is an array. + * + * @return array The value as an array. + */ + private static function ensure_array( $value ): array { + return is_array( $value ) ? $value : [ $value ]; + } } From b64dbe0ef4386d2021606195450fa2d69d858c8f Mon Sep 17 00:00:00 2001 From: Dimitrios Pantazis Date: Thu, 16 Oct 2025 19:51:44 +0300 Subject: [PATCH 02/15] Ading test coverage --- .../Traits/Custom_Table_Query_MethodsTest.php | 228 ++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 tests/wpunit/Traits/Custom_Table_Query_MethodsTest.php diff --git a/tests/wpunit/Traits/Custom_Table_Query_MethodsTest.php b/tests/wpunit/Traits/Custom_Table_Query_MethodsTest.php new file mode 100644 index 0000000..d501026 --- /dev/null +++ b/tests/wpunit/Traits/Custom_Table_Query_MethodsTest.php @@ -0,0 +1,228 @@ +get_query_test_table()->drop(); + } + + /** + * Test that update_multiple handles array values correctly. + * + * This test covers the fix in commit d4aea4a that ensures array values + * returned from prepare_value_for_query are properly unfolded when used + * in database::prepare() calls. + * + * @test + */ + public function should_update_multiple_with_array_values() { + $table = $this->get_query_test_table(); + Register::table( $table ); + + // Insert test data + $table::insert( [ + 'name' => 'Test 1', + 'slug' => 'test-1', + 'status' => 1, + ] ); + + $table::insert( [ + 'name' => 'Test 2', + 'slug' => 'test-2', + 'status' => 1, + ] ); + + // Update multiple rows using array values + $updated = $table::update_multiple( [ + [ + 'slug' => 'test-1', + 'name' => 'Updated Test 1', + ], + [ + 'slug' => 'test-2', + 'name' => 'Updated Test 2', + ], + ], 'slug' ); + + $this->assertEquals( 2, $updated ); + + // Verify the updates + $result1 = $table::get_first_by( 'slug', 'test-1' ); + $this->assertEquals( 'Updated Test 1', $result1['name'] ); + + $result2 = $table::get_first_by( 'slug', 'test-2' ); + $this->assertEquals( 'Updated Test 2', $result2['name'] ); + } + + /** + * Test that get_all_by handles array values correctly with IN operator. + * + * @test + */ + public function should_get_all_by_with_array_values() { + $table = $this->get_query_test_table(); + Register::table( $table ); + + // Insert test data + $id1 = $table::insert( [ + 'name' => 'Test 1', + 'slug' => 'test-1', + 'status' => 1, + ] ); + + $id2 = $table::insert( [ + 'name' => 'Test 2', + 'slug' => 'test-2', + 'status' => 1, + ] ); + + $id3 = $table::insert( [ + 'name' => 'Test 3', + 'slug' => 'test-3', + 'status' => 0, + ] ); + + // Get all by status using array (simulating IN operator scenario) + $results = $table::get_all_by( 'status', 1 ); + + $this->assertCount( 2, $results ); + } + + /** + * Test that get_first_by handles array values correctly. + * + * @test + */ + public function should_get_first_by_with_array_values() { + $table = $this->get_query_test_table(); + Register::table( $table ); + + // Insert test data + $table::insert( [ + 'name' => 'First Match', + 'slug' => 'first-match', + 'status' => 1, + ] ); + + $table::insert( [ + 'name' => 'Second Match', + 'slug' => 'second-match', + 'status' => 1, + ] ); + + // Get first by slug + $result = $table::get_first_by( 'slug', 'first-match' ); + + $this->assertNotNull( $result ); + $this->assertEquals( 'First Match', $result['name'] ); + } + + /** + * Test that update_multiple handles integer array values. + * + * @test + */ + public function should_update_multiple_with_integer_array_values() { + $table = $this->get_query_test_table(); + Register::table( $table ); + + // Insert test data + $table::insert( [ + 'name' => 'Active Item', + 'slug' => 'active-item', + 'status' => 1, + ] ); + + // Update using integer value + $updated = $table::update_multiple( [ + [ + 'slug' => 'active-item', + 'status' => 0, + ], + ], 'slug' ); + + $this->assertEquals( 1, $updated ); + + // Verify the update + $result = $table::get_first_by( 'slug', 'active-item' ); + $this->assertEquals( 0, $result['status'] ); + } + + /** + * Test that ensure_array helper works correctly with scalar values. + * + * This indirectly tests the ensure_array method through the public API. + * + * @test + */ + public function should_handle_scalar_values_in_queries() { + $table = $this->get_query_test_table(); + Register::table( $table ); + + // Insert test data with scalar values + $id = $table::insert( [ + 'name' => 'Scalar Test', + 'slug' => 'scalar-test', + 'status' => 1, + ] ); + + $this->assertIsInt( $id ); + $this->assertGreaterThan( 0, $id ); + + // Verify scalar value retrieval + $result = $table::get_first_by( 'id', $id ); + $this->assertEquals( 'Scalar Test', $result['name'] ); + } + + /** + * Get a test table for query method testing. + */ + private function get_query_test_table() { + return new class extends Table { + const SCHEMA_VERSION = '1.0.0'; + + protected static $base_table_name = 'query_test'; + protected static $group = 'test'; + protected static $schema_slug = 'test-query'; + + public static function get_schema_history(): array { + $table_name = static::table_name( true ); + $callable = function() use ( $table_name ) { + $columns = new Column_Collection(); + + $columns[] = ( new ID( 'id' ) )->set_length( 11 )->set_type( Column_Types::INT ); + $columns[] = ( new String_Column( 'name' ) )->set_length( 255 ); + $columns[] = ( new String_Column( 'slug' ) )->set_length( 255 )->set_is_index( true ); + $columns[] = ( new Integer_Column( 'status' ) )->set_length( 1 )->set_default_value( 0 ); + + return new Table_Schema( $table_name, $columns ); + }; + + return [ + static::SCHEMA_VERSION => $callable, + ]; + } + + public static function transform_from_array( array $result_array ) { + return $result_array; + } + }; + } +} From 3b3cc7ee21e9683c20d4d551e7dcd9c4f2d7bd3a Mon Sep 17 00:00:00 2001 From: Dimitrios Pantazis Date: Thu, 16 Oct 2025 19:51:49 +0300 Subject: [PATCH 03/15] Fixing some variable names --- src/Schema/Traits/Custom_Table_Query_Methods.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Schema/Traits/Custom_Table_Query_Methods.php b/src/Schema/Traits/Custom_Table_Query_Methods.php index 04fb0f7..63aef53 100644 --- a/src/Schema/Traits/Custom_Table_Query_Methods.php +++ b/src/Schema/Traits/Custom_Table_Query_Methods.php @@ -692,12 +692,12 @@ public static function get_all_by( string $column, $value, string $operator = '= $database = Config::get_db(); $results = []; - foreach ( static::fetch_all_where( $database::prepare( "WHERE %i {$operator} {$placeholder}", ...array_filter( [ $column, ...self::ensure_array( $value ) ], static fn( $v ) => null !== $v ) ), $limit, ARRAY_A, $order_by ) as $task_array ) { - if ( empty( $task_array[ static::uid_column() ] ) ) { + foreach ( static::fetch_all_where( $database::prepare( "WHERE %i {$operator} {$placeholder}", ...array_filter( [ $column, ...self::ensure_array( $value ) ], static fn( $v ) => null !== $v ) ), $limit, ARRAY_A, $order_by ) as $data_array ) { + if ( empty( $data_array[ static::uid_column() ] ) ) { continue; } - $results[] = static::transform_from_array( self::amend_value_types( $task_array ) ); + $results[] = static::transform_from_array( self::amend_value_types( $data_array ) ); } return $results; @@ -720,13 +720,13 @@ public static function get_first_by( string $column, $value ) { [ $value, $placeholder ] = self::prepare_value_for_query( $column, $value ); $database = Config::get_db(); - $task_array = static::fetch_first_where( $database::prepare( "WHERE %i = {$placeholder}", ...array_filter( [ $column, ...self::ensure_array( $value ) ], static fn( $v ) => null !== $v ) ), ARRAY_A ); + $data_array = static::fetch_first_where( $database::prepare( "WHERE %i = {$placeholder}", ...array_filter( [ $column, ...self::ensure_array( $value ) ], static fn( $v ) => null !== $v ) ), ARRAY_A ); - if ( empty( $task_array[ static::uid_column() ] ) ) { + if ( empty( $data_array[ static::uid_column() ] ) ) { return null; } - return static::transform_from_array( self::amend_value_types( $task_array ) ); + return static::transform_from_array( self::amend_value_types( $data_array ) ); } /** From cf4aea7f0c1a0cf93453cd8494261daf402e229c Mon Sep 17 00:00:00 2001 From: Dimitrios Pantazis Date: Thu, 16 Oct 2025 19:54:42 +0300 Subject: [PATCH 04/15] added changelog --- CHANGELOG.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ed95ac..f4bfcb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,15 +2,19 @@ All notable changes to this project will be documented in this file. This project adhere to the [Semantic Versioning](http://semver.org/) standard. +## [3.1.4] 2025-10-16 + +* Fix - Amend the `get_*` query methods to properly handle array values. + ## [3.1.3] 2025-10-08 -* Fix - Fix the `get_current_schema` method to cache the schema of each implementation. +* Fix - Amend the `get_current_schema` method to cache the schema of each implementation. [3.1.3]: https://github.com/stellarwp/schema/releases/tag/3.1.3 ## [3.1.2] 2025-10-02 -* Fix - Fix the `update_many` method to properly check if the transaction was successful. +* Fix - Amend the `update_many` method to properly check if the transaction was successful. [3.1.2]: https://github.com/stellarwp/schema/releases/tag/3.1.2 From 442be02ef369cd427e0a799f18ba460e1d243a3a Mon Sep 17 00:00:00 2001 From: Dimitrios Pantazis Date: Thu, 16 Oct 2025 20:14:38 +0300 Subject: [PATCH 05/15] Fix tests --- .../Traits/Custom_Table_Query_Methods.php | 13 +++- .../Traits/Custom_Table_Query_MethodsTest.php | 70 +++++++++++-------- 2 files changed, 50 insertions(+), 33 deletions(-) diff --git a/src/Schema/Traits/Custom_Table_Query_Methods.php b/src/Schema/Traits/Custom_Table_Query_Methods.php index 63aef53..bd8cc85 100644 --- a/src/Schema/Traits/Custom_Table_Query_Methods.php +++ b/src/Schema/Traits/Custom_Table_Query_Methods.php @@ -708,19 +708,28 @@ public static function get_all_by( string $column, $value, string $operator = '= * * @since 3.0.0 * @since 3.1.4 Enabled unfolding the value if is an array. + * @since 3.1.4 Added the $operator parameter. * * @param string $column The column to get the model by. * @param mixed $value The value to get the model by. + * @param string $operator The operator to use. * * @return ?mixed The model, or `null` if no model is found. * * @throws InvalidArgumentException If the column does not exist. + * @throws InvalidArgumentException If the operator is invalid. */ - public static function get_first_by( string $column, $value ) { + public static function get_first_by( string $column, $value, string $operator = '=' ) { [ $value, $placeholder ] = self::prepare_value_for_query( $column, $value ); + $operator = strtoupper( $operator ); + + if ( ! in_array( $operator, self::operators(), true ) ) { + throw new InvalidArgumentException( "Invalid operator: {$operator}." ); + } + $database = Config::get_db(); - $data_array = static::fetch_first_where( $database::prepare( "WHERE %i = {$placeholder}", ...array_filter( [ $column, ...self::ensure_array( $value ) ], static fn( $v ) => null !== $v ) ), ARRAY_A ); + $data_array = static::fetch_first_where( $database::prepare( "WHERE %i {$operator} {$placeholder}", ...array_filter( [ $column, ...self::ensure_array( $value ) ], static fn( $v ) => null !== $v ) ), ARRAY_A ); if ( empty( $data_array[ static::uid_column() ] ) ) { return null; diff --git a/tests/wpunit/Traits/Custom_Table_Query_MethodsTest.php b/tests/wpunit/Traits/Custom_Table_Query_MethodsTest.php index d501026..07ad4ec 100644 --- a/tests/wpunit/Traits/Custom_Table_Query_MethodsTest.php +++ b/tests/wpunit/Traits/Custom_Table_Query_MethodsTest.php @@ -12,24 +12,20 @@ use StellarWP\Schema\Collections\Column_Collection; use StellarWP\Schema\Tables\Contracts\Table; use StellarWP\Schema\Tables\Table_Schema; +use StellarWP\DB\DB; class Custom_Table_Query_MethodsTest extends SchemaTestCase { use Table_Fixtures; /** * @before + * @after */ public function drop_tables() { $this->get_query_test_table()->drop(); } /** - * Test that update_multiple handles array values correctly. - * - * This test covers the fix in commit d4aea4a that ensures array values - * returned from prepare_value_for_query are properly unfolded when used - * in database::prepare() calls. - * * @test */ public function should_update_multiple_with_array_values() { @@ -43,23 +39,27 @@ public function should_update_multiple_with_array_values() { 'status' => 1, ] ); + $id1 = DB::last_insert_id(); + $table::insert( [ 'name' => 'Test 2', 'slug' => 'test-2', 'status' => 1, ] ); + $id2 = DB::last_insert_id(); + // Update multiple rows using array values - $updated = $table::update_multiple( [ + $updated = $table::update_many( [ [ - 'slug' => 'test-1', + 'id' => $id1, 'name' => 'Updated Test 1', ], [ - 'slug' => 'test-2', + 'id' => $id2, 'name' => 'Updated Test 2', ], - ], 'slug' ); + ] ); $this->assertEquals( 2, $updated ); @@ -72,8 +72,6 @@ public function should_update_multiple_with_array_values() { } /** - * Test that get_all_by handles array values correctly with IN operator. - * * @test */ public function should_get_all_by_with_array_values() { @@ -81,33 +79,45 @@ public function should_get_all_by_with_array_values() { Register::table( $table ); // Insert test data - $id1 = $table::insert( [ + $table::insert( [ 'name' => 'Test 1', 'slug' => 'test-1', 'status' => 1, ] ); - $id2 = $table::insert( [ + $id1 = DB::last_insert_id(); + + $table::insert( [ 'name' => 'Test 2', 'slug' => 'test-2', 'status' => 1, ] ); - $id3 = $table::insert( [ + $id2 = DB::last_insert_id(); + + $table::insert( [ 'name' => 'Test 3', 'slug' => 'test-3', 'status' => 0, ] ); + $id3 = DB::last_insert_id(); + // Get all by status using array (simulating IN operator scenario) - $results = $table::get_all_by( 'status', 1 ); + $results = $table::get_all_by( 'status', [ 1, 0 ], 'IN' ); + + $this->assertCount( 3, $results ); - $this->assertCount( 2, $results ); + $this->assertEquals( 'Test 1', $results[0]['name'] ); + $this->assertEquals( 'Test 2', $results[1]['name'] ); + $this->assertEquals( 'Test 3', $results[2]['name'] ); + + $this->assertEquals( 1, $results[0]['status'] ); + $this->assertEquals( 1, $results[1]['status'] ); + $this->assertEquals( 0, $results[2]['status'] ); } /** - * Test that get_first_by handles array values correctly. - * * @test */ public function should_get_first_by_with_array_values() { @@ -128,15 +138,13 @@ public function should_get_first_by_with_array_values() { ] ); // Get first by slug - $result = $table::get_first_by( 'slug', 'first-match' ); + $result = $table::get_first_by( 'slug', [ 'second-match' ], 'NOT IN' ); $this->assertNotNull( $result ); $this->assertEquals( 'First Match', $result['name'] ); } /** - * Test that update_multiple handles integer array values. - * * @test */ public function should_update_multiple_with_integer_array_values() { @@ -150,13 +158,15 @@ public function should_update_multiple_with_integer_array_values() { 'status' => 1, ] ); + $id1 = DB::last_insert_id(); + // Update using integer value - $updated = $table::update_multiple( [ + $updated = $table::update_many( [ [ - 'slug' => 'active-item', + 'id' => $id1, 'status' => 0, ], - ], 'slug' ); + ] ); $this->assertEquals( 1, $updated ); @@ -166,10 +176,6 @@ public function should_update_multiple_with_integer_array_values() { } /** - * Test that ensure_array helper works correctly with scalar values. - * - * This indirectly tests the ensure_array method through the public API. - * * @test */ public function should_handle_scalar_values_in_queries() { @@ -177,12 +183,14 @@ public function should_handle_scalar_values_in_queries() { Register::table( $table ); // Insert test data with scalar values - $id = $table::insert( [ + $table::insert( [ 'name' => 'Scalar Test', 'slug' => 'scalar-test', 'status' => 1, ] ); + $id = DB::last_insert_id(); + $this->assertIsInt( $id ); $this->assertGreaterThan( 0, $id ); @@ -210,7 +218,7 @@ public static function get_schema_history(): array { $columns[] = ( new ID( 'id' ) )->set_length( 11 )->set_type( Column_Types::INT ); $columns[] = ( new String_Column( 'name' ) )->set_length( 255 ); $columns[] = ( new String_Column( 'slug' ) )->set_length( 255 )->set_is_index( true ); - $columns[] = ( new Integer_Column( 'status' ) )->set_length( 1 )->set_default_value( 0 ); + $columns[] = ( new Integer_Column( 'status' ) )->set_length( 1 )->set_default( 0 ); return new Table_Schema( $table_name, $columns ); }; From 20a6443d33e55fbababc38bd6f55c571c8e65f5e Mon Sep 17 00:00:00 2001 From: Dimitrios Pantazis Date: Fri, 17 Oct 2025 16:30:37 +0300 Subject: [PATCH 06/15] Update CHANGELOG.md Co-authored-by: theAverageDev (Luca Tumedei) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4bfcb3..215803f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. This projec ## [3.1.4] 2025-10-16 -* Fix - Amend the `get_*` query methods to properly handle array values. +* Fix - Handle array values correctly in the `get_*` query methods. ## [3.1.3] 2025-10-08 From 9db4cd0d828e546bf258db06261677afe2962a8e Mon Sep 17 00:00:00 2001 From: Dimitrios Pantazis Date: Fri, 17 Oct 2025 16:35:57 +0300 Subject: [PATCH 07/15] amend changelog entries --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 215803f..f2bb264 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,13 +8,13 @@ All notable changes to this project will be documented in this file. This projec ## [3.1.3] 2025-10-08 -* Fix - Amend the `get_current_schema` method to cache the schema of each implementation. +* Fix - `get_current_schema` method will npw cache the schema of each implementation correctly. [3.1.3]: https://github.com/stellarwp/schema/releases/tag/3.1.3 ## [3.1.2] 2025-10-02 -* Fix - Amend the `update_many` method to properly check if the transaction was successful. +* Fix - `update_many` method will now properly check if the transaction was successful. [3.1.2]: https://github.com/stellarwp/schema/releases/tag/3.1.2 From f6020f3363d96ab8c989939ef412bac3919d820d Mon Sep 17 00:00:00 2001 From: Dimitrios Pantazis Date: Fri, 17 Oct 2025 16:36:02 +0300 Subject: [PATCH 08/15] add test case for empty array --- .../Traits/Custom_Table_Query_MethodsTest.php | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/wpunit/Traits/Custom_Table_Query_MethodsTest.php b/tests/wpunit/Traits/Custom_Table_Query_MethodsTest.php index 07ad4ec..52d4276 100644 --- a/tests/wpunit/Traits/Custom_Table_Query_MethodsTest.php +++ b/tests/wpunit/Traits/Custom_Table_Query_MethodsTest.php @@ -117,6 +117,43 @@ public function should_get_all_by_with_array_values() { $this->assertEquals( 0, $results[2]['status'] ); } + /** + * @test + */ + public function should_handle_empty_array() { + $table = $this->get_query_test_table(); + Register::table( $table ); + + // Insert test data + $table::insert( [ + 'name' => 'Test 1', + 'slug' => 'test-1', + 'status' => 1, + ] ); + + $id1 = DB::last_insert_id(); + + $table::insert( [ + 'name' => 'Test 2', + 'slug' => 'test-2', + 'status' => 1, + ] ); + + $id2 = DB::last_insert_id(); + + $table::insert( [ + 'name' => 'Test 3', + 'slug' => 'test-3', + 'status' => 0, + ] ); + + $id3 = DB::last_insert_id(); + + $results = $table::get_all_by( 'status', [], 'IN' ); + + $this->assertEmpty( $results ); + } + /** * @test */ From e530895b1c4153560c61b479cf717c71af97f8d0 Mon Sep 17 00:00:00 2001 From: Dimitrios Pantazis Date: Fri, 17 Oct 2025 16:47:19 +0300 Subject: [PATCH 09/15] trying to handle empty arrays --- src/Schema/Traits/Custom_Table_Query_Methods.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Schema/Traits/Custom_Table_Query_Methods.php b/src/Schema/Traits/Custom_Table_Query_Methods.php index bd8cc85..0d53be3 100644 --- a/src/Schema/Traits/Custom_Table_Query_Methods.php +++ b/src/Schema/Traits/Custom_Table_Query_Methods.php @@ -803,6 +803,10 @@ private static function prepare_value_for_query( string $column, $value ): array throw new InvalidArgumentException( "Unsupported column type: $column_type." ); } + if ( is_array( $value ) && empty( $value ) ) { + return [ null, '(NULL)' ]; + } + // @phpstan-ignore-next-line return [ $value, is_array( $value ) ? '(' . implode( ',', array_fill( 0, count( $value ), $placeholder ) ) . ')' : $placeholder ]; } From 205e138816ccab8584069e7bd67aab9e930dd2c1 Mon Sep 17 00:00:00 2001 From: Dimitrios Pantazis Date: Fri, 17 Oct 2025 16:55:23 +0300 Subject: [PATCH 10/15] amend docblock to fix static analysis --- src/Schema/Traits/Custom_Table_Query_Methods.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Schema/Traits/Custom_Table_Query_Methods.php b/src/Schema/Traits/Custom_Table_Query_Methods.php index 0d53be3..17c5591 100644 --- a/src/Schema/Traits/Custom_Table_Query_Methods.php +++ b/src/Schema/Traits/Custom_Table_Query_Methods.php @@ -743,8 +743,8 @@ public static function get_first_by( string $column, $value, string $operator = * * @since 3.0.0 * - * @param string $column The column to prepare the value for. - * @param mixed $value The value to prepare. + * @param string $column The column to prepare the value for. + * @param null|string|int|float|bool|DateTimeInterface|array $value The value to prepare. * * @return array{0: mixed, 1: string} The prepared value and placeholder. * @@ -803,7 +803,7 @@ private static function prepare_value_for_query( string $column, $value ): array throw new InvalidArgumentException( "Unsupported column type: $column_type." ); } - if ( is_array( $value ) && empty( $value ) ) { + if ( is_array( $value ) && ! $value ) { return [ null, '(NULL)' ]; } From a3a970b4e73b4b5046a08af0e890f628cffad9b2 Mon Sep 17 00:00:00 2001 From: Dimitrios Pantazis Date: Fri, 17 Oct 2025 17:31:07 +0300 Subject: [PATCH 11/15] Ensuring expected variable type --- phpstan.neon.dist | 1 + .../Traits/Custom_Table_Query_Methods.php | 32 ++++++++++++------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index e485880..1ea8d3c 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -20,6 +20,7 @@ parameters: level: 5 inferPrivatePropertyTypeFromConstructor: true reportUnmatchedIgnoredErrors: false + treatPhpDocTypesAsCertain: false # Paths to be analyzed. paths: diff --git a/src/Schema/Traits/Custom_Table_Query_Methods.php b/src/Schema/Traits/Custom_Table_Query_Methods.php index 17c5591..78e2583 100644 --- a/src/Schema/Traits/Custom_Table_Query_Methods.php +++ b/src/Schema/Traits/Custom_Table_Query_Methods.php @@ -338,7 +338,7 @@ public static function get_total_items( array $args = [] ): int { * @since 3.0.0 * @since 3.1.4 Enabled unfolding the value if is an array. * - * @param array $entries The entries to update. + * @param array> $entries The entries to update. * * @return bool Whether the update was successful. */ @@ -675,11 +675,11 @@ protected static function get_join_parts( string $join_table, string $join_condi * @since 3.1.1 Added the $order_by parameter. * @since 3.1.4 Enabled unfolding the value if is an array. * - * @param string $column The column to get the models by. - * @param mixed $value The value to get the models by. - * @param string $operator The operator to use. - * @param int $limit The limit of models to return. - * @param string $order_by The order by clause to use. + * @param string $column The column to get the models by. + * @param null|int|string|float|bool|DateTimeInterface|string[]|int[]|float[]|DateTimeInterface[] $value The value to get the models by. + * @param string $operator The operator to use. + * @param int $limit The limit of models to return. + * @param string $order_by The order by clause to use. * * @return mixed[] The models, or an empty array if no models are found. * @@ -710,9 +710,9 @@ public static function get_all_by( string $column, $value, string $operator = '= * @since 3.1.4 Enabled unfolding the value if is an array. * @since 3.1.4 Added the $operator parameter. * - * @param string $column The column to get the model by. - * @param mixed $value The value to get the model by. - * @param string $operator The operator to use. + * @param string $column The column to get the model by. + * @param null|int|string|float|bool|DateTimeInterface|string[]|int[]|float[]|DateTimeInterface[] $value The value to get the model by. + * @param string $operator The operator to use. * * @return ?mixed The model, or `null` if no model is found. * @@ -940,11 +940,21 @@ public static function cast_value_based_on_type( string $type, $value ) { * * @since 3.1.4 * - * @param mixed $value The value to ensure is an array. + * @param null|string|int|float|bool|DateTimeInterface|string[]|int[]|float[]|DateTimeInterface[] $value The value to ensure is an array. * - * @return array The value as an array. + * @return array The value as an array. */ private static function ensure_array( $value ): array { + if ( is_object( $value ) && ! $value instanceof DateTimeInterface ) { + throw new InvalidArgumentException( 'Value should not be an object.' ); + } + + if ( is_array( $value ) && $value ) { + foreach ( $value as $v ) { + self::ensure_array( $v ); + } + } + return is_array( $value ) ? $value : [ $value ]; } } From 1103b8dc3e501509557c0fb73f7902f6023a8312 Mon Sep 17 00:00:00 2001 From: Dimitrios Pantazis Date: Fri, 17 Oct 2025 17:39:42 +0300 Subject: [PATCH 12/15] amend value types --- src/Schema/Traits/Custom_Table_Query_Methods.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Schema/Traits/Custom_Table_Query_Methods.php b/src/Schema/Traits/Custom_Table_Query_Methods.php index 78e2583..23c1d61 100644 --- a/src/Schema/Traits/Custom_Table_Query_Methods.php +++ b/src/Schema/Traits/Custom_Table_Query_Methods.php @@ -945,13 +945,15 @@ public static function cast_value_based_on_type( string $type, $value ) { * @return array The value as an array. */ private static function ensure_array( $value ): array { - if ( is_object( $value ) && ! $value instanceof DateTimeInterface ) { - throw new InvalidArgumentException( 'Value should not be an object.' ); + if ( ! is_int( $value ) && ! is_string( $value ) && ! is_float( $value ) && ! is_bool( $value ) && ! $value instanceof DateTimeInterface && ! is_array( $value ) ) { + throw new InvalidArgumentException( 'Value should be an integer, string, float, boolean, DateTimeInterface, or array.' ); } if ( is_array( $value ) && $value ) { foreach ( $value as $v ) { - self::ensure_array( $v ); + if ( ! is_int( $v ) && ! is_string( $v ) && ! is_float( $v ) && ! is_bool( $v ) && ! $v instanceof DateTimeInterface ) { + throw new InvalidArgumentException( 'Offset of value should be an integer, string, float, boolean or DateTimeInterface.' ); + } } } From d01b131e3f12711685a7f6907791eeaec4fbcd7b Mon Sep 17 00:00:00 2001 From: Dimitrios Pantazis Date: Fri, 17 Oct 2025 17:41:04 +0300 Subject: [PATCH 13/15] include nulls --- src/Schema/Traits/Custom_Table_Query_Methods.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Schema/Traits/Custom_Table_Query_Methods.php b/src/Schema/Traits/Custom_Table_Query_Methods.php index 23c1d61..b43db10 100644 --- a/src/Schema/Traits/Custom_Table_Query_Methods.php +++ b/src/Schema/Traits/Custom_Table_Query_Methods.php @@ -945,13 +945,13 @@ public static function cast_value_based_on_type( string $type, $value ) { * @return array The value as an array. */ private static function ensure_array( $value ): array { - if ( ! is_int( $value ) && ! is_string( $value ) && ! is_float( $value ) && ! is_bool( $value ) && ! $value instanceof DateTimeInterface && ! is_array( $value ) ) { + if ( null !== $value && ! is_int( $value ) && ! is_string( $value ) && ! is_float( $value ) && ! is_bool( $value ) && ! $value instanceof DateTimeInterface && ! is_array( $value ) ) { throw new InvalidArgumentException( 'Value should be an integer, string, float, boolean, DateTimeInterface, or array.' ); } if ( is_array( $value ) && $value ) { foreach ( $value as $v ) { - if ( ! is_int( $v ) && ! is_string( $v ) && ! is_float( $v ) && ! is_bool( $v ) && ! $v instanceof DateTimeInterface ) { + if ( null !== $v && ! is_int( $v ) && ! is_string( $v ) && ! is_float( $v ) && ! is_bool( $v ) && ! $v instanceof DateTimeInterface ) { throw new InvalidArgumentException( 'Offset of value should be an integer, string, float, boolean or DateTimeInterface.' ); } } From 88069c981f487eae87ad8ed461b18f5f5ebeb3d0 Mon Sep 17 00:00:00 2001 From: Dimitrios Pantazis Date: Fri, 17 Oct 2025 17:45:23 +0300 Subject: [PATCH 14/15] cr amends --- src/Schema/Traits/Custom_Table_Query_Methods.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Schema/Traits/Custom_Table_Query_Methods.php b/src/Schema/Traits/Custom_Table_Query_Methods.php index b43db10..671f088 100644 --- a/src/Schema/Traits/Custom_Table_Query_Methods.php +++ b/src/Schema/Traits/Custom_Table_Query_Methods.php @@ -945,14 +945,14 @@ public static function cast_value_based_on_type( string $type, $value ) { * @return array The value as an array. */ private static function ensure_array( $value ): array { - if ( null !== $value && ! is_int( $value ) && ! is_string( $value ) && ! is_float( $value ) && ! is_bool( $value ) && ! $value instanceof DateTimeInterface && ! is_array( $value ) ) { + if ( is_object( $value ) && ! $value instanceof DateTimeInterface ) { throw new InvalidArgumentException( 'Value should be an integer, string, float, boolean, DateTimeInterface, or array.' ); } if ( is_array( $value ) && $value ) { - foreach ( $value as $v ) { + foreach ( $value as $key => $v ) { if ( null !== $v && ! is_int( $v ) && ! is_string( $v ) && ! is_float( $v ) && ! is_bool( $v ) && ! $v instanceof DateTimeInterface ) { - throw new InvalidArgumentException( 'Offset of value should be an integer, string, float, boolean or DateTimeInterface.' ); + throw new InvalidArgumentException( "Array element with offset '{$key}' should be an integer, string, float, boolean or DateTimeInterface." ); } } } From b3c191e59ca1edbaaeabdd1a033dfcfe2d262ff7 Mon Sep 17 00:00:00 2001 From: Dimitrios Pantazis Date: Fri, 17 Oct 2025 17:51:13 +0300 Subject: [PATCH 15/15] CR amends --- src/Schema/Traits/Custom_Table_Query_Methods.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Schema/Traits/Custom_Table_Query_Methods.php b/src/Schema/Traits/Custom_Table_Query_Methods.php index 671f088..095c065 100644 --- a/src/Schema/Traits/Custom_Table_Query_Methods.php +++ b/src/Schema/Traits/Custom_Table_Query_Methods.php @@ -945,14 +945,14 @@ public static function cast_value_based_on_type( string $type, $value ) { * @return array The value as an array. */ private static function ensure_array( $value ): array { - if ( is_object( $value ) && ! $value instanceof DateTimeInterface ) { + if ( null !== $value && ! is_int( $value ) && ! is_string( $value ) && ! is_float( $value ) && ! is_bool( $value ) && ! $value instanceof DateTimeInterface && ! is_array( $value ) ) { throw new InvalidArgumentException( 'Value should be an integer, string, float, boolean, DateTimeInterface, or array.' ); } if ( is_array( $value ) && $value ) { - foreach ( $value as $key => $v ) { + foreach ( $value as $k => $v ) { if ( null !== $v && ! is_int( $v ) && ! is_string( $v ) && ! is_float( $v ) && ! is_bool( $v ) && ! $v instanceof DateTimeInterface ) { - throw new InvalidArgumentException( "Array element with offset '{$key}' should be an integer, string, float, boolean or DateTimeInterface." ); + throw new InvalidArgumentException( 'Value with offset ' . $k . ' should be an integer, string, float, boolean or DateTimeInterface.' ); } } }