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

wp-graphql / wp-graphql / 19651653124

24 Nov 2025 10:39PM UTC coverage: 83.619% (+0.007%) from 83.612%
19651653124

push

github

actions-user
release: merge develop into master for v2.5.3

16243 of 19425 relevant lines covered (83.62%)

261.09 hits per line

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

94.84
/src/Data/Connection/PostObjectConnectionResolver.php
1
<?php
2

3
namespace WPGraphQL\Data\Connection;
4

5
use GraphQL\Error\InvariantViolation;
6
use GraphQL\Type\Definition\ResolveInfo;
7
use WPGraphQL\AppContext;
8
use WPGraphQL\Model\Post;
9
use WPGraphQL\Utils\Utils;
10

11
/**
12
 * Class PostObjectConnectionResolver
13
 *
14
 * @package WPGraphQL\Data\Connection
15
 * @extends \WPGraphQL\Data\Connection\AbstractConnectionResolver<\WP_Query>
16
 */
17
class PostObjectConnectionResolver extends AbstractConnectionResolver {
18

19
        /**
20
         * The name of the post type, or array of post types the connection resolver is resolving for
21
         *
22
         * @var mixed|string|string[]
23
         */
24
        protected $post_type;
25

26
        /**
27
         * {@inheritDoc}
28
         *
29
         * @param mixed|string|string[] $post_type The post type to resolve for
30
         */
31
        public function __construct( $source, array $args, AppContext $context, ResolveInfo $info, $post_type = 'any' ) {
198✔
32

33
                /**
34
                 * The $post_type can either be a single value or an array of post_types to
35
                 * pass to WP_Query.
36
                 *
37
                 * If the value is revision or attachment, we will leave the value
38
                 * as a string, as we validate against this later.
39
                 *
40
                 * If the value is anything else, we cast as an array. For example
41
                 *
42
                 * $post_type = 'post' would become [ 'post ' ], as we check later
43
                 * for `in_array()` if the $post_type is not "attachment" or "revision"
44
                 */
45
                if ( 'revision' === $post_type || 'attachment' === $post_type ) {
198✔
46
                        $this->post_type = $post_type;
27✔
47
                } elseif ( 'any' === $post_type ) {
177✔
48
                        $post_types      = \WPGraphQL::get_allowed_post_types();
35✔
49
                        $this->post_type = ! empty( $post_types ) ? array_values( $post_types ) : [];
35✔
50
                } else {
51
                        $post_type = is_array( $post_type ) ? $post_type : [ $post_type ];
158✔
52
                        unset( $post_type['attachment'] );
158✔
53
                        unset( $post_type['revision'] );
158✔
54
                        $this->post_type = $post_type;
158✔
55
                }
56

57
                /**
58
                 * Call the parent construct to setup class data
59
                 */
60
                parent::__construct( $source, $args, $context, $info );
198✔
61
        }
62

63
        /**
64
         * {@inheritDoc}
65
         */
66
        protected function loader_name(): string {
198✔
67
                return 'post';
198✔
68
        }
69

70
        /**
71
         * {@inheritDoc}
72
         */
73
        protected function query_class(): string {
198✔
74
                return \WP_Query::class;
198✔
75
        }
76

77
        /**
78
         * {@inheritDoc}
79
         *
80
         * @throws \GraphQL\Error\InvariantViolation If the query has been modified to suppress_filters.
81
         */
82
        protected function query( array $query_args ) {
195✔
83
                $query = parent::query( $query_args );
195✔
84

85
                if ( isset( $query->query_vars['suppress_filters'] ) && true === $query->query_vars['suppress_filters'] ) {
195✔
86
                        throw new InvariantViolation( esc_html__( 'WP_Query has been modified by a plugin or theme to suppress_filters, which will cause issues with WPGraphQL Execution. If you need to suppress filters for a specific reason within GraphQL, consider registering a custom field to the WPGraphQL Schema with a custom resolver.', 'wp-graphql' ) );
1✔
87
                }
88

89
                return $query;
194✔
90
        }
91

92
        /**
93
         * {@inheritDoc}
94
         */
95
        public function get_ids_from_query() {
194✔
96
                /**
97
                 * @todo This is for b/c. We can just use $this->get_query().
98
                 */
99
                $query = isset( $this->query ) ? $this->query : $this->get_query();
194✔
100

101
                /** @var int[] */
102
                $ids = ! empty( $query->posts ) ? $query->posts : [];
194✔
103

104
                // If we're going backwards, we need to reverse the array.
105
                $args = $this->get_args();
194✔
106

107
                if ( ! empty( $args['last'] ) ) {
194✔
108
                        $ids = array_reverse( $ids );
17✔
109
                }
110

111
                return $ids;
194✔
112
        }
113

114
        /**
115
         * {@inheritDoc}
116
         */
117
        public function should_execute() {
198✔
118
                /**
119
                 * If the post_type is not revision we can just return the parent::should_execute().
120
                 *
121
                 * @todo This works because AbstractConnectionResolver::pre_should_execute does a permission check on the `Post` model )
122
                 */
123
                if ( ! isset( $this->post_type ) || 'revision' !== $this->post_type ) {
198✔
124
                        return parent::should_execute();
193✔
125
                }
126

127
                // If the connection is from the RootQuery (i.e. it doesn't have a `Post` source), check if the user has the 'edit_posts' capability.
128
                if ( ! $this->source instanceof Post && current_user_can( 'edit_posts' ) ) {
15✔
129
                        return true;
1✔
130
                }
131

132
                // For revisions, we only want to execute the connection query if the user has access to edit the parent post.
133
                if ( $this->source instanceof Post && isset( $this->source->post_type ) ) {
14✔
134
                        $parent_post_type_obj = get_post_type_object( $this->source->post_type );
13✔
135

136
                        if ( isset( $parent_post_type_obj->cap->edit_post ) && current_user_can( $parent_post_type_obj->cap->edit_post, $this->source->databaseId ) ) {
13✔
137
                                return true;
12✔
138
                        }
139
                }
140

141
                return false;
2✔
142
        }
143

144
        /**
145
         * {@inheritDoc}
146
         */
147
        protected function prepare_query_args( array $args ): array {
198✔
148
                /**
149
                 * Prepare for later use
150
                 */
151
                $last = ! empty( $args['last'] ) ? $args['last'] : null;
198✔
152

153
                $query_args = [];
198✔
154
                /**
155
                 * Ignore sticky posts by default
156
                 */
157
                $query_args['ignore_sticky_posts'] = true;
198✔
158

159
                /**
160
                 * Set the post_type for the query based on the type of post being queried
161
                 */
162
                $query_args['post_type'] = ! empty( $this->post_type ) ? $this->post_type : 'post';
198✔
163

164
                /**
165
                 * Don't calculate the total rows, it's not needed and can be expensive
166
                 */
167
                $query_args['no_found_rows'] = true;
198✔
168

169
                /**
170
                 * Set the post_status to "publish" by default
171
                 */
172
                $query_args['post_status'] = 'publish';
198✔
173

174
                /**
175
                 * Set posts_per_page the highest value of $first and $last, with a (filterable) max of 100
176
                 */
177
                $query_args['posts_per_page'] = $this->one_to_one ? 1 : $this->get_query_amount() + 1;
198✔
178

179
                // set the graphql cursor args
180
                $query_args['graphql_cursor_compare'] = ! empty( $last ) ? '>' : '<';
198✔
181
                $query_args['graphql_after_cursor']   = $this->get_after_offset();
198✔
182
                $query_args['graphql_before_cursor']  = $this->get_before_offset();
198✔
183

184
                /**
185
                 * If the cursor offsets not empty,
186
                 * ignore sticky posts on the query
187
                 */
188
                if ( ! empty( $this->get_after_offset() ) || ! empty( $this->get_after_offset() ) ) {
198✔
189
                        $query_args['ignore_sticky_posts'] = true;
28✔
190
                }
191

192
                /**
193
                 * Pass the graphql $args to the WP_Query
194
                 */
195
                $query_args['graphql_args'] = $args;
198✔
196

197
                /**
198
                 * Collect the input_fields and sanitize them to prepare them for sending to the WP_Query
199
                 */
200
                $input_fields = [];
198✔
201
                if ( ! empty( $args['where'] ) ) {
198✔
202
                        $input_fields = $this->sanitize_input_fields( $args['where'] );
60✔
203
                }
204

205
                /**
206
                 * If the post_type is "attachment", use 'any' status to catch all attachment statuses.
207
                 *
208
                 * Most attachments have 'inherit' status (default), but plugins may change
209
                 * attachment status to 'publish' or other statuses. Using 'any' ensures we catch
210
                 * attachments regardless of their status. The Model's is_private() method will handle
211
                 * privacy checks based on the attachment's parent and status.
212
                 *
213
                 * For revisions, keep the default 'inherit' status.
214
                 */
215
                if ( 'attachment' === $this->post_type ) {
198✔
216
                        $query_args['post_status'] = 'any';
18✔
217
                } elseif ( 'revision' === $this->post_type ) {
188✔
218
                        $query_args['post_status'] = 'inherit';
15✔
219
                }
220

221
                /**
222
                 * Unset the "post_parent" for attachments, as we don't really care if they
223
                 * have a post_parent set by default
224
                 */
225
                if ( 'attachment' === $this->post_type && isset( $input_fields['parent'] ) ) {
198✔
226
                        unset( $input_fields['parent'] );
×
227
                }
228

229
                /**
230
                 * Merge the input_fields with the default query_args
231
                 */
232
                if ( ! empty( $input_fields ) ) {
198✔
233
                        $query_args = array_merge( $query_args, $input_fields );
60✔
234
                }
235

236
                /**
237
                 * If the query is a search, the source is not another Post, and the parent input $arg is not
238
                 * explicitly set in the query, unset the $query_args['post_parent'] so the search
239
                 * can search all posts, not just top level posts.
240
                 */
241
                if ( ! $this->source instanceof \WP_Post && isset( $query_args['search'] ) && ! isset( $input_fields['parent'] ) ) {
198✔
242
                        unset( $query_args['post_parent'] );
×
243
                }
244

245
                /**
246
                 * If the query contains search default the results to
247
                 */
248
                if ( isset( $query_args['search'] ) && ! empty( $query_args['search'] ) ) {
198✔
249
                        /**
250
                         * Don't order search results by title (causes funky issues with cursors)
251
                         */
252
                        $query_args['search_orderby_title'] = false;
×
253
                        $query_args['orderby']              = 'date';
×
254
                        $query_args['order']                = isset( $last ) ? 'ASC' : 'DESC';
×
255
                }
256

257
                if ( empty( $args['where']['orderby'] ) && ! empty( $query_args['post__in'] ) ) {
198✔
258
                        $post_in = $query_args['post__in'];
16✔
259
                        // Make sure the IDs are integers
260
                        $post_in = array_map(
16✔
261
                                static function ( $id ) {
16✔
262
                                        return absint( $id );
16✔
263
                                },
16✔
264
                                $post_in
16✔
265
                        );
16✔
266

267
                        // If we're coming backwards, let's reverse the IDs
268
                        if ( ! empty( $args['last'] ) || ! empty( $args['before'] ) ) {
16✔
269
                                $post_in = array_reverse( $post_in );
4✔
270
                        }
271

272
                        $cursor_offset = $this->get_offset_for_cursor( $args['after'] ?? ( $args['before'] ?? 0 ) );
16✔
273

274
                        if ( ! empty( $cursor_offset ) ) {
16✔
275
                                // Determine if the offset is in the array
276
                                $key = array_search( $cursor_offset, $post_in, true );
4✔
277

278
                                // If the offset is in the array
279
                                if ( false !== $key ) {
4✔
280
                                        $key     = absint( $key );
4✔
281
                                        $post_in = array_slice( $post_in, $key + 1, null, true );
4✔
282
                                }
283
                        }
284

285
                        $query_args['post__in'] = $post_in;
16✔
286
                        $query_args['orderby']  = 'post__in';
16✔
287
                        $query_args['order']    = isset( $last ) ? 'ASC' : 'DESC';
16✔
288
                }
289

290
                /**
291
                 * Map the orderby inputArgs to the WP_Query
292
                 */
293
                if ( isset( $args['where']['orderby'] ) && is_array( $args['where']['orderby'] ) ) {
198✔
294
                        $query_args['orderby'] = [];
4✔
295

296
                        foreach ( $args['where']['orderby'] as $orderby_input ) {
4✔
297
                                // Create a type hint for orderby_input. This is an array with a field and order key.
298
                                /** @var array<string,string> $orderby_input */
299
                                if ( empty( $orderby_input['field'] ) ) {
4✔
300
                                        continue;
×
301
                                }
302

303
                                /**
304
                                 * These orderby options should not include the order parameter.
305
                                 */
306
                                if ( in_array(
4✔
307
                                        $orderby_input['field'],
4✔
308
                                        [
4✔
309
                                                'post__in',
4✔
310
                                                'post_name__in',
4✔
311
                                                'post_parent__in',
4✔
312
                                        ],
4✔
313
                                        true
4✔
314
                                ) ) {
4✔
315
                                        $query_args['orderby'] = esc_sql( $orderby_input['field'] );
×
316

317
                                        // If we're ordering explicitly, there's no reason to check other orderby inputs.
318
                                        break;
×
319
                                }
320

321
                                $order = $orderby_input['order'];
4✔
322

323
                                if ( isset( $query_args['graphql_args']['last'] ) && ! empty( $query_args['graphql_args']['last'] ) ) {
4✔
324
                                        if ( 'ASC' === $order ) {
3✔
325
                                                $order = 'DESC';
1✔
326
                                        } else {
327
                                                $order = 'ASC';
3✔
328
                                        }
329
                                }
330

331
                                $query_args['orderby'][ esc_sql( $orderby_input['field'] ) ] = esc_sql( $order );
4✔
332
                        }
333
                }
334

335
                /**
336
                 * Convert meta_value_num to separate meta_value value field which our
337
                 * graphql_wp_term_query_cursor_pagination_support knowns how to handle
338
                 */
339
                if ( isset( $query_args['orderby'] ) && 'meta_value_num' === $query_args['orderby'] ) {
198✔
340
                        $query_args['orderby'] = [
1✔
341
                                'meta_value' => empty( $query_args['order'] ) ? 'DESC' : $query_args['order'], // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
1✔
342
                        ];
1✔
343
                        unset( $query_args['order'] );
1✔
344
                        $query_args['meta_type'] = 'NUMERIC';
1✔
345
                }
346

347
                /**
348
                 * If there's no orderby params in the inputArgs, set order based on the first/last argument
349
                 */
350
                if ( empty( $query_args['orderby'] ) ) {
198✔
351
                        $query_args['order'] = ! empty( $last ) ? 'ASC' : 'DESC';
169✔
352
                }
353

354
                /**
355
                 * NOTE: Only IDs should be queried here as the Deferred resolution will handle
356
                 * fetching the full objects, either from cache of from a follow-up query to the DB
357
                 */
358
                $query_args['fields'] = 'ids';
198✔
359

360
                /**
361
                 * Filter the $query args to allow folks to customize queries programmatically
362
                 *
363
                 * @param array<string,mixed>                  $query_args The args that will be passed to the WP_Query
364
                 * @param mixed                                $source     The source that's passed down the GraphQL queries
365
                 * @param array<string,mixed>                  $args       The inputArgs on the field
366
                 * @param \WPGraphQL\AppContext                $context    The AppContext passed down the GraphQL tree
367
                 * @param \GraphQL\Type\Definition\ResolveInfo $info       The ResolveInfo passed down the GraphQL tree
368
                 */
369
                return apply_filters( 'graphql_post_object_connection_query_args', $query_args, $this->source, $args, $this->context, $this->info );
198✔
370
        }
371

372
        /**
373
         * This sets up the "allowed" args, and translates the GraphQL-friendly keys to WP_Query
374
         * friendly keys. There's probably a cleaner/more dynamic way to approach this, but
375
         * this was quick. I'd be down to explore more dynamic ways to map this, but for
376
         * now this gets the job done.
377
         *
378
         * @param array<string,mixed> $where_args The args passed to the connection
379
         *
380
         * @return array<string,mixed>
381
         * @since  0.0.5
382
         */
383
        public function sanitize_input_fields( array $where_args ) {
60✔
384
                $arg_mapping = [
60✔
385
                        'authorIn'      => 'author__in',
60✔
386
                        'authorName'    => 'author_name',
60✔
387
                        'authorNotIn'   => 'author__not_in',
60✔
388
                        'categoryId'    => 'cat',
60✔
389
                        'categoryIn'    => 'category__in',
60✔
390
                        'categoryName'  => 'category_name',
60✔
391
                        'categoryNotIn' => 'category__not_in',
60✔
392
                        'contentTypes'  => 'post_type',
60✔
393
                        'dateQuery'     => 'date_query',
60✔
394
                        'hasPassword'   => 'has_password',
60✔
395
                        'id'            => 'p',
60✔
396
                        'in'            => 'post__in',
60✔
397
                        'mimeType'      => 'post_mime_type',
60✔
398
                        'nameIn'        => 'post_name__in',
60✔
399
                        'notIn'         => 'post__not_in',
60✔
400
                        'parent'        => 'post_parent',
60✔
401
                        'parentIn'      => 'post_parent__in',
60✔
402
                        'parentNotIn'   => 'post_parent__not_in',
60✔
403
                        'password'      => 'post_password',
60✔
404
                        'search'        => 's',
60✔
405
                        'stati'         => 'post_status',
60✔
406
                        'status'        => 'post_status',
60✔
407
                        'tagId'         => 'tag_id',
60✔
408
                        'tagIds'        => 'tag__and',
60✔
409
                        'tagIn'         => 'tag__in',
60✔
410
                        'tagNotIn'      => 'tag__not_in',
60✔
411
                        'tagSlugAnd'    => 'tag_slug__and',
60✔
412
                        'tagSlugIn'     => 'tag_slug__in',
60✔
413
                ];
60✔
414

415
                /**
416
                 * Map and sanitize the input args to the WP_Query compatible args
417
                 */
418
                $query_args = Utils::map_input( $where_args, $arg_mapping );
60✔
419

420
                if ( ! empty( $query_args['post_status'] ) ) {
60✔
421
                        $allowed_stati             = $this->sanitize_post_stati( $query_args['post_status'] );
8✔
422
                        $query_args['post_status'] = ! empty( $allowed_stati ) ? $allowed_stati : [ 'publish' ];
8✔
423
                }
424

425
                /**
426
                 * Filter the input fields
427
                 * This allows plugins/themes to hook in and alter what $args should be allowed to be passed
428
                 * from a GraphQL Query to the WP_Query
429
                 *
430
                 * @param array<string,mixed>                  $query_args The mapped query arguments
431
                 * @param array<string,mixed>                  $args       Query "where" args
432
                 * @param mixed                                $source     The query results for a query calling this
433
                 * @param array<string,mixed>                  $all_args   All of the arguments for the query (not just the "where" args)
434
                 * @param \WPGraphQL\AppContext                $context    The AppContext object
435
                 * @param \GraphQL\Type\Definition\ResolveInfo $info       The ResolveInfo object
436
                 * @param mixed|string|string[]                $post_type  The post type for the query
437
                 *
438
                 * @since 0.0.5
439
                 */
440
                $query_args = apply_filters( 'graphql_map_input_fields_to_wp_query', $query_args, $where_args, $this->source, $this->get_args(), $this->context, $this->info, $this->post_type );
60✔
441

442
                /**
443
                 * Return the Query Args
444
                 */
445
                return ! empty( $query_args ) && is_array( $query_args ) ? $query_args : [];
60✔
446
        }
447

448
        /**
449
         * Limit the status of posts a user can query.
450
         *
451
         * By default, published posts are public, and other statuses require permission to access.
452
         *
453
         * This strips the status from the query_args if the user doesn't have permission to query for
454
         * posts of that status.
455
         *
456
         * @param string[]|string $stati The status(es) to sanitize.
457
         *
458
         * @return string[]|null
459
         */
460
        public function sanitize_post_stati( $stati ) {
8✔
461
                /**
462
                 * If no stati is explicitly set by the input, default to publish. This will be the
463
                 * most common scenario.
464
                 */
465
                if ( empty( $stati ) ) {
8✔
466
                        $stati = [ 'publish' ];
×
467
                }
468

469
                /**
470
                 * Parse the list of stati
471
                 */
472
                $statuses = wp_parse_slug_list( $stati );
8✔
473

474
                /**
475
                 * Get the Post Type object
476
                 */
477
                $post_type_objects = [];
8✔
478
                if ( is_array( $this->post_type ) ) {
8✔
479
                        foreach ( $this->post_type as $post_type ) {
8✔
480
                                $post_type_objects[] = get_post_type_object( $post_type );
8✔
481
                        }
482
                } else {
483
                        $post_type_objects[] = get_post_type_object( $this->post_type );
×
484
                }
485

486
                /**
487
                 * Make sure the statuses are allowed to be queried by the current user. If so, allow it,
488
                 * otherwise return null, effectively removing it from the $allowed_statuses that will
489
                 * be passed to WP_Query
490
                 */
491
                $allowed_statuses = array_filter(
8✔
492
                        array_map(
8✔
493
                                static function ( $status ) use ( $post_type_objects ) {
8✔
494
                                        foreach ( $post_type_objects as $post_type_object ) {
8✔
495
                                                if ( 'publish' === $status ) {
8✔
496
                                                        return $status;
8✔
497
                                                }
498

499
                                                if ( 'private' === $status && ( ! isset( $post_type_object->cap->read_private_posts ) || ! current_user_can( $post_type_object->cap->read_private_posts ) ) ) {
7✔
500
                                                        return null;
2✔
501
                                                }
502

503
                                                if ( ! isset( $post_type_object->cap->edit_posts ) || ! current_user_can( $post_type_object->cap->edit_posts ) ) {
5✔
504
                                                        return null;
2✔
505
                                                }
506

507
                                                return $status;
3✔
508
                                        }
509
                                },
8✔
510
                                $statuses
8✔
511
                        )
8✔
512
                );
8✔
513

514
                /**
515
                 * If there are no allowed statuses to pass to WP_Query, prevent the connection
516
                 * from executing
517
                 *
518
                 * For example, if a subscriber tries to query:
519
                 *
520
                 * {
521
                 *   posts( where: { stati: [ DRAFT ] } ) {
522
                 *     ...fields
523
                 *   }
524
                 * }
525
                 *
526
                 * We can safely prevent the execution of the query because they are asking for content
527
                 * in a status that we know they can't ask for.
528
                 */
529
                if ( empty( $allowed_statuses ) ) {
8✔
530
                        $this->should_execute = false;
×
531
                }
532

533
                /**
534
                 * Return the $allowed_statuses to the query args
535
                 */
536
                return $allowed_statuses;
8✔
537
        }
538

539
        /**
540
         * {@inheritDoc}
541
         */
542
        protected function prepare_args( array $args ): array {
192✔
543
                if ( ! empty( $args['where'] ) ) {
192✔
544
                        // Ensure all IDs are converted to database IDs.
545
                        foreach ( $args['where'] as $input_key => $input_value ) {
49✔
546
                                if ( empty( $input_value ) ) {
49✔
547
                                        continue;
1✔
548
                                }
549

550
                                switch ( $input_key ) {
551
                                        case 'in':
49✔
552
                                        case 'notIn':
41✔
553
                                        case 'parent':
41✔
554
                                        case 'parentIn':
41✔
555
                                        case 'parentNotIn':
41✔
556
                                        case 'authorIn':
40✔
557
                                        case 'authorNotIn':
40✔
558
                                        case 'categoryIn':
40✔
559
                                        case 'categoryNotIn':
40✔
560
                                        case 'tagId':
40✔
561
                                        case 'tagIn':
40✔
562
                                        case 'tagNotIn':
40✔
563
                                                if ( is_array( $input_value ) ) {
20✔
564
                                                        $args['where'][ $input_key ] = array_map(
20✔
565
                                                                static function ( $id ) {
20✔
566
                                                                        return Utils::get_database_id_from_id( $id );
20✔
567
                                                                },
20✔
568
                                                                $input_value
20✔
569
                                                        );
20✔
570
                                                        break;
20✔
571
                                                }
572

573
                                                $args['where'][ $input_key ] = Utils::get_database_id_from_id( $input_value );
2✔
574
                                                break;
2✔
575
                                }
576
                        }
577
                }
578

579
                /**
580
                 * Filters the GraphQL args before they are used in get_query_args().
581
                 *
582
                 * @param array<string,mixed> $args            The GraphQL args passed to the resolver.
583
                 * @param self                $resolver        Instance of the ConnectionResolver.
584
                 * @param array<string,mixed> $unfiltered_args Array of arguments input in the field as part of the GraphQL query.
585
                 *
586
                 * @since 1.11.0
587
                 */
588
                return apply_filters( 'graphql_post_object_connection_args', $args, $this, $this->get_unfiltered_args() );
192✔
589
        }
590

591
        /**
592
         * {@inheritDoc}
593
         *
594
         * @param int $offset The ID of the node used in the cursor offset.
595
         */
596
        public function is_valid_offset( $offset ) {
34✔
597
                return (bool) get_post( absint( $offset ) );
34✔
598
        }
599
}
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