-
Notifications
You must be signed in to change notification settings - Fork 442
/
CommentConnectionResolver.php
321 lines (284 loc) · 9.31 KB
/
CommentConnectionResolver.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
<?php
namespace WPGraphQL\Data\Connection;
use GraphQL\Error\UserError;
use WPGraphQL\Utils\Utils;
use WP_Comment_Query;
/**
* Class CommentConnectionResolver
*
* @package WPGraphQL\Data\Connection
*/
class CommentConnectionResolver extends AbstractConnectionResolver {
/**
* {@inheritDoc}
*
* @var \WP_Comment_Query
*/
protected $query;
/**
* {@inheritDoc}
*
* @throws \GraphQL\Error\UserError
*/
public function get_query_args() {
/**
* Prepare for later use
*/
$last = ! empty( $this->args['last'] ) ? $this->args['last'] : null;
$query_args = [];
/**
* Don't calculate the total rows, it's not needed and can be expensive
*/
$query_args['no_found_rows'] = true;
/**
* Set the default comment_status for Comment Queries to be "comment_approved"
*/
$query_args['status'] = 'approve';
/**
* Set the number, ensuring it doesn't exceed the amount set as the $max_query_amount
*
* @since 0.0.6
*/
$query_args['number'] = $this->get_query_amount() + 1;
/**
* Set the default order
*/
$query_args['orderby'] = 'comment_date';
/**
* Take any of the $this->args that were part of the GraphQL query and map their
* GraphQL names to the WP_Term_Query names to be used in the WP_Term_Query
*
* @since 0.0.5
*/
$input_fields = [];
if ( ! empty( $this->args['where'] ) ) {
$input_fields = $this->sanitize_input_fields( $this->args['where'] );
}
/**
* Merge the default $query_args with the $this->args that were entered
* in the query.
*
* @since 0.0.5
*/
if ( ! empty( $input_fields ) ) {
$query_args = array_merge( $query_args, $input_fields );
}
/**
* If the current user cannot moderate comments, do not include unapproved comments
*/
if ( ! current_user_can( 'moderate_comments' ) ) {
$query_args['status'] = [ 'approve' ];
$query_args['include_unapproved'] = get_current_user_id() ? [ get_current_user_id() ] : [];
if ( empty( $query_args['include_unapproved'] ) ) {
unset( $query_args['include_unapproved'] );
}
}
/**
* Throw an exception if the query is attempted to be queried by
*/
if ( 'comment__in' === $query_args['orderby'] && empty( $query_args['comment__in'] ) ) {
throw new UserError( esc_html__( 'In order to sort by comment__in, an array of IDs must be passed as the commentIn argument', 'wp-graphql' ) );
}
/**
* If there's no orderby params in the inputArgs, set order based on the first/last argument
*/
if ( empty( $query_args['order'] ) ) {
$query_args['order'] = ! empty( $last ) ? 'ASC' : 'DESC';
}
/**
* Set the graphql_cursor_compare to determine
* whether the data is being paginated forward (>) or backward (<)
* default to forward
*/
$query_args['graphql_cursor_compare'] = ( isset( $last ) ) ? '>' : '<';
// these args are used by the cursor builder to generate the proper SQL needed to respect the cursors
$query_args['graphql_after_cursor'] = $this->get_after_offset();
$query_args['graphql_before_cursor'] = $this->get_before_offset();
/**
* Pass the graphql $this->args to the WP_Query
*/
$query_args['graphql_args'] = $this->args;
// encode the graphql args as a cache domain to ensure the
// graphql_args are used to identify different queries.
// see: https://core.trac.wordpress.org/ticket/35075
$encoded_args = wp_json_encode( $this->args );
$query_args['cache_domain'] = ! empty( $encoded_args ) ? 'graphql:' . md5( $encoded_args ) : 'graphql';
/**
* We only want to query IDs because deferred resolution will resolve the full
* objects.
*/
$query_args['fields'] = 'ids';
/**
* Filter the query_args that should be applied to the query. This filter is applied AFTER the input args from
* the GraphQL Query have been applied and has the potential to override the GraphQL Query Input Args.
*
* @param array<string,mixed> $query_args array of query_args being passed to the
* @param mixed $source source passed down from the resolve tree
* @param array<string,mixed> $args array of arguments input in the field as part of the GraphQL query
* @param \WPGraphQL\AppContext $context object passed down the resolve tree
* @param \GraphQL\Type\Definition\ResolveInfo $info info about fields passed down the resolve tree
*
* @since 0.0.6
*/
return apply_filters( 'graphql_comment_connection_query_args', $query_args, $this->source, $this->args, $this->context, $this->info );
}
/**
* {@inheritDoc}
*
* @return \WP_Comment_Query
* @throws \Exception
*/
public function get_query() {
return new WP_Comment_Query( $this->query_args );
}
/**
* {@inheritDoc}
*/
protected function loader_name(): string {
return 'comment';
}
/**
* {@inheritDoc}
*/
public function get_ids_from_query() {
/** @var int[]|string[] $ids */
$ids = ! empty( $this->query->get_comments() ) ? $this->query->get_comments() : [];
// If we're going backwards, we need to reverse the array.
if ( ! empty( $this->args['last'] ) ) {
$ids = array_reverse( $ids );
}
return $ids;
}
/**
* Filters the GraphQL args before they are used in get_query_args().
*
* @return array<string,mixed>
*/
public function get_args(): array {
$args = $this->get_unfiltered_args();
if ( ! empty( $args['where'] ) ) {
// Ensure all IDs are converted to database IDs.
foreach ( $args['where'] as $input_key => $input_value ) {
if ( empty( $input_value ) ) {
continue;
}
switch ( $input_key ) {
case 'authorIn':
case 'authorNotIn':
case 'commentIn':
case 'commentNotIn':
case 'parentIn':
case 'parentNotIn':
case 'contentAuthorIn':
case 'contentAuthorNotIn':
case 'contentId':
case 'contentIdIn':
case 'contentIdNotIn':
case 'contentAuthor':
case 'userId':
if ( is_array( $input_value ) ) {
$args['where'][ $input_key ] = array_map(
static function ( $id ) {
return Utils::get_database_id_from_id( $id );
},
$input_value
);
break;
}
$args['where'][ $input_key ] = Utils::get_database_id_from_id( $input_value );
break;
case 'includeUnapproved':
if ( is_string( $input_value ) ) {
$input_value = [ $input_value ];
}
$args['where'][ $input_key ] = array_map(
static function ( $id ) {
if ( is_email( $id ) ) {
return $id;
}
return Utils::get_database_id_from_id( $id );
},
$input_value
);
break;
}
}
}
/**
* Filters the GraphQL args before they are used in get_query_args().
*
* @param array<string,mixed> $args The GraphQL args passed to the resolver.
* @param \WPGraphQL\Data\Connection\CommentConnectionResolver $connection_resolver Instance of the ConnectionResolver
*
* @since 1.11.0
*/
return apply_filters( 'graphql_comment_connection_args', $args, $this );
}
/**
* This sets up the "allowed" args, and translates the GraphQL-friendly keys to
* WP_Comment_Query friendly keys.
*
* There's probably a cleaner/more dynamic way to approach this, but this was quick. I'd be
* down to explore more dynamic ways to map this, but for now this gets the job done.
*
* @param array<string,mixed> $args The array of query arguments
*
* @since 0.0.5
* @return array<string,mixed>
*/
public function sanitize_input_fields( array $args ) {
$arg_mapping = [
'authorEmail' => 'author_email',
'authorIn' => 'author__in',
'authorNotIn' => 'author__not_in',
'authorUrl' => 'author_url',
'commentIn' => 'comment__in',
'commentNotIn' => 'comment__not_in',
'commentType' => 'type',
'commentTypeIn' => 'type__in',
'commentTypeNotIn' => 'type__not_in',
'contentAuthor' => 'post_author',
'contentAuthorIn' => 'post_author__in',
'contentAuthorNotIn' => 'post_author__not_in',
'contentId' => 'post_id',
'contentIdIn' => 'post__in',
'contentIdNotIn' => 'post__not_in',
'contentName' => 'post_name',
'contentParent' => 'post_parent',
'contentStatus' => 'post_status',
'contentType' => 'post_type',
'includeUnapproved' => 'include_unapproved',
'parentIn' => 'parent__in',
'parentNotIn' => 'parent__not_in',
'userId' => 'user_id',
];
/**
* Map and sanitize the input args to the WP_Comment_Query compatible args
*/
$query_args = Utils::map_input( $args, $arg_mapping );
/**
* Filter the input fields
*
* This allows plugins/themes to hook in and alter what $args should be allowed to be passed
* from a GraphQL Query to the get_terms query
*
* @since 0.0.5
*/
$query_args = apply_filters( 'graphql_map_input_fields_to_wp_comment_query', $query_args, $args, $this->source, $this->args, $this->context, $this->info );
return ! empty( $query_args ) && is_array( $query_args ) ? $query_args : [];
}
/**
* {@inheritDoc}
*
* @param int $offset The ID of the node used for the cursor offset.
*/
public function is_valid_offset( $offset ) {
return ! empty( get_comment( $offset ) );
}
/**
* {@inheritDoc}
*/
public function should_execute() {
return true;
}
}