• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wp-graphql / wp-graphql / 17562196906

08 Sep 2025 07:44PM UTC coverage: 84.575% (+0.4%) from 84.17%
17562196906

push

github

web-flow
Merge pull request #3389 from wp-graphql/develop

release: next version 📦

238 of 308 new or added lines in 13 files covered. (77.27%)

6 existing lines in 6 files now uncovered.

15884 of 18781 relevant lines covered (84.57%)

261.69 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.7
/src/Data/Connection/TermObjectConnectionResolver.php
1
<?php
2

3
namespace WPGraphQL\Data\Connection;
4

5
use GraphQL\Type\Definition\ResolveInfo;
6
use WPGraphQL\AppContext;
7
use WPGraphQL\Utils\Utils;
8

9
/**
10
 * Class TermObjectConnectionResolver
11
 *
12
 * @package WPGraphQL\Data\Connection
13
 * @extends \WPGraphQL\Data\Connection\AbstractConnectionResolver<\WP_Term_Query>
14
 */
15
class TermObjectConnectionResolver extends AbstractConnectionResolver {
16
        /**
17
         * The name of the Taxonomy the resolver is intended to be used for
18
         *
19
         * @var array<string>|string
20
         */
21
        protected $taxonomy;
22

23
        /**
24
         * {@inheritDoc}
25
         *
26
         * @param mixed|array<string>|string|null $taxonomy The name of the Taxonomy the resolver is intended to be used for.
27
         */
28
        public function __construct( $source, array $args, AppContext $context, ResolveInfo $info, $taxonomy = null ) {
60✔
29
                $this->taxonomy = $taxonomy;
60✔
30
                parent::__construct( $source, $args, $context, $info );
60✔
31
        }
32

33
        /**
34
         * {@inheritDoc}
35
         */
36
        protected function prepare_query_args( array $args ): array {
60✔
37
                $all_taxonomies = \WPGraphQL::get_allowed_taxonomies();
60✔
38

39
                if ( ! is_array( $this->taxonomy ) ) {
60✔
40
                        $taxonomy = ! empty( $this->taxonomy ) && in_array( $this->taxonomy, $all_taxonomies, true ) ? [ $this->taxonomy ] : $all_taxonomies;
58✔
41
                } else {
42
                        $taxonomy = $this->taxonomy;
7✔
43
                }
44

45
                if ( ! empty( $args['where']['taxonomies'] ) ) {
60✔
46
                        /**
47
                         * Set the taxonomy for the $args
48
                         */
49
                        $requested_taxonomies = $args['where']['taxonomies'];
1✔
50
                        $taxonomy             = array_intersect( $all_taxonomies, $requested_taxonomies );
1✔
51
                }
52

53
                $query_args = [
60✔
54
                        'taxonomy' => $taxonomy,
60✔
55
                ];
60✔
56

57
                /**
58
                 * Prepare for later use
59
                 */
60
                $last = ! empty( $args['last'] ) ? $args['last'] : null;
60✔
61

62
                /**
63
                 * Set hide_empty as false by default
64
                 */
65
                $query_args['hide_empty'] = false;
60✔
66

67
                /**
68
                 * Set the number, ensuring it doesn't exceed the amount set as the $max_query_amount
69
                 */
70
                $query_args['number'] = $this->get_query_amount() + 1;
60✔
71

72
                /**
73
                 * Don't calculate the total rows, it's not needed and can be expensive
74
                 */
75
                $query_args['count'] = false;
60✔
76

77
                /**
78
                 * Take any of the $args that were part of the GraphQL query and map their
79
                 * GraphQL names to the WP_Term_Query names to be used in the WP_Term_Query
80
                 *
81
                 * @since 0.0.5
82
                 */
83
                $input_fields = [];
60✔
84
                if ( ! empty( $args['where'] ) ) {
60✔
85
                        $input_fields = $this->sanitize_input_fields();
11✔
86
                }
87

88
                /**
89
                 * Merge the default $query_args with the $args that were entered
90
                 * in the query.
91
                 *
92
                 * @since 0.0.5
93
                 */
94
                if ( ! empty( $input_fields ) ) {
60✔
95
                        $query_args = array_merge( $query_args, $input_fields );
11✔
96
                }
97

98
                $query_args['graphql_cursor_compare'] = ( ! empty( $last ) ) ? '>' : '<';
60✔
99
                $query_args['graphql_after_cursor']   = $this->get_after_offset();
60✔
100
                $query_args['graphql_before_cursor']  = $this->get_before_offset();
60✔
101

102
                /**
103
                 * Pass the graphql $args to the WP_Query
104
                 */
105
                $query_args['graphql_args'] = $args;
60✔
106

107
                /**
108
                 * NOTE: We query for JUST the IDs here as deferred resolution of the nodes gets the full
109
                 * object from the cache or a follow-up request for the full object if it's not cached.
110
                 */
111
                $query_args['fields'] = 'ids';
60✔
112

113
                /**
114
                 * If there's no orderby params in the inputArgs, default to ordering by name.
115
                 */
116
                if ( empty( $query_args['orderby'] ) ) {
60✔
117
                        $query_args['orderby'] = 'name';
58✔
118
                }
119

120
                /**
121
                 * If orderby params set but not order, default to ASC if going forward, DESC if going backward.
122
                 */
123
                if ( empty( $query_args['order'] ) && 'name' === $query_args['orderby'] ) {
60✔
124
                        $query_args['order'] = ! empty( $last ) ? 'DESC' : 'ASC';
58✔
125
                } elseif ( empty( $query_args['order'] ) ) {
2✔
126
                        $query_args['order'] = ! empty( $last ) ? 'ASC' : 'DESC';
×
127
                }
128

129
                /**
130
                 * Filters the query args used by the connection.
131
                 *
132
                 * @param array<string,mixed>                  $query_args array of query_args being passed to the
133
                 * @param mixed                                $source     source passed down from the resolve tree
134
                 * @param array<string,mixed>                  $args       array of arguments input in the field as part of the GraphQL query
135
                 * @param \WPGraphQL\AppContext                $context    object passed down the resolve tree
136
                 * @param \GraphQL\Type\Definition\ResolveInfo $info       info about fields passed down the resolve tree
137
                 *
138
                 * @since 0.0.6
139
                 */
140
                $query_args = apply_filters( 'graphql_term_object_connection_query_args', $query_args, $this->source, $args, $this->context, $this->info );
60✔
141

142
                return $query_args;
60✔
143
        }
144

145
        /**
146
         * {@inheritDoc}
147
         */
148
        protected function query_class(): string {
60✔
149
                return \WP_Term_Query::class;
60✔
150
        }
151

152
        /**
153
         * {@inheritDoc}
154
         */
155
        public function get_ids_from_query() {
60✔
156
                /**
157
                 * @todo This is for b/c. We can just use $this->get_query().
158
                 */
159
                $queried = isset( $this->query ) ? $this->query : $this->get_query();
60✔
160

161
                /** @var string[] $ids */
162
                $ids = ! empty( $queried->get_terms() ) ? $queried->get_terms() : [];
60✔
163

164
                // If we're going backwards, we need to reverse the array.
165
                $args = $this->get_args();
60✔
166
                if ( ! empty( $args['last'] ) ) {
60✔
167
                        $ids = array_reverse( $ids );
9✔
168
                }
169

170
                return $ids;
60✔
171
        }
172

173
        /**
174
         * {@inheritDoc}
175
         */
176
        protected function loader_name(): string {
60✔
177
                return 'term';
60✔
178
        }
179

180
        /**
181
         * This maps the GraphQL "friendly" args to get_terms $args.
182
         * There's probably a cleaner/more dynamic way to approach this, but this was quick. I'd be down
183
         * to explore more dynamic ways to map this, but for now this gets the job done.
184
         *
185
         * @since  0.0.5
186
         * @return array<string,mixed>
187
         */
188
        public function sanitize_input_fields() {
11✔
189
                $arg_mapping = [
11✔
190
                        'objectIds'           => 'object_ids',
11✔
191
                        'hideEmpty'           => 'hide_empty',
11✔
192
                        'excludeTree'         => 'exclude_tree',
11✔
193
                        // @todo remove in 3.0.0
194
                        'termTaxonomId'       => 'term_taxonomy_id',
11✔
195
                        'termTaxonomyId'      => 'term_taxonomy_id',
11✔
196
                        'nameLike'            => 'name__like',
11✔
197
                        'descriptionLike'     => 'description__like',
11✔
198
                        'padCounts'           => 'pad_counts',
11✔
199
                        'childOf'             => 'child_of',
11✔
200
                        'cacheDomain'         => 'cache_domain',
11✔
201
                        'updateTermMetaCache' => 'update_term_meta_cache',
11✔
202
                        'taxonomies'          => 'taxonomy',
11✔
203
                ];
11✔
204

205
                $args       = $this->get_args();
11✔
206
                $where_args = ! empty( $args['where'] ) ? $args['where'] : null;
11✔
207

208
                // Only convert value if 'termTaxonomyId' isnt already set.
209
                // @todo Remove in 3.0.0
210
                if ( ! empty( $where_args['termTaxonomId'] ) && empty( $where_args['termTaxonomyId'] ) ) {
11✔
NEW
211
                        $where_args['termTaxonomyId'] = $where_args['termTaxonomId'];
×
212
                }
213

214
                /**
215
                 * Map and sanitize the input args to the WP_Term_Query compatible args
216
                 */
217
                $query_args = Utils::map_input( $where_args, $arg_mapping );
11✔
218

219
                /**
220
                 * Filter the input fields
221
                 * This allows plugins/themes to hook in and alter what $args should be allowed to be passed
222
                 * from a GraphQL Query to the get_terms query
223
                 *
224
                 * @param array<string,mixed>                  $query_args Array of mapped query args
225
                 * @param array<string,mixed>                  $where_args Array of query "where" args
226
                 * @param array<string>|string                 $taxonomy   The name of the taxonomy
227
                 * @param mixed                                $source     The query results
228
                 * @param array<string,mixed>                  $all_args   All of the query arguments (not just the "where" args)
229
                 * @param \WPGraphQL\AppContext                $context   The AppContext object
230
                 * @param \GraphQL\Type\Definition\ResolveInfo $info      The ResolveInfo object
231
                 *
232
                 * @since 0.0.5
233
                 */
234
                $query_args = apply_filters( 'graphql_map_input_fields_to_get_terms', $query_args, $where_args, $this->taxonomy, $this->source, $args, $this->context, $this->info );
11✔
235

236
                return ! empty( $query_args ) && is_array( $query_args ) ? $query_args : [];
11✔
237
        }
238

239
        /**
240
         * {@inheritDoc}
241
         */
242
        protected function prepare_args( array $args ): array {
60✔
243
                if ( ! empty( $args['where'] ) ) {
60✔
244
                        // Ensure all IDs are converted to database IDs.
245
                        foreach ( $args['where'] as $input_key => $input_value ) {
11✔
246
                                if ( empty( $input_value ) ) {
11✔
247
                                        continue;
3✔
248
                                }
249

250
                                switch ( $input_key ) {
251
                                        case 'exclude':
9✔
252
                                        case 'excludeTree':
9✔
253
                                        case 'include':
9✔
254
                                        case 'objectIds':
9✔
255
                                        case 'termTaxonomId':
8✔
256
                                        case 'termTaxonomyId':
8✔
257
                                                if ( is_array( $input_value ) ) {
2✔
258
                                                        $args['where'][ $input_key ] = array_map(
2✔
259
                                                                static function ( $id ) {
2✔
260
                                                                        return Utils::get_database_id_from_id( $id );
2✔
261
                                                                },
2✔
262
                                                                $input_value
2✔
263
                                                        );
2✔
264
                                                        break;
2✔
265
                                                }
266

267
                                                $args['where'][ $input_key ] = Utils::get_database_id_from_id( $input_value );
×
268
                                                break;
×
269
                                }
270
                        }
271
                }
272

273
                /**
274
                 * Filters the GraphQL args before they are used in get_query_args().
275
                 *
276
                 * @param array<string,mixed> $args            The GraphQL args passed to the resolver.
277
                 * @param self                $resolver        Instance of the ConnectionResolver.
278
                 * @param array<string,mixed> $unfiltered_args Array of arguments input in the field as part of the GraphQL query.
279
                 *
280
                 * @since 1.11.0
281
                 */
282
                return apply_filters( 'graphql_term_object_connection_args', $args, $this, $this->get_unfiltered_args() );
60✔
283
        }
284

285
        /**
286
         * {@inheritDoc}
287
         *
288
         * @param int $offset The ID of the node used in the cursor for offset.
289
         */
290
        public function is_valid_offset( $offset ) {
9✔
291
                return get_term( absint( $offset ) ) instanceof \WP_Term;
9✔
292
        }
293
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc