PluginProbe ʕ •ᴥ•ʔ
Advanced Custom Fields (ACF®) / 6.8.9
Advanced Custom Fields (ACF®) v6.8.9
6.8.9 6.8.8 6.8.7 6.8.6 6.8.5 6.8.4 6.8.3 6.8.2 6.8.1 5.8.5 5.8.6 5.8.7 5.8.8 5.8.9 5.9.0 5.9.1 5.9.2 5.9.3 5.9.4 5.9.5 5.9.6 5.9.7 5.9.8 5.9.9 6.0.0 6.0.1 6.0.2 6.0.3 6.0.4 6.0.5 6.0.6 6.0.7 6.1.0 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.1.6 6.1.7 6.1.8 6.2.0 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.2.6 6.2.6.1 6.2.7 6.2.8 6.2.9 6.3.0 6.3.1 6.3.10.2 6.3.11 6.3.12 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.6.1 6.4.0 6.4.0.1 6.4.1 6.4.2 6.4.3 6.5.0 6.5.1 6.6.0 6.6.1 6.6.2 6.7.0 6.7.1 6.7.2 6.8.0 trunk 1.0.0 1.0.2 1.0.3 1.0.5 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.1.1 2.1.3 2.1.4 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.6 3.0.7 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 3.2.0 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 3.2.8 3.2.9 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.3.8 3.3.9 3.4.0 3.4.1 3.4.2 3.4.3 3.5.0 3.5.1 3.5.2 3.5.3 3.5.4 3.5.5 3.5.6 3.5.7 3.5.8 4.0.0 4.0.1 4.0.2 4.0.3 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.1.5 4.1.6 4.1.8 4.2.0 4.2.1 4.2.2 4.3.0 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.3.7 4.3.8 4.3.9 4.4.0 4.4.1 4.4.10 4.4.11 4.4.12 4.4.2 4.4.3 4.4.4 4.4.5 4.4.6 4.4.7 4.4.8 4.4.9 5.10 5.10.1 5.10.2 5.11 5.11.1 5.11.2 5.11.3 5.11.4 5.12 5.12.1 5.12.2 5.12.3 5.12.4 5.12.5 5.12.6 5.6.10 5.6.2 5.6.3 5.6.4 5.6.5 5.6.6 5.6.7 5.6.8 5.6.9 5.7.0 5.7.1 5.7.10 5.7.12 5.7.13 5.7.2 5.7.3 5.7.4 5.7.5 5.7.6 5.7.7 5.7.8 5.7.9 5.8.0 5.8.1 5.8.10 5.8.11 5.8.12 5.8.13 5.8.14 5.8.2 5.8.3 5.8.4
advanced-custom-fields / includes / fields / class-acf-field-post_object.php
advanced-custom-fields / includes / fields Last commit date
class-acf-field-accordion.php 5 months ago class-acf-field-button-group.php 5 months ago class-acf-field-checkbox.php 2 months ago class-acf-field-color_picker.php 5 months ago class-acf-field-date_picker.php 5 months ago class-acf-field-date_time_picker.php 5 months ago class-acf-field-email.php 5 months ago class-acf-field-file.php 5 months ago class-acf-field-google-map.php 5 months ago class-acf-field-group.php 5 months ago class-acf-field-icon_picker.php 5 months ago class-acf-field-image.php 4 weeks ago class-acf-field-link.php 5 months ago class-acf-field-message.php 5 months ago class-acf-field-number.php 5 months ago class-acf-field-oembed.php 2 months ago class-acf-field-output.php 5 months ago class-acf-field-page_link.php 4 weeks ago class-acf-field-password.php 5 months ago class-acf-field-post_object.php 4 weeks ago class-acf-field-radio.php 2 months ago class-acf-field-range.php 5 months ago class-acf-field-relationship.php 4 weeks ago class-acf-field-select.php 2 months ago class-acf-field-separator.php 5 months ago class-acf-field-tab.php 5 months ago class-acf-field-taxonomy.php 2 months ago class-acf-field-text.php 5 months ago class-acf-field-textarea.php 5 months ago class-acf-field-time_picker.php 5 months ago class-acf-field-true_false.php 5 months ago class-acf-field-url.php 5 months ago class-acf-field-user.php 2 months ago class-acf-field-wysiwyg.php 5 months ago class-acf-field.php 5 months ago index.php 2 years ago
class-acf-field-post_object.php
815 lines
1 <?php
2 /**
3 * @package ACF
4 * @author WP Engine
5 *
6 * © 2026 Advanced Custom Fields (ACF®). All rights reserved.
7 * "ACF" is a trademark of WP Engine.
8 * Licensed under the GNU General Public License v2 or later.
9 * https://www.gnu.org/licenses/gpl-2.0.html
10 */
11
12 if ( ! class_exists( 'acf_field_post_object' ) ) :
13
14 class acf_field_post_object extends acf_field {
15
16
17 /**
18 * This function will setup the field type data
19 *
20 * @since 5.0.0
21 */
22 public function initialize() {
23 $this->name = 'post_object';
24 $this->label = __( 'Post Object', 'acf' );
25 $this->category = 'relational';
26 $this->description = __( 'An interactive and customizable UI for picking one or many posts, pages or post type items with the option to search. ', 'acf' );
27 $this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-post-object.png';
28 $this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/post-object/', 'docs', 'field-type-selection' );
29 $this->defaults = array(
30 'post_type' => array(),
31 'taxonomy' => array(),
32 'allow_null' => 0,
33 'multiple' => 0,
34 'return_format' => 'object',
35 'ui' => 1,
36 'bidirectional_target' => array(),
37 );
38
39 // extra
40 add_action( 'wp_ajax_acf/fields/post_object/query', array( $this, 'ajax_query' ) );
41 add_action( 'wp_ajax_nopriv_acf/fields/post_object/query', array( $this, 'ajax_query' ) );
42 add_filter( 'acf/conditional_logic/choices', array( $this, 'render_field_post_object_conditional_choices' ), 10, 3 );
43 }
44
45 /**
46 * Filters choices in post object conditions.
47 *
48 * @since 6.3
49 *
50 * @param array $choices The selected choice.
51 * @param array $conditional_field The conditional field settings object.
52 * @param string $rule_value The rule value.
53 * @return array
54 */
55 public function render_field_post_object_conditional_choices( $choices, $conditional_field, $rule_value ) {
56 if ( ! is_array( $conditional_field ) || $conditional_field['type'] !== 'post_object' ) {
57 return $choices;
58 }
59 if ( ! empty( $rule_value ) ) {
60 $post_title = esc_html( get_the_title( $rule_value ) );
61 $choices = array( $rule_value => $post_title );
62 }
63 return $choices;
64 }
65
66 /**
67 * AJAX query handler for post object fields.
68 *
69 * @since 5.0.0
70 *
71 * @return void
72 */
73 public function ajax_query() {
74 $nonce = acf_request_arg( 'nonce', '' );
75 $key = acf_request_arg( 'field_key', '' );
76 $conditional_logic = (bool) acf_request_arg( 'conditional_logic', false );
77
78 if ( $conditional_logic ) {
79 if ( ! acf_current_user_can_admin() ) {
80 die();
81 }
82
83 // Use the standard ACF admin nonce.
84 $nonce = '';
85 $key = '';
86 }
87
88 if ( ! acf_verify_ajax( $nonce, $key, ! $conditional_logic, 'post_object' ) ) {
89 die();
90 }
91
92 acf_send_ajax_results( $this->get_ajax_query( $_POST ) );
93 }
94
95 /**
96 * Returns an array of data formatted for use in a select2 AJAX response.
97 *
98 * @since 5.0.9
99 *
100 * @param array $options The options being queried for the ajax request.
101 * @return array|boolean The AJAX response array, or false on failure.
102 */
103 public function get_ajax_query( $options = array() ) {
104 // defaults
105 $options = acf_parse_args(
106 $options,
107 array(
108 'post_id' => 0,
109 's' => '',
110 'field_key' => '',
111 'paged' => 1,
112 'include' => '',
113 )
114 );
115
116 // load field
117 $field = acf_get_field( $options['field_key'] );
118 if ( ! $field ) {
119 return false;
120 }
121
122 // vars
123 $results = array();
124 $args = array();
125 $s = false;
126 $is_search = false;
127
128 // paged
129 $args['posts_per_page'] = 20;
130 $args['paged'] = (int) $options['paged'];
131
132 // search
133 if ( $options['s'] !== '' ) {
134
135 // strip slashes (search may be integer)
136 $s = wp_unslash( strval( $options['s'] ) );
137
138 // update vars
139 $args['s'] = $s;
140 $is_search = true;
141 }
142
143 if ( ! empty( $options['include'] ) ) {
144 $args['include'] = (int) $options['include'];
145 }
146
147 // post_type
148 if ( ! empty( $field['post_type'] ) ) {
149 $args['post_type'] = acf_get_array( $field['post_type'] );
150 } else {
151 $args['post_type'] = acf_get_post_types();
152 }
153
154 // Post status - use field config only, don't accept from user input.
155 if ( ! empty( $field['post_status'] ) ) {
156 $args['post_status'] = acf_get_array( $field['post_status'] );
157 }
158
159 // If there is an include set, we will unset search to avoid attempting to further filter by the search term.
160 if ( isset( $args['include'] ) ) {
161 unset( $args['s'] );
162 }
163
164 // taxonomy
165 if ( ! empty( $field['taxonomy'] ) ) {
166
167 // vars
168 $terms = acf_decode_taxonomy_terms( $field['taxonomy'] );
169
170 // append to $args
171 $args['tax_query'] = array();
172
173 // now create the tax queries
174 foreach ( $terms as $k => $v ) {
175 $args['tax_query'][] = array(
176 'taxonomy' => $k,
177 'field' => 'slug',
178 'terms' => $v,
179 );
180 }
181 }
182
183 $args['perm'] = 'readable';
184 $args = acf_ensure_perm_readable_post_status( $args );
185
186 // filters
187 $args = apply_filters( 'acf/fields/post_object/query', $args, $field, $options['post_id'] );
188 $args = apply_filters( 'acf/fields/post_object/query/name=' . $field['name'], $args, $field, $options['post_id'] );
189 $args = apply_filters( 'acf/fields/post_object/query/key=' . $field['key'], $args, $field, $options['post_id'] );
190
191 // Re-normalize in case a filter reset `post_status` to 'any' while leaving `perm=readable`.
192 $args = acf_ensure_perm_readable_post_status( $args );
193
194 // get posts grouped by post type
195 $groups = acf_get_grouped_posts( $args );
196
197 // bail early if no posts
198 if ( empty( $groups ) ) {
199 return false;
200 }
201
202 // loop
203 foreach ( array_keys( $groups ) as $group_title ) {
204
205 // vars
206 $posts = acf_extract_var( $groups, $group_title );
207
208 // data
209 $data = array(
210 'text' => $group_title,
211 'children' => array(),
212 );
213
214 // convert post objects to post titles
215 foreach ( array_keys( $posts ) as $post_id ) {
216 $posts[ $post_id ] = $this->get_post_title( $posts[ $post_id ], $field, $options['post_id'], $is_search, true );
217 }
218
219 // order posts by search
220 if ( $is_search && empty( $args['orderby'] ) && isset( $args['s'] ) ) {
221 $posts = acf_order_by_search( $posts, $args['s'] );
222 }
223
224 // append to $data
225 foreach ( array_keys( $posts ) as $post_id ) {
226 $data['children'][] = $this->get_post_result( $post_id, $posts[ $post_id ] );
227 }
228
229 // append to $results
230 $results[] = $data;
231 }
232
233 // optgroup or single
234 $post_type = acf_get_array( $args['post_type'] );
235 if ( count( $post_type ) == 1 ) {
236 $results = $results[0]['children'];
237 }
238
239 // vars
240 $response = array(
241 'results' => $results,
242 'limit' => $args['posts_per_page'],
243 );
244
245 // return
246 return $response;
247 }
248
249 /**
250 * This function will return an array containing id, text and maybe description data
251 *
252 * @since 5.4.0
253 *
254 * @param mixed $id The ID of the post result.
255 * @param string $text The text for the response item.
256 * @return array The combined result array.
257 */
258 public function get_post_result( $id, $text ) {
259
260 // vars
261 $result = array(
262 'id' => $id,
263 'text' => $text,
264 );
265
266 // look for parent
267 $search = '| ' . __( 'Parent', 'acf' ) . ':';
268 $pos = strpos( $text, $search );
269
270 if ( $pos !== false ) {
271 $result['description'] = substr( $text, $pos + 2 );
272 $result['text'] = substr( $text, 0, $pos );
273 }
274
275 // return
276 return $result;
277 }
278
279
280 /**
281 * This function post object's filtered output post title
282 *
283 * @since 5.0.0
284 *
285 * @param WP_Post $post The WordPress post.
286 * @param array $field The field being output.
287 * @param integer $post_id The post_id to which this value is saved to.
288 * @param integer $is_search An int-as-boolean value for whether we're performing a search.
289 * @param boolean $unescape Should we return an unescaped post title.
290 * @return string A potentially user filtered post title for the post, which may contain unsafe HTML.
291 */
292 public function get_post_title( $post, $field, $post_id = 0, $is_search = 0, $unescape = false ) {
293
294 // get post_id
295 if ( ! $post_id ) {
296 $post_id = acf_get_form_data( 'post_id' );
297 }
298
299 // vars
300 $title = acf_get_post_title( $post, $is_search );
301
302 // unescape for select2 output which handles the escaping.
303 if ( $unescape ) {
304 $title = html_entity_decode( $title );
305 }
306
307 // filters
308 $title = apply_filters( 'acf/fields/post_object/result', $title, $post, $field, $post_id );
309 $title = apply_filters( 'acf/fields/post_object/result/name=' . $field['_name'], $title, $post, $field, $post_id );
310 $title = apply_filters( 'acf/fields/post_object/result/key=' . $field['key'], $title, $post, $field, $post_id );
311
312 // return untrusted output.
313 return $title;
314 }
315
316
317 /**
318 * Create the HTML interface for the post object field.
319 *
320 * @since 3.6
321 *
322 * @param array $field An array holding all the field's data.
323 * @return void
324 */
325 public function render_field( $field ) {
326 // Change Field into a select
327 $field['type'] = 'select';
328 $field['ui'] = 1;
329 $field['ajax'] = 1;
330 $field['nonce'] = wp_create_nonce( 'acf_field_' . $this->name . '_' . $field['key'] );
331 $field['choices'] = array();
332
333 // load posts
334 $posts = $this->get_posts( $field['value'], $field );
335
336 if ( $posts ) {
337 foreach ( array_keys( $posts ) as $i ) {
338
339 // vars
340 $post = acf_extract_var( $posts, $i );
341
342 // append to choices
343 $field['choices'][ $post->ID ] = $this->get_post_title( $post, $field );
344 }
345 }
346
347 // render
348 acf_render_field( $field );
349 }
350
351
352 /**
353 * Create extra options for post object field. This is rendered when editing.
354 * The value of $field['name'] can be used (like below) to save extra data to the $field.
355 *
356 * @since 3.6
357 *
358 * @param array $field An array holding all the field's data.
359 */
360 public function render_field_settings( $field ) {
361 acf_render_field_setting(
362 $field,
363 array(
364 'label' => __( 'Filter by Post Type', 'acf' ),
365 'instructions' => '',
366 'type' => 'select',
367 'name' => 'post_type',
368 'choices' => acf_get_pretty_post_types(),
369 'multiple' => 1,
370 'ui' => 1,
371 'allow_null' => 1,
372 'placeholder' => __( 'All post types', 'acf' ),
373 )
374 );
375
376 acf_render_field_setting(
377 $field,
378 array(
379 'label' => __( 'Filter by Post Status', 'acf' ),
380 'instructions' => '',
381 'type' => 'select',
382 'name' => 'post_status',
383 'choices' => acf_get_pretty_post_statuses(),
384 'multiple' => 1,
385 'ui' => 1,
386 'allow_null' => 1,
387 'placeholder' => __( 'Any post status', 'acf' ),
388 )
389 );
390
391 acf_render_field_setting(
392 $field,
393 array(
394 'label' => __( 'Filter by Taxonomy', 'acf' ),
395 'instructions' => '',
396 'type' => 'select',
397 'name' => 'taxonomy',
398 'choices' => acf_get_taxonomy_terms(),
399 'multiple' => 1,
400 'ui' => 1,
401 'allow_null' => 1,
402 'placeholder' => __( 'All taxonomies', 'acf' ),
403 )
404 );
405
406 acf_render_field_setting(
407 $field,
408 array(
409 'label' => __( 'Return Format', 'acf' ),
410 'instructions' => '',
411 'type' => 'radio',
412 'name' => 'return_format',
413 'choices' => array(
414 'object' => __( 'Post Object', 'acf' ),
415 'id' => __( 'Post ID', 'acf' ),
416 ),
417 'layout' => 'horizontal',
418 )
419 );
420
421 acf_render_field_setting(
422 $field,
423 array(
424 'label' => __( 'Select Multiple', 'acf' ),
425 'instructions' => 'Allow content editors to select multiple values',
426 'name' => 'multiple',
427 'type' => 'true_false',
428 'ui' => 1,
429 )
430 );
431 }
432
433 /**
434 * Renders the field settings used in the "Validation" tab.
435 *
436 * @since 6.0
437 *
438 * @param array $field The field settings array.
439 * @return void
440 */
441 public function render_field_validation_settings( $field ) {
442 acf_render_field_setting(
443 $field,
444 array(
445 'label' => __( 'Allow Null', 'acf' ),
446 'instructions' => '',
447 'name' => 'allow_null',
448 'type' => 'true_false',
449 'ui' => 1,
450 )
451 );
452 }
453
454 /**
455 * Renders the field settings used in the "Advanced" tab.
456 *
457 * @since 6.2
458 *
459 * @param array $field The field settings array.
460 * @return void
461 */
462 public function render_field_advanced_settings( $field ) {
463 acf_render_bidirectional_field_settings( $field );
464 }
465
466 /**
467 * This filter is applied to the $value after it is loaded from the db
468 *
469 * @since 3.6
470 *
471 * @param mixed $value The value found in the database
472 * @param mixed $post_id The post_id from which the value was loaded
473 * @param array $field The field array holding all the field options
474 * @return mixed $value
475 */
476 public function load_value( $value, $post_id, $field ) {
477
478 // ACF4 null
479 if ( $value === 'null' ) {
480 return false;
481 }
482
483 // return
484 return $value;
485 }
486
487
488 /**
489 * This filter is appied to the $value after it is loaded from the db and before it is returned to the template
490 *
491 * @since 3.6
492 *
493 * @param mixed $value The value found in the database
494 * @param mixed $post_id The post_id from which the value was loaded
495 * @param array $field The field array holding all the field options
496 * @return mixed $value
497 */
498 public function format_value( $value, $post_id, $field ) {
499 $value = acf_get_numeric( $value );
500
501 // bail early if no value
502 if ( empty( $value ) ) {
503 return false;
504 }
505
506 // load posts if needed
507 if ( $field['return_format'] == 'object' ) {
508 $value = $this->get_posts( $value, $field );
509 }
510
511 // convert back from array if neccessary
512 if ( ! $field['multiple'] && is_array( $value ) ) {
513 $value = current( $value );
514 }
515
516 // return value
517 return $value;
518 }
519
520
521 /**
522 * Filters the field value before it is saved into the database.
523 *
524 * @since 3.6
525 *
526 * @param mixed $value The value which will be saved in the database.
527 * @param integer $post_id The post_id of which the value will be saved.
528 * @param array $field The field array holding all the field options.
529 * @return mixed $value The modified value.
530 */
531 public function update_value( $value, $post_id, $field ) {
532
533 // Bail early if no value.
534 if ( empty( $value ) ) {
535 acf_update_bidirectional_values( array(), $post_id, $field );
536 return $value;
537 }
538
539 // Format array of values.
540 // - ensure each value is an id.
541 // - Parse each id as string for SQL LIKE queries.
542 if ( acf_is_sequential_array( $value ) ) {
543 $value = array_map( 'acf_idval', $value );
544 $value = array_map( 'strval', $value );
545
546 // Parse single value for id.
547 } else {
548 $value = acf_idval( $value );
549 }
550
551 acf_update_bidirectional_values( acf_get_array( $value ), $post_id, $field );
552
553 return $value;
554 }
555
556
557 /**
558 * This function will return an array of posts for a given field value
559 *
560 * @since 5.0
561 *
562 * @param mixed $value The value of the field.
563 * @param array $field The field array holding all the field options.
564 * @return array $value An array of post objects.
565 */
566 public function get_posts( $value, $field ) {
567
568 // numeric
569 $value = acf_get_numeric( $value );
570
571 // bail early if no value
572 if ( empty( $value ) ) {
573 return false;
574 }
575
576 // get posts
577 $posts = acf_get_posts(
578 array(
579 'post__in' => $value,
580 'post_type' => $field['post_type'],
581 )
582 );
583
584 // return
585 return $posts;
586 }
587
588 /**
589 * Validates post object fields updated via the REST API.
590 *
591 * @since 5.11
592 *
593 * @param boolean $valid The current validity booleean
594 * @param integer $value The value of the field
595 * @param array $field The field array
596 * @return boolean|WP_Error
597 */
598 public function validate_rest_value( $valid, $value, $field ) {
599 if ( is_null( $value ) ) {
600 return $valid;
601 }
602
603 $param = sprintf( '%s[%s]', $field['prefix'], $field['name'] );
604 $data = array( 'param' => $param );
605 $value = is_array( $value ) ? $value : array( $value );
606
607 $invalid_posts = array();
608 $post_type_errors = array();
609 $taxonomy_errors = array();
610
611 foreach ( $value as $post_id ) {
612 if ( is_string( $post_id ) ) {
613 continue;
614 }
615
616 $post_type = get_post_type( $post_id );
617 if ( ! $post_type ) {
618 $invalid_posts[] = $post_id;
619 continue;
620 }
621
622 if (
623 is_array( $field['post_type'] ) &&
624 ! empty( $field['post_type'] ) &&
625 ! in_array( $post_type, $field['post_type'] )
626 ) {
627 $post_type_errors[] = $post_id;
628 }
629
630 if ( is_array( $field['taxonomy'] ) && ! empty( $field['taxonomy'] ) ) {
631 $found = false;
632 foreach ( $field['taxonomy'] as $taxonomy_term ) {
633 $decoded = acf_decode_taxonomy_term( $taxonomy_term );
634 if ( $decoded && is_object_in_term( $post_id, $decoded['taxonomy'], $decoded['term'] ) ) {
635 $found = true;
636 break;
637 }
638 }
639
640 if ( ! $found ) {
641 $taxonomy_errors[] = $post_id;
642 }
643 }
644 }
645
646 if ( count( $invalid_posts ) ) {
647 $error = sprintf(
648 __( '%1$s must have a valid post ID.', 'acf' ),
649 $param
650 );
651 $data['value'] = $invalid_posts;
652 return new WP_Error( 'rest_invalid_param', $error, $data );
653 }
654
655 if ( count( $post_type_errors ) ) {
656 $error = sprintf(
657 _n(
658 '%1$s must be of post type %2$s.',
659 '%1$s must be of one of the following post types: %2$s',
660 count( $field['post_type'] ),
661 'acf'
662 ),
663 $param,
664 count( $field['post_type'] ) > 1 ? implode( ', ', $field['post_type'] ) : $field['post_type'][0]
665 );
666 $data['value'] = $post_type_errors;
667
668 return new WP_Error( 'rest_invalid_param', $error, $data );
669 }
670
671 if ( count( $taxonomy_errors ) ) {
672 $error = sprintf(
673 _n(
674 '%1$s must have term %2$s.',
675 '%1$s must have one of the following terms: %2$s',
676 count( $field['taxonomy'] ),
677 'acf'
678 ),
679 $param,
680 count( $field['taxonomy'] ) > 1 ? implode( ', ', $field['taxonomy'] ) : $field['taxonomy'][0]
681 );
682 $data['value'] = $taxonomy_errors;
683
684 return new WP_Error( 'rest_invalid_param', $error, $data );
685 }
686
687 return $valid;
688 }
689
690 /**
691 * Return the schema array for the REST API.
692 *
693 * @since 5.11
694 *
695 * @param array $field The field array.
696 * @return array
697 */
698 public function get_rest_schema( array $field ) {
699 $schema = array(
700 'type' => array( 'integer', 'array', 'null' ),
701 'required' => ! empty( $field['required'] ),
702 'items' => array(
703 'type' => 'integer',
704 ),
705 );
706
707 if ( empty( $field['allow_null'] ) ) {
708 $schema['minItems'] = 1;
709 }
710
711 if ( empty( $field['multiple'] ) ) {
712 $schema['maxItems'] = 1;
713 }
714
715 return $schema;
716 }
717
718 /**
719 * REST link attributes generator for this field.
720 *
721 * @since 5.11
722 * @see \acf_field::get_rest_links()
723 *
724 * @param mixed $value The raw (unformatted) field value.
725 * @param integer|string $post_id The post ID being queried.
726 * @param array $field The field array.
727 * @return array
728 */
729 public function get_rest_links( $value, $post_id, array $field ) {
730 $links = array();
731
732 if ( empty( $value ) ) {
733 return $links;
734 }
735
736 foreach ( (array) $value as $object_id ) {
737 if ( ! $post_type = get_post_type( $object_id ) ) {
738 continue;
739 }
740
741 if ( ! $post_type_object = get_post_type_object( $post_type ) ) {
742 continue;
743 }
744
745 $rest_base = acf_get_object_type_rest_base( $post_type_object );
746 $links[] = array(
747 'rel' => $post_type_object->name === 'attachment' ? 'acf:attachment' : 'acf:post',
748 'href' => rest_url( sprintf( '/wp/v2/%s/%s', $rest_base, $object_id ) ),
749 'embeddable' => true,
750 );
751 }
752
753 return $links;
754 }
755
756 /**
757 * Apply basic formatting to prepare the value for default REST output.
758 *
759 * @since 5.11
760 *
761 * @param mixed $value The raw (unformatted) field value.
762 * @param integer|string $post_id The post ID being queried.
763 * @param array $field The field array.
764 * @return mixed
765 */
766 public function format_value_for_rest( $value, $post_id, array $field ) {
767 return acf_format_numerics( $value );
768 }
769
770 /**
771 * Formats the field value for JSON-LD output.
772 *
773 * @since 6.8.0
774 *
775 * @param mixed $value The value of the field.
776 * @param integer|string $post_id The ID of the post.
777 * @param array $field The field array.
778 * @return mixed
779 */
780 public function format_value_for_jsonld( $value, $post_id, $field ) {
781 $value = acf_format_numerics( $value );
782
783 if ( ! $value ) {
784 return $value;
785 }
786
787 if ( is_array( $value ) ) {
788 return array_map(
789 function ( $post_id ) {
790 return get_permalink( $post_id );
791 },
792 $value
793 );
794 }
795
796 return get_permalink( $value );
797 }
798
799 /**
800 * Returns an array of JSON-LD Property output types that are supported by this field type.
801 *
802 * @since 6.8
803 *
804 * @return string[]
805 */
806 public function get_jsonld_output_types(): array {
807 return array( 'Thing', 'URL' );
808 }
809 }
810
811
812 // initialize
813 acf_register_field_type( 'acf_field_post_object' );
814 endif; // class_exists check
815