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 / Connection / CommentConnectionResolver.php

CommentConnectionResolver.php in WPGraphQL trunk, at src/Data/Connection/CommentConnectionResolver.php

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