Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
61.54% covered (warning)
61.54%
32 / 52
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
WP_Post_Query_Abstract
61.54% covered (warning)
61.54%
32 / 52
0.00% covered (danger)
0.00%
0 / 4
15.69
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_post_type
n/a
0 / 0
n/a
0 / 0
0
 get_wp_post_fields
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_meta_input
n/a
0 / 0
n/a
0 / 0
0
 get_valid_keys
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
2
 to_query_array
94.12% covered (success)
94.12%
32 / 34
0.00% covered (danger)
0.00%
0 / 1
7.01
1<?php
2/**
3 * DTO to accept primitive data in the constructor, map those variable names to wp_post fields, to get an array for
4 * `get_post()` etc. queries.
5 *
6 * Enums are parsed to their backing value.
7 * Money uses {@see Money::jsonSerialize()} to store it as a readable JSON object.
8 *
9 * Extend this class to suit a specific post_type; use `::to_query_array()` in calls to `update_post()` etc.
10 *
11 * @package brianhenryie/bh-wp-bitcoin-gateway
12 */
13
14namespace BrianHenryIE\WP_Bitcoin_Gateway\API\Repositories\Queries;
15
16use BackedEnum;
17use BrianHenryIE\WP_Bitcoin_Gateway\Brick\Money\Money;
18use InvalidArgumentException;
19
20/**
21 * @phpstan-type WpUpdatePostArray array{ID?: int, post_type?:string, post_status?:string, post_author?: int, post_date?: string, post_date_gmt?: string, post_content?: string, post_content_filtered?: string, post_title?: string, post_excerpt?: string, meta_input?:array<string,mixed>}
22 */
23abstract readonly class WP_Post_Query_Abstract {
24
25    /**
26     * @see Bitcoin_Address_WP_Post_Interface::POST_TYPE
27     */
28    public string $post_type;
29
30    /**
31     * Override this with the pieces of data to be saved to the WP_Post.
32     */
33    public function __construct() {
34        $this->post_type = $this->get_post_type();
35    }
36
37    /**
38     * Set the `post_type` for the query.
39     */
40    abstract protected function get_post_type(): string;
41
42    /**
43     * Map data from object variable name to WP_Post field name.
44     *
45     * @see self::get_valid_keys()
46     *
47     * @return array<string,mixed> $map field_name : variable
48     */
49    protected function get_wp_post_fields(): array {
50        return array();
51    }
52
53    /**
54     * Map data from object variable name to post_meta key name.
55     *
56     * TODO: Document behavior: updates or appends?!
57     *
58     * @return array<string,mixed> meta_key : meta_value.
59     */
60    abstract protected function get_meta_input(): array;
61
62    /**
63     * @return string[] List of valid field in the WP_Query.
64     *
65     * @see wordpress/wp-admin/includes/schema.php:159
66     */
67    protected function get_valid_keys(): array {
68        return array(
69            'ID', // Indexed.
70            'post_date', // Indexed 3rd `(post_type,post_status,post_date...)`.
71            'post_type', // Indexed.
72            'post_name', // Indexed.
73            'post_content',
74            'post_excerpt',
75            'post_title',
76            'post_status', // Indexed 2nd `(post_type,post_status...)`.
77            'post_parent', // Indexed.
78            'numberposts',
79            'orderby',
80            'order',
81            'posts_per_page',
82            'meta_input',
83        );
84    }
85
86    /**
87     * TODO: need a convention for excluding fields that the caller knows aren't important/helpful.
88     *
89     * @return WpUpdatePostArray&array<string, mixed>
90     * @throws InvalidArgumentException When an unknown field is used.
91     */
92    public function to_query_array(): array {
93
94        // TODO: are the field names case sensitive?
95        $wp_post_fields = $this->get_wp_post_fields();
96
97        foreach ( array_keys( $wp_post_fields ) as $field_name ) {
98            if ( ! in_array( $field_name, $this->get_valid_keys(), true ) ) {
99                throw new InvalidArgumentException( 'Invalid key: ' . $field_name );
100            }
101        }
102
103        $wp_post_fields['post_type'] = $this->post_type;
104
105        $map_types_to_json = function ( $value ) {
106            if ( $value instanceof BackedEnum ) {
107                return $value->value;
108            }
109            if ( $value instanceof Money ) {
110                return wp_json_encode( $value->jsonSerialize() );
111            }
112            if ( is_array( $value ) ) {
113                return wp_json_encode( $value );
114            }
115            return $value;
116        };
117
118        $filter_null = fn( $value ) => ! is_null( $value );
119
120        /** @var WpUpdatePostArray $wp_post_fields */
121        $wp_post_fields = array_map(
122            $map_types_to_json,
123            $wp_post_fields,
124        );
125
126        $wp_post_fields['meta_input'] = array_map(
127            $map_types_to_json,
128            (array) $this->get_meta_input()
129        );
130
131        $wp_post_fields['meta_input'] = array_filter(
132            $wp_post_fields['meta_input'],
133            $filter_null
134        );
135
136        /** @var WpUpdatePostArray $wp_post_fields */
137        $wp_post_fields = array_filter(
138            $wp_post_fields,
139            $filter_null
140        );
141
142        if ( empty( $wp_post_fields['meta_input'] ) ) {
143            unset( $wp_post_fields['meta_input'] );
144        }
145
146        return $wp_post_fields;
147    }
148}