diff --git a/src/mantle/database/model/class-post.php b/src/mantle/database/model/class-post.php index f6d72c87..6784eb50 100644 --- a/src/mantle/database/model/class-post.php +++ b/src/mantle/database/model/class-post.php @@ -65,7 +65,7 @@ * @method static \Mantle\Database\Query\Post_Query_Builder whereTerm( array|\WP_Term|\Mantle\Database\Model\Term|int $term, ?string $taxonomy = null, string $operator = 'IN', string $field = 'term_id' ) * @method static \Mantle\Database\Query\Post_Query_Builder andWhereTerm( array|\WP_Term|\Mantle\Database\Model\Term|int $term, ?string $taxonomy = null, string $operator = 'IN', string $field = 'term_id' ) * @method static \Mantle\Database\Query\Post_Query_Builder orWhereTerm( array|\WP_Term|\Mantle\Database\Model\Term|int $term, ?string $taxonomy = null, string $operator = 'IN', string $field = 'term_id' ) - * @method static \Mantle\Database\Query\Post_Query_Builder whereMeta( string $key, mixed $value, string $operator = '=' ) + * @method static \Mantle\Database\Query\Post_Query_Builder whereMeta( string|\BackedEnum $key, mixed $value, string $operator = '=' ) * @method static \Mantle\Database\Query\Post_Query_Builder whereRaw( array|string $column, ?string $operator = null, mixed $value = null, string $boolean = 'AND' ) * @method static \Mantle\Database\Query\Post_Query_Builder where_raw( array|string $column, ?string $operator = null, mixed $value = null, string $boolean = 'AND' ) * @method static \Mantle\Database\Query\Post_Query_Builder orWhereRaw( array|string $column, ?string $operator = null, mixed $value = null, string $boolean = 'AND' ) diff --git a/src/mantle/database/model/meta/trait-model-meta.php b/src/mantle/database/model/meta/trait-model-meta.php index 59ef97ca..774f2e0a 100644 --- a/src/mantle/database/model/meta/trait-model-meta.php +++ b/src/mantle/database/model/meta/trait-model-meta.php @@ -7,6 +7,7 @@ namespace Mantle\Database\Model\Meta; +use BackedEnum; use Mantle\Database\Model\Model_Exception; /** @@ -47,7 +48,7 @@ public function add_meta( string $meta_key, mixed $meta_value, mixed $prev_value return; } - \add_metadata( $this->get_meta_type(), $this->id(), $meta_key, $meta_value ); + \add_metadata( $this->get_meta_type(), $this->id(), $meta_key, $this->serialize_value_for_storage( $meta_value ) ); } /** @@ -63,7 +64,7 @@ public function set_meta( string $meta_key, mixed $meta_value, mixed $prev_value return; } - \update_metadata( $this->get_meta_type(), $this->id(), $meta_key, $meta_value, $prev_value ); + \update_metadata( $this->get_meta_type(), $this->id(), $meta_key, $this->serialize_value_for_storage( $meta_value ), $prev_value ); } /** @@ -73,7 +74,7 @@ public function set_meta( string $meta_key, mixed $meta_value, mixed $prev_value * @param mixed $meta_value Previous meta value to delete. */ public function delete_meta( string $meta_key, mixed $meta_value = '' ): void { - \delete_metadata( $this->get_meta_type(), $this->id(), $meta_key, $meta_value ); + \delete_metadata( $this->get_meta_type(), $this->id(), $meta_key, $this->serialize_value_for_storage( $meta_value ) ); } /** @@ -141,4 +142,18 @@ public function store_queued_meta(): void { $this->queued_meta = []; } + + /** + * Serialize meta value for storage, converting all backed enums to their value. + * + * @param mixed $value Value to serialize. + * @return mixed + */ + protected function serialize_value_for_storage( mixed $value ): mixed { + if ( $value instanceof BackedEnum ) { + return $value->value; + } + + return $value; + } } diff --git a/src/mantle/database/query/class-builder.php b/src/mantle/database/query/class-builder.php index 969ad838..5775ba38 100644 --- a/src/mantle/database/query/class-builder.php +++ b/src/mantle/database/query/class-builder.php @@ -11,6 +11,7 @@ namespace Mantle\Database\Query; +use BackedEnum; use Closure; use Mantle\Container\Container; use Mantle\Contracts\Database\Scope; @@ -348,12 +349,20 @@ protected function resolve_attribute( string $attribute ): string { /** * Query by a meta field. * - * @param string $key Meta key. - * @param mixed $value Meta value. - * @param string $compare Comparison method, defaults to '='. + * @param string|\BackedEnum $key Meta key. + * @param mixed $value Meta value. + * @param string $compare Comparison method, defaults to '='. * @return static */ public function whereMeta( $key, $value, string $compare = '=' ) { + if ( $key instanceof BackedEnum ) { + $key = $key->value; + } + + if ( $value instanceof BackedEnum ) { + $value = $value->value; + } + $meta_query = [ 'compare' => $compare, 'key' => $key, @@ -366,6 +375,7 @@ public function whereMeta( $key, $value, string $compare = '=' ) { } $this->meta_query[] = $meta_query; + return $this; } @@ -379,6 +389,7 @@ public function whereMeta( $key, $value, string $compare = '=' ) { */ public function andWhereMeta( ...$args ) { $this->meta_query['relation'] = 'AND'; + return $this->whereMeta( ...$args ); } @@ -392,6 +403,7 @@ public function andWhereMeta( ...$args ) { */ public function orWhereMeta( ...$args ) { $this->meta_query['relation'] = 'OR'; + return $this->whereMeta( ...$args ); } diff --git a/tests/Database/Query/PostQueryBuilderTest.php b/tests/Database/Query/PostQueryBuilderTest.php index 34dd296a..759c8711 100644 --- a/tests/Database/Query/PostQueryBuilderTest.php +++ b/tests/Database/Query/PostQueryBuilderTest.php @@ -673,6 +673,46 @@ protected static function get_random_post_id( $args = [] ): int { array_pop( $post_ids ); return $post_ids[ array_rand( $post_ids ) ]; } + + public function test_query_by_enum() { + // Check if enum is supported. + if ( PHP_VERSION_ID < 80100 ) { + $this->markTestSkipped( 'PHP 8.1+ is required for this test.' ); + } + + $post = static::factory()->post + ->with_meta( [ + 'example-meta' => Testable_Meta_Values::Meta_Value_A, + Testable_Meta_Values::Meta_Key_A->value => Testable_Meta_Values::Meta_Value_A, + Testable_Meta_Values::Meta_Key_B->value => Testable_Meta_Values::Meta_Value_B, + ] ) + ->as_models() + ->create_and_get(); + + $this->assertEquals( + Testable_Meta_Values::Meta_Value_A->value, + $post->get_meta( 'example-meta' ), + ); + + $this->assertEquals( + Testable_Meta_Values::Meta_Value_B->value, + $post->get_meta( Testable_Meta_Values::Meta_Key_B->value ), + ); + + $this->assertEmpty( + Testable_Post::whereMeta( Testable_Meta_Values::Meta_Key_A->value, 'unknown' )->first(), + ); + + $this->assertEquals( + $post->id(), + Testable_Post::whereMeta( 'example-meta', Testable_Meta_Values::Meta_Value_A )->first()?->id, + ); + + $this->assertEquals( + $post->id(), + Testable_Post::whereMeta( Testable_Meta_Values::Meta_Key_A, Testable_Meta_Values::Meta_Value_A )->first()?->id, + ); + } } class Testable_Post extends Post { @@ -686,3 +726,12 @@ class Another_Testable_Post extends Post { class Testable_Tag extends Term { public static $object_name = 'post_tag'; } + +if ( PHP_VERSION_ID >= 80100 ) { + enum Testable_Meta_Values: string { + case Meta_Key_A = 'meta-key-a'; + case Meta_Key_B = 'meta-key-b'; + case Meta_Value_A = 'meta-value-a'; + case Meta_Value_B = 'meta-value-b'; + } +}