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

wp-graphql / wp-graphql / 17334288077

29 Aug 2025 09:07PM UTC coverage: 84.593% (+0.4%) from 84.169%
17334288077

push

github

actions-user
chore: update changeset for PR #3410

15884 of 18777 relevant lines covered (84.59%)

260.51 hits per line

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

58.33
/src/Data/Cursor/TermObjectCursor.php
1
<?php
2
namespace WPGraphQL\Data\Cursor;
3

4
use WP_Term;
5

6
class TermObjectCursor extends AbstractCursor {
7

8
        /**
9
         * @var ?\WP_Term ;
10
         */
11
        public $cursor_node;
12

13
        /**
14
         * Counter for meta value joins
15
         *
16
         * @var int
17
         */
18
        public $meta_join_alias = 0;
19

20
        /**
21
         * {@inheritDoc}
22
         *
23
         * @var string
24
         */
25
        protected $id_key = 't.term_id';
26

27

28
        /**
29
         * {@inheritDoc}
30
         *
31
         * @return ?\WP_Term ;
32
         */
33
        public function get_cursor_node() {
60✔
34
                // Bail if no offset.
35
                if ( ! $this->cursor_offset ) {
60✔
36
                        return null;
60✔
37
                }
38

39
                /**
40
                 * If pre-hooked, return filtered node.
41
                 *
42
                 * @param \WP_Term|null                           $pre_term The pre-filtered term node.
43
                 * @param int                                     $offset   The cursor offset.
44
                 * @param \WPGraphQL\Data\Cursor\TermObjectCursor $node     The cursor instance.
45
                 *
46
                 * @return \WP_Term|null
47
                 */
48
                $pre_term = apply_filters( 'graphql_pre_term_cursor_node', null, $this->cursor_offset, $this );
9✔
49
                if ( null !== $pre_term ) {
9✔
50
                        return $pre_term;
×
51
                }
52

53
                // Get cursor node.
54
                $term = WP_Term::get_instance( $this->cursor_offset );
9✔
55

56
                return $term instanceof WP_Term ? $term : null;
9✔
57
        }
58

59
        /**
60
         * {@inheritDoc}
61
         *
62
         * @param array<array<string,mixed>>[]|null $fields The fields from the CursorBuilder to convert to SQL.
63
         */
64
        public function to_sql( $fields = null ) {
9✔
65
                $sql = $this->builder->to_sql( $fields );
9✔
66
                if ( empty( $sql ) ) {
9✔
67
                        return '';
×
68
                }
69
                return ' AND ' . $sql;
9✔
70
        }
71

72
        /**
73
         * {@inheritDoc}
74
         */
75
        public function get_where() {
9✔
76
                // If we have a bad cursor, just skip.
77
                if ( ! $this->is_valid_offset_and_node() ) {
9✔
78
                        return '';
×
79
                }
80

81
                $orderby = $this->get_query_var( 'orderby' );
9✔
82
                $order   = $this->get_query_var( 'order' );
9✔
83

84
                if ( 'name' === $orderby ) {
9✔
85
                        if ( '>' === $this->compare ) {
8✔
86
                                $order         = 'DESC';
6✔
87
                                $this->compare = '<';
6✔
88
                        } elseif ( '<' === $this->compare ) {
6✔
89
                                $this->compare = '>';
6✔
90
                                $order         = 'ASC';
6✔
91
                        }
92
                }
93

94
                /**
95
                 * If $orderby is just a string just compare with it directly as DESC
96
                 */
97
                if ( ! empty( $orderby ) && is_string( $orderby ) ) {
9✔
98
                        $this->compare_with( $orderby, $order );
9✔
99
                }
100

101
                /**
102
                 * If there's no orderby specified yet, compare with the following fields.
103
                 */
104
                if ( ! $this->builder->has_fields() ) {
9✔
105
                        $this->compare_with_cursor_fields();
×
106
                }
107

108
                /**
109
                 * Stabilize cursor by consistently comparing with the ID.
110
                 */
111
                $this->compare_with_id_field();
9✔
112

113
                return $this->to_sql();
9✔
114
        }
115

116
        /**
117
         * Get AND operator for given order by key
118
         *
119
         * @param string $by    The order by key
120
         * @param string $order The order direction ASC or DESC
121
         */
122
        private function compare_with( string $by, string $order ): void {
9✔
123

124
                // Bail early, if "key" and "value" provided in query_vars.
125
                $key   = $this->get_query_var( "graphql_cursor_compare_by_{$by}_key" );
9✔
126
                $value = $this->get_query_var( "graphql_cursor_compare_by_{$by}_value" );
9✔
127
                if ( ! empty( $key ) && ! empty( $value ) ) {
9✔
128
                        $this->builder->add_field( $key, $value, null, $order );
×
129
                        return;
×
130
                }
131

132
                // Set "key" as term table column and get "value" from cursor node.
133
                $key   = "t.{$by}";
9✔
134
                $value = $this->cursor_node->{$by};
9✔
135

136
                /**
137
                 * If key or value are null, check whether this is a meta key based ordering before bailing.
138
                 */
139
                if ( null === $value ) {
9✔
140
                        $meta_key = $this->get_meta_key( $by );
×
141
                        if ( $meta_key ) {
×
142
                                $this->compare_with_meta_field( $meta_key, $order );
×
143
                        }
144
                        return;
×
145
                }
146

147
                $this->builder->add_field( $key, $value, null, $order );
9✔
148
        }
149

150
        /**
151
         * Compare with meta key field
152
         *
153
         * @param string $meta_key meta key
154
         * @param string $order    The comparison string
155
         */
156
        private function compare_with_meta_field( string $meta_key, string $order ): void {
×
157
                $meta_type  = $this->get_query_var( 'meta_type' );
×
158
                $meta_value = get_term_meta( $this->cursor_offset, $meta_key, true );
×
159

160
                $key = "{$this->wpdb->termmeta}.meta_value";
×
161

162
                /**
163
                 * WP uses mt1, mt2 etc. style aliases for additional meta value joins.
164
                 */
165
                if ( 0 !== $this->meta_join_alias ) {
×
166
                        $key = "mt{$this->meta_join_alias}.meta_value";
×
167
                }
168

169
                ++$this->meta_join_alias;
×
170

171
                $this->builder->add_field( $key, $meta_value, $meta_type, $order, $this );
×
172
        }
173

174
        /**
175
         * Get the actual meta key if any
176
         *
177
         * @param string $by The order by key
178
         *
179
         * @return string|null
180
         */
181
        private function get_meta_key( string $by ) {
×
182
                if ( 'meta_value' === $by || 'meta_value_num' === $by ) {
×
183
                        return $this->get_query_var( 'meta_key' );
×
184
                }
185

186
                /**
187
                 * Check for the WP 4.2+ style meta clauses
188
                 * https://make.wordpress.org/core/2015/03/30/query-improvements-in-wp-4-2-orderby-and-meta_query/
189
                 */
190
                if ( ! isset( $this->query_vars['meta_query'][ $by ] ) ) {
×
191
                        return null;
×
192
                }
193

194
                $clause = $this->query_vars['meta_query'][ $by ];
×
195

196
                return empty( $clause['key'] ) ? null : $clause['key'];
×
197
        }
198

199
        /**
200
         * @todo remove in 3.0.0
201
         * @deprecated 1.9.0
202
         * @codeCoverageIgnore
203
         *
204
         * @param string $name The name of the query var to get
205
         * @return mixed|null
206
         */
207
        public function get_query_arg( string $name ) {
208
                _doing_it_wrong(
209
                        __METHOD__,
210
                        sprintf(
211
                                // translators: %s is the method name
212
                                esc_html__( 'This method will be removed in the next major release. Use %s instead.', 'wp-graphql' ),
213
                                self::class . '::get_query_var()'
214
                        ),
215
                        '1.9.0'
216
                );
217

218
                return $this->get_query_var( $name );
219
        }
220

221
        /**
222
         * @todo remove in 3.0.0
223
         * @deprecated 1.9.0
224
         * @codeCoverageIgnore
225
         *
226
         * @return ?\WP_Term
227
         */
228
        public function get_cursor_term() {
229
                _doing_it_wrong(
230
                        __METHOD__,
231
                        sprintf(
232
                                // translators: %s is the method name
233
                                esc_html__( 'This method will be removed in the next major release. Use %s instead.', 'wp-graphql' ),
234
                                self::class . '::get_cursor_node()'
235
                        ),
236
                        '1.9.0'
237
                );
238

239
                return $this->cursor_node;
240
        }
241
}
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

© 2026 Coveralls, Inc