PluginProbe
WPGraphQL / trunk
WPGraphQL vtrunk
2.22.3 2.22.2 2.22.1 2.22.0 2.21.1 2.21.0 2.20.0 2.19.0 2.18.0 2.17.0 2.16.0 2.15.1 2.15.0 2.14.1 2.14.0 2.13.0 2.2.0 2.3.0 2.3.3 2.3.6 2.3.8 2.5.0 2.5.1 2.5.2 2.5.3 All 177 releases
wp-graphql / src / Data / Cursor / UserCursor.php

UserCursor.php in WPGraphQL trunk, at src/Data/Cursor/UserCursor.php

267 lines 6.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WPGraphQL\Data\Cursor;
4
5 use WP_User_Query;
6
7 /**
8 * User Cursor
9 *
10 * This class generates the SQL AND operators for cursor based pagination for users
11 *
12 * @package WPGraphQL\Data\Cursor
13 */
14 class UserCursor extends AbstractCursor {
15
16 /**
17 * @var ?\WP_User
18 */
19 public $cursor_node;
20
21 /**
22 * Counter for meta value joins
23 *
24 * @var int
25 */
26 public $meta_join_alias = 0;
27
28 /**
29 * {@inheritDoc}
30 *
31 * @param array<string,mixed>|\WP_User_Query $query_vars The query vars to use when building the SQL statement.
32 */
33 public function __construct( $query_vars, $cursor = 'after' ) {
34 // @todo remove in 3.0.0
35 if ( $query_vars instanceof WP_User_Query ) {
36 _doing_it_wrong(
37 __METHOD__,
38 esc_html__( 'The first argument should be an array of $query_vars, not the WP_Query object. This will throw an error in the next major release', 'wp-graphql' ),
39 '1.9.0'
40 );
41 $query_vars = $query_vars->query_vars;
42 }
43
44 // Initialize the class properties.
45 parent::__construct( $query_vars, $cursor );
46
47 // Set ID key.
48 $this->id_key = "{$this->wpdb->users}.ID";
49 }
50
51 /**
52 * {@inheritDoc}
53 *
54 * Unlike most queries, users by default are in ascending order.
55 */
56 public function get_cursor_compare() {
57 if ( 'before' === $this->cursor ) {
58 return '<';
59 }
60 return '>';
61 }
62
63 /**
64 * {@inheritDoc}
65 *
66 * @return ?\WP_User
67 */
68 public function get_cursor_node() {
69 // Bail if no offset.
70 if ( ! $this->cursor_offset ) {
71 return null;
72 }
73
74 /**
75 * If pre-hooked, return filtered node.
76 *
77 * @param \WP_User|null $pre_user The pre-filtered user node.
78 * @param int $offset The cursor offset.
79 * @param \WPGraphQL\Data\Cursor\UserCursor $node The cursor instance.
80 *
81 * @hookGroup connections
82 * @since 0.0.5
83 *
84 * @return \WP_User|null
85 */
86 $pre_user = apply_filters( 'graphql_pre_user_cursor_node', null, $this->cursor_offset, $this );
87 if ( null !== $pre_user ) {
88 return $pre_user;
89 }
90
91 // Get cursor node.
92 $user = get_user_by( 'id', $this->cursor_offset );
93
94 return false !== $user ? $user : null;
95 }
96
97 /**
98 * {@inheritDoc}
99 */
100 public function to_sql() {
101 return ' AND ' . $this->builder->to_sql();
102 }
103
104 /**
105 * {@inheritDoc}
106 */
107 public function get_where() {
108 // If we have a bad cursor, just skip.
109 if ( ! $this->is_valid_offset_and_node() ) {
110 return '';
111 }
112
113 $orderby = $this->get_query_var( 'orderby' );
114 $order = $this->get_query_var( 'order' );
115
116 if ( ! empty( $orderby ) && is_array( $orderby ) ) {
117
118 /**
119 * Loop through all order keys if it is an array
120 */
121 foreach ( $orderby as $by => $order ) {
122 $this->compare_with( $by, $order );
123 }
124 } elseif ( ! empty( $orderby ) && is_string( $orderby ) && 'login' !== $orderby ) {
125
126 /**
127 * If $orderby is just a string just compare with it directly as DESC
128 */
129 $this->compare_with( $orderby, $order );
130 }
131
132 /**
133 * If there's no orderby specified yet, compare with the following fields.
134 */
135 if ( ! $this->builder->has_fields() ) {
136 $this->compare_with_cursor_fields(
137 [
138 [
139 'key' => "{$this->wpdb->users}.user_login",
140 'value' => $this->cursor_node ? $this->cursor_node->user_login : null,
141 'type' => 'CHAR',
142 ],
143 ]
144 );
145 }
146
147 $this->compare_with_id_field();
148
149 return $this->to_sql();
150 }
151
152 /**
153 * Get AND operator for given order by key
154 *
155 * @param string $by The order by key
156 * @param string $order The order direction ASC or DESC
157 */
158 private function compare_with( $by, $order ): void {
159 // Bail early, if "key" and "value" provided in query_vars.
160 $key = $this->get_query_var( "graphql_cursor_compare_by_{$by}_key" );
161 $value = $this->get_query_var( "graphql_cursor_compare_by_{$by}_value" );
162 if ( ! empty( $key ) && ! empty( $value ) ) {
163 $this->builder->add_field( $key, $value, null, $order );
164 return;
165 }
166
167 /**
168 * Find out whether this is a user field
169 */
170 $orderby_user_fields = [
171 'user_email',
172 'user_login',
173 'user_nicename',
174 'user_registered',
175 'user_url',
176 ];
177 if ( in_array( $by, $orderby_user_fields, true ) ) {
178 $key = "{$this->wpdb->users}.{$by}";
179 $value = $this->cursor_node->{$by} ?? null;
180 }
181
182 /**
183 * If key or value are null, check whether this is a meta key based ordering before bailing.
184 */
185 if ( null === $key || null === $value ) {
186 $meta_key = $this->get_meta_key( $by );
187 if ( $meta_key ) {
188 $this->compare_with_meta_field( $meta_key, $order );
189 }
190 return;
191 }
192
193 // Add field to build.
194 $this->builder->add_field( $key, $value, null, $order );
195 }
196
197 /**
198 * Compare with meta key field
199 *
200 * @param string $meta_key user meta key
201 * @param string $order The comparison string
202 */
203 private function compare_with_meta_field( string $meta_key, string $order ): void {
204 $meta_type = $this->get_query_var( 'meta_type' );
205 $meta_value = get_user_meta( $this->cursor_offset, $meta_key, true );
206
207 $key = "{$this->wpdb->usermeta}.meta_value";
208
209 /**
210 * WP uses mt1, mt2 etc. style aliases for additional meta value joins.
211 */
212 if ( 0 !== $this->meta_join_alias ) {
213 $key = "mt{$this->meta_join_alias}.meta_value";
214 }
215
216 ++$this->meta_join_alias;
217
218 $this->builder->add_field( $key, $meta_value, $meta_type, $order );
219 }
220
221 /**
222 * Get the actual meta key if any
223 *
224 * @param string $by The order by key
225 *
226 * @return string|null
227 */
228 private function get_meta_key( $by ) {
229 if ( 'meta_value' === $by ) {
230 return $this->get_query_var( 'meta_key' );
231 }
232
233 /**
234 * Check for the WP 4.2+ style meta clauses
235 * https://make.wordpress.org/core/2015/03/30/query-improvements-in-wp-4-2-orderby-and-meta_query/
236 */
237 if ( ! isset( $this->query_vars['meta_query'][ $by ] ) ) {
238 return null;
239 }
240
241 $clause = $this->query_vars['meta_query'][ $by ];
242
243 return empty( $clause['key'] ) ? null : $clause['key'];
244 }
245
246 /**
247 * @todo remove in 3.0.0
248 * @deprecated 1.9.0
249 * @codeCoverageIgnore
250 *
251 * @return ?\WP_User
252 */
253 public function get_cursor_user() {
254 _doing_it_wrong(
255 __METHOD__,
256 sprintf(
257 // translators: %s is the method name
258 esc_html__( 'This method will be removed in the next major release. Use %s instead.', 'wp-graphql' ),
259 self::class . '::get_cursor_node()'
260 ),
261 '1.9.0'
262 );
263
264 return $this->cursor_node;
265 }
266 }
267