PluginProbe ʕ •ᴥ•ʔ
Secure Custom Fields / trunk
Secure Custom Fields vtrunk
6.9.1 6.9.0 6.8.9 6.8.7 6.8.8 6.8.6 6.8.4 6.8.5 trunk 6.4.0-beta1 6.4.0-beta2 6.4.1 6.4.1-beta3 6.4.1-beta4 6.4.1-beta5 6.4.1-beta6 6.4.1-beta7 6.4.2 6.5.0 6.5.1 6.5.2 6.5.3 6.5.4 6.5.5 6.5.6 6.5.7 6.6.0 6.7.0 6.7.1 6.8.0 6.8.1 6.8.2 6.8.3
secure-custom-fields / includes / fields / class-acf-field-relationship.php
secure-custom-fields / includes / fields Last commit date
FlexibleContent 2 months ago class-acf-field-accordion.php 2 months ago class-acf-field-button-group.php 2 months ago class-acf-field-checkbox.php 5 days ago class-acf-field-clone.php 2 months ago class-acf-field-color_picker.php 2 months ago class-acf-field-date_picker.php 2 months ago class-acf-field-date_time_picker.php 2 months ago class-acf-field-email.php 2 months ago class-acf-field-file.php 2 months ago class-acf-field-flexible-content.php 2 weeks ago class-acf-field-gallery.php 3 weeks ago class-acf-field-google-map.php 2 months ago class-acf-field-group.php 2 months ago class-acf-field-icon_picker.php 7 months ago class-acf-field-image.php 2 months ago class-acf-field-link.php 2 months ago class-acf-field-message.php 1 year ago class-acf-field-nav-menu.php 2 weeks ago class-acf-field-number.php 2 months ago class-acf-field-oembed.php 3 weeks ago class-acf-field-output.php 1 year ago class-acf-field-page_link.php 3 weeks ago class-acf-field-password.php 2 months ago class-acf-field-post_object.php 3 weeks ago class-acf-field-radio.php 5 days ago class-acf-field-range.php 2 months ago class-acf-field-relationship.php 3 weeks ago class-acf-field-repeater.php 3 weeks ago class-acf-field-select.php 5 days ago class-acf-field-separator.php 1 year ago class-acf-field-tab.php 1 year ago class-acf-field-taxonomy.php 3 weeks ago class-acf-field-text.php 3 weeks ago class-acf-field-textarea.php 3 weeks ago class-acf-field-time_picker.php 2 months ago class-acf-field-true_false.php 2 months ago class-acf-field-url.php 3 weeks ago class-acf-field-user.php 3 weeks ago class-acf-field-wysiwyg.php 2 months ago class-acf-field.php 2 months ago class-acf-repeater-table.php 1 year ago index.php 1 year ago
class-acf-field-relationship.php
959 lines
1 <?php
2
3 if ( ! class_exists( 'acf_field_relationship' ) ) :
4
5 class acf_field_relationship extends acf_field {
6
7
8
9 /**
10 * This function will setup the field type data
11 *
12 * @type function
13 * @date 5/03/2014
14 * @since ACF 5.0.0
15 */
16 public function initialize() {
17 $this->name = 'relationship';
18 $this->label = __( 'Relationship', 'secure-custom-fields' );
19 $this->category = 'relational';
20 $this->description = __( 'A dual-column interface to select one or more posts, pages, or custom post type items to create a relationship with the item that you\'re currently editing. Includes options to search and filter.', 'secure-custom-fields' );
21 $this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-relationship.png';
22 $this->doc_url = 'https://developer.wordpress.org/secure-custom-fields/features/fields/relationship/';
23 $this->tutorial_url = 'https://developer.wordpress.org/secure-custom-fields/features/fields/relationship/relationship-tutorial/';
24 $this->defaults = array(
25 'post_type' => array(),
26 'taxonomy' => array(),
27 'min' => 0,
28 'max' => 0,
29 'filters' => array( 'search', 'post_type', 'taxonomy' ),
30 'elements' => array(),
31 'return_format' => 'object',
32 'bidirectional_target' => array(),
33 );
34 add_filter( 'acf/conditional_logic/choices', array( $this, 'render_field_relation_conditional_choices' ), 10, 3 );
35
36 // extra
37 add_action( 'wp_ajax_acf/fields/relationship/query', array( $this, 'ajax_query' ) );
38 add_action( 'wp_ajax_nopriv_acf/fields/relationship/query', array( $this, 'ajax_query' ) );
39 }
40
41 /**
42 * Filters choices in relation conditions.
43 *
44 * @since ACF 6.3
45 *
46 * @param array $choices The selected choice.
47 * @param array $conditional_field The conditional field settings object.
48 * @param string $rule_value The rule value.
49 * @return array
50 */
51 public function render_field_relation_conditional_choices( $choices, $conditional_field, $rule_value ) {
52 if ( ! is_array( $conditional_field ) || $conditional_field['type'] !== 'relationship' ) {
53 return $choices;
54 }
55 if ( ! empty( $rule_value ) ) {
56 $post_title = esc_html( get_the_title( $rule_value ) );
57 $choices = array( $rule_value => $post_title );
58 }
59 return $choices;
60 }
61
62 /**
63 * description
64 *
65 * @type function
66 * @date 16/12/2015
67 * @since ACF 5.3.2
68 *
69 * @param $post_id (int)
70 * @return $post_id (int)
71 */
72 function input_admin_enqueue_scripts() {
73
74 // localize
75 acf_localize_text(
76 array(
77 // 'Minimum values reached ( {min} values )' => __('Minimum values reached ( {min} values )', 'secure-custom-fields'),
78 'Maximum values reached ( {max} values )' => __( 'Maximum values reached ( {max} values )', 'secure-custom-fields' ),
79 'Loading' => __( 'Loading', 'secure-custom-fields' ),
80 'No matches found' => __( 'No matches found', 'secure-custom-fields' ),
81 )
82 );
83 }
84
85 /**
86 * Returns AJAX results for the Relationship field.
87 *
88 * @since ACF 5.0.0
89 *
90 * @return void
91 */
92 public function ajax_query() {
93 $nonce = acf_request_arg( 'nonce', '' );
94 $key = acf_request_arg( 'field_key', '' );
95 $conditional_logic = (bool) acf_request_arg( 'conditional_logic', false );
96
97 if ( $conditional_logic ) {
98 if ( ! acf_current_user_can_admin() ) {
99 die();
100 }
101
102 // Use the standard ACF admin nonce.
103 $nonce = '';
104 $key = '';
105 }
106
107 if ( ! acf_verify_ajax( $nonce, $key, ! $conditional_logic, 'relationship' ) ) {
108 die();
109 }
110
111 acf_send_ajax_results( $this->get_ajax_query( $_POST ) );
112 }
113
114 /**
115 * Returns an array of data formatted for use in a select2 AJAX response.
116 *
117 * @since ACF 5.0.9
118 *
119 * @param array $options An array of options for the query.
120 * @return array|false
121 */
122 public function get_ajax_query( $options = array() ) {
123 // defaults
124 $options = wp_parse_args(
125 $options,
126 array(
127 'post_id' => 0,
128 's' => '',
129 'field_key' => '',
130 'paged' => 1,
131 'post_type' => '',
132 'include' => '',
133 'taxonomy' => '',
134 )
135 );
136
137 // load field
138 $field = acf_get_field( $options['field_key'] );
139 if ( ! $field ) {
140 return false;
141 }
142
143 // vars
144 $results = array();
145 $args = array();
146 $s = false;
147 $is_search = false;
148
149 // paged
150 $args['posts_per_page'] = 20;
151 $args['paged'] = (int) $options['paged'];
152
153 // search
154 if ( $options['s'] !== '' && empty( $options['include'] ) ) {
155 // strip slashes (search may be integer)
156 $s = wp_unslash( strval( $options['s'] ) );
157
158 // update vars
159 $args['s'] = $s;
160 $is_search = true;
161 }
162
163 // post_type - validate user input against field config to prevent bypass.
164 if ( ! empty( $field['post_type'] ) ) {
165 $allowed_post_types = acf_get_array( $field['post_type'] );
166
167 if ( ! empty( $options['post_type'] ) ) {
168 // User is filtering - only allow post types within field config.
169 $requested_types = acf_get_array( $options['post_type'] );
170 $args['post_type'] = array_intersect( $requested_types, $allowed_post_types );
171
172 if ( empty( $args['post_type'] ) ) {
173 // Requested types not allowed, fall back to field config.
174 $args['post_type'] = $allowed_post_types;
175 }
176 } else {
177 $args['post_type'] = $allowed_post_types;
178 }
179 } elseif ( ! empty( $options['post_type'] ) ) {
180 // No field restriction, allow user filter.
181 $args['post_type'] = acf_get_array( $options['post_type'] );
182 } else {
183 $args['post_type'] = acf_get_post_types();
184 }
185
186 // Post status - use field config only, don't accept from user input.
187 if ( ! empty( $field['post_status'] ) ) {
188 $args['post_status'] = acf_get_array( $field['post_status'] );
189 }
190
191 // taxonomy - validate user input against field config to prevent bypass.
192 if ( ! empty( $field['taxonomy'] ) ) {
193 // Field has taxonomy restrictions.
194 $allowed_terms = acf_decode_taxonomy_terms( $field['taxonomy'] );
195
196 if ( ! empty( $options['taxonomy'] ) ) {
197 // User is filtering - validate both taxonomy AND term are allowed.
198 $term = acf_decode_taxonomy_term( $options['taxonomy'] );
199
200 if ( $term && isset( $allowed_terms[ $term['taxonomy'] ] ) && in_array( $term['term'], $allowed_terms[ $term['taxonomy'] ], true ) ) {
201 // Taxonomy:term combination is allowed.
202 $args['tax_query'] = array();
203 $args['tax_query'][] = array(
204 'taxonomy' => $term['taxonomy'],
205 'field' => 'slug',
206 'terms' => $term['term'],
207 );
208 } else {
209 // Requested taxonomy:term not allowed, fall back to field config.
210 $args['tax_query'] = array( 'relation' => 'OR' );
211
212 foreach ( $allowed_terms as $k => $v ) {
213 $args['tax_query'][] = array(
214 'taxonomy' => $k,
215 'field' => 'slug',
216 'terms' => $v,
217 );
218 }
219 }
220 } else {
221 // No user filter, use field config.
222 $args['tax_query'] = array( 'relation' => 'OR' );
223
224 foreach ( $allowed_terms as $k => $v ) {
225 $args['tax_query'][] = array(
226 'taxonomy' => $k,
227 'field' => 'slug',
228 'terms' => $v,
229 );
230 }
231 }
232 } elseif ( ! empty( $options['taxonomy'] ) ) {
233 // No field restriction, allow user filter.
234 $term = acf_decode_taxonomy_term( $options['taxonomy'] );
235
236 if ( $term ) {
237 $args['tax_query'] = array();
238 $args['tax_query'][] = array(
239 'taxonomy' => $term['taxonomy'],
240 'field' => 'slug',
241 'terms' => $term['term'],
242 );
243 }
244 }
245
246 if ( ! empty( $options['include'] ) ) {
247 // If we have an include, we need to return only the selected posts.
248 $args['post__in'] = array( (int) $options['include'] );
249 }
250
251 // filters
252 $args = apply_filters( 'acf/fields/relationship/query', $args, $field, $options['post_id'] );
253 $args = apply_filters( 'acf/fields/relationship/query/name=' . $field['name'], $args, $field, $options['post_id'] );
254 $args = apply_filters( 'acf/fields/relationship/query/key=' . $field['key'], $args, $field, $options['post_id'] );
255
256 // get posts grouped by post type
257 $groups = acf_get_grouped_posts( $args );
258
259 // bail early if no posts
260 if ( empty( $groups ) ) {
261 return false;
262 }
263
264 // loop
265 foreach ( array_keys( $groups ) as $group_title ) {
266
267 // vars
268 $posts = acf_extract_var( $groups, $group_title );
269
270 // data
271 $data = array(
272 'text' => $group_title,
273 'children' => array(),
274 );
275
276 // convert post objects to post titles
277 foreach ( array_keys( $posts ) as $post_id ) {
278 $posts[ $post_id ] = $this->get_post_title( $posts[ $post_id ], $field, $options['post_id'] );
279 }
280
281 // order posts by search
282 if ( $is_search && empty( $args['orderby'] ) && isset( $args['s'] ) ) {
283 $posts = acf_order_by_search( $posts, $args['s'] );
284 }
285
286 // append to $data
287 foreach ( array_keys( $posts ) as $post_id ) {
288 $data['children'][] = $this->get_post_result( $post_id, $posts[ $post_id ] );
289 }
290
291 // append to $results
292 $results[] = $data;
293 }
294
295 // add as optgroup or results
296 if ( count( $args['post_type'] ) === 1 ) {
297 $results = $results[0]['children'];
298 }
299
300 return array(
301 'results' => $results,
302 'limit' => $args['posts_per_page'],
303 );
304 }
305
306 /**
307 * This function will return an array containing id, text and maybe description data
308 *
309 * @type function
310 * @date 7/07/2016
311 * @since ACF 5.4.0
312 *
313 * @param $id (mixed)
314 * @param $text (string)
315 * @return (array)
316 */
317 function get_post_result( $id, $text ) {
318
319 // vars
320 $result = array(
321 'id' => $id,
322 'text' => $text,
323 );
324
325 // return
326 return $result;
327 }
328
329
330 /**
331 * This function returns the HTML for a result
332 *
333 * @type function
334 * @date 1/11/2013
335 * @since ACF 5.0.0
336 *
337 * @param $post (object)
338 * @param $field (array)
339 * @param $post_id (int) the post_id to which this value is saved to
340 * @return (string)
341 */
342 function get_post_title( $post, $field, $post_id = 0, $is_search = 0 ) {
343
344 // get post_id
345 if ( ! $post_id ) {
346 $post_id = acf_get_form_data( 'post_id' );
347 }
348
349 // vars
350 $title = esc_html( acf_get_post_title( $post, $is_search ) );
351
352 // featured_image
353 if ( acf_in_array( 'featured_image', $field['elements'] ) ) {
354
355 // vars
356 $class = 'thumbnail';
357 $thumbnail = acf_get_post_thumbnail( $post->ID, array( 17, 17 ) );
358
359 // icon
360 if ( $thumbnail['type'] == 'icon' ) {
361 $class .= ' -' . $thumbnail['type'];
362 }
363
364 // append
365 $title = '<div class="' . $class . '">' . $thumbnail['html'] . '</div>' . $title;
366 }
367
368 // filters
369 $title = apply_filters( 'acf/fields/relationship/result', $title, $post, $field, $post_id );
370 $title = apply_filters( 'acf/fields/relationship/result/name=' . $field['_name'], $title, $post, $field, $post_id );
371 $title = apply_filters( 'acf/fields/relationship/result/key=' . $field['key'], $title, $post, $field, $post_id );
372
373 // return
374 return $title;
375 }
376
377
378 /**
379 * Create the HTML interface for your field
380 *
381 * @param $field - an array holding all the field's data
382 *
383 * @type action
384 * @since ACF 3.6
385 * @date 23/01/13
386 */
387 function render_field( $field ) {
388
389 // vars
390 $post_type = acf_get_array( $field['post_type'] );
391 $taxonomy = acf_get_array( $field['taxonomy'] );
392 $filters = acf_get_array( $field['filters'] );
393
394 // filters
395 $filter_count = count( $filters );
396 $filter_post_type_choices = array();
397 $filter_taxonomy_choices = array();
398
399 // post_type filter
400 if ( in_array( 'post_type', $filters ) ) {
401 $filter_post_type_choices = array(
402 '' => __( 'Select post type', 'secure-custom-fields' ),
403 ) + acf_get_pretty_post_types( $post_type );
404 }
405
406 // taxonomy filter
407 if ( in_array( 'taxonomy', $filters ) ) {
408 $term_choices = array();
409 $filter_taxonomy_choices = array(
410 '' => __( 'Select taxonomy', 'secure-custom-fields' ),
411 );
412
413 // check for specific taxonomy setting
414 if ( $taxonomy ) {
415 $terms = acf_get_encoded_terms( $taxonomy );
416 $term_choices = acf_get_choices_from_terms( $terms, 'slug' );
417
418 // if no terms were specified, find all terms
419 } else {
420
421 // restrict taxonomies by the post_type selected
422 $term_args = array();
423 if ( $post_type ) {
424 $term_args['taxonomy'] = acf_get_taxonomies(
425 array(
426 'post_type' => $post_type,
427 )
428 );
429 }
430
431 // get terms
432 $terms = acf_get_grouped_terms( $term_args );
433 $term_choices = acf_get_choices_from_grouped_terms( $terms, 'slug' );
434 }
435
436 // append term choices
437 $filter_taxonomy_choices = $filter_taxonomy_choices + $term_choices;
438 }
439
440 // div attributes
441 $atts = array(
442 'id' => $field['id'],
443 'class' => "acf-relationship {$field['class']}",
444 'data-min' => $field['min'],
445 'data-max' => $field['max'],
446 'data-s' => '',
447 'data-paged' => 1,
448 'data-post_type' => '',
449 'data-taxonomy' => '',
450 'data-nonce' => wp_create_nonce( 'acf_field_' . $this->name . '_' . $field['key'] ),
451 );
452
453 ?>
454 <div <?php echo acf_esc_attrs( $atts ); ?>>
455
456 <?php
457 acf_hidden_input(
458 array(
459 'name' => $field['name'],
460 'value' => '',
461 )
462 );
463 ?>
464
465 <?php
466
467 /* filters */
468 if ( $filter_count ) :
469 ?>
470 <div class="filters -f<?php echo esc_attr( $filter_count ); ?>">
471 <?php
472
473 /* search */
474 if ( in_array( 'search', $filters ) ) :
475 ?>
476 <div class="filter -search">
477 <?php
478 acf_text_input(
479 array(
480 'placeholder' => __( 'Search...', 'secure-custom-fields' ),
481 'data-filter' => 's',
482 )
483 );
484 ?>
485 </div>
486 <?php
487 endif;
488
489 /* post_type */
490 if ( in_array( 'post_type', $filters ) ) :
491 ?>
492 <div class="filter -post_type">
493 <?php
494 acf_select_input(
495 array(
496 'choices' => $filter_post_type_choices,
497 'data-filter' => 'post_type',
498 )
499 );
500 ?>
501 </div>
502 <?php
503 endif;
504
505 /* post_type */
506 if ( in_array( 'taxonomy', $filters ) ) :
507 ?>
508 <div class="filter -taxonomy">
509 <?php
510 acf_select_input(
511 array(
512 'choices' => $filter_taxonomy_choices,
513 'data-filter' => 'taxonomy',
514 )
515 );
516 ?>
517 </div>
518 <?php endif; ?>
519 </div>
520 <?php endif; ?>
521
522 <div class="selection">
523 <div class="choices">
524 <ul class="acf-bl list choices-list"></ul>
525 </div>
526 <div class="values">
527 <ul class="acf-bl list values-list">
528 <?php
529 if ( ! empty( $field['value'] ) ) :
530
531 // get posts
532 $posts = acf_get_posts(
533 array(
534 'post__in' => $field['value'],
535 'post_type' => $field['post_type'],
536 )
537 );
538
539 // loop
540 foreach ( $posts as $post ) :
541 ?>
542 <li>
543 <?php
544 acf_hidden_input(
545 array(
546 'name' => $field['name'] . '[]',
547 'value' => $post->ID,
548 )
549 );
550 ?>
551 <span tabindex="0" data-id="<?php echo esc_attr( $post->ID ); ?>" class="acf-rel-item acf-rel-item-remove">
552 <?php echo acf_esc_html( $this->get_post_title( $post, $field ) ); ?>
553 <a href="#" class="acf-icon -minus small dark" data-name="remove_item"></a>
554 </span>
555 </li>
556 <?php endforeach; ?>
557 <?php endif; ?>
558 </ul>
559 </div>
560 </div>
561 </div>
562 <?php
563 }
564
565
566 /**
567 * Create extra options for your field. This is rendered when editing a field.
568 * The value of $field['name'] can be used (like bellow) to save extra data to the $field
569 *
570 * @type action
571 * @since ACF 3.6
572 * @date 23/01/13
573 *
574 * @param $field - an array holding all the field's data
575 */
576 function render_field_settings( $field ) {
577 acf_render_field_setting(
578 $field,
579 array(
580 'label' => __( 'Filter by Post Type', 'secure-custom-fields' ),
581 'instructions' => '',
582 'type' => 'select',
583 'name' => 'post_type',
584 'choices' => acf_get_pretty_post_types(),
585 'multiple' => 1,
586 'ui' => 1,
587 'allow_null' => 1,
588 'placeholder' => __( 'All post types', 'secure-custom-fields' ),
589 )
590 );
591
592 acf_render_field_setting(
593 $field,
594 array(
595 'label' => __( 'Filter by Post Status', 'secure-custom-fields' ),
596 'instructions' => '',
597 'type' => 'select',
598 'name' => 'post_status',
599 'choices' => acf_get_pretty_post_statuses(),
600 'multiple' => 1,
601 'ui' => 1,
602 'allow_null' => 1,
603 'placeholder' => __( 'Any post status', 'secure-custom-fields' ),
604 )
605 );
606
607 acf_render_field_setting(
608 $field,
609 array(
610 'label' => __( 'Filter by Taxonomy', 'secure-custom-fields' ),
611 'instructions' => '',
612 'type' => 'select',
613 'name' => 'taxonomy',
614 'choices' => acf_get_taxonomy_terms(),
615 'multiple' => 1,
616 'ui' => 1,
617 'allow_null' => 1,
618 'placeholder' => __( 'All taxonomies', 'secure-custom-fields' ),
619 )
620 );
621
622 acf_render_field_setting(
623 $field,
624 array(
625 'label' => __( 'Filters', 'secure-custom-fields' ),
626 'instructions' => '',
627 'type' => 'checkbox',
628 'name' => 'filters',
629 'choices' => array(
630 'search' => __( 'Search', 'secure-custom-fields' ),
631 'post_type' => __( 'Post Type', 'secure-custom-fields' ),
632 'taxonomy' => __( 'Taxonomy', 'secure-custom-fields' ),
633 ),
634 )
635 );
636
637 acf_render_field_setting(
638 $field,
639 array(
640 'label' => __( 'Return Format', 'secure-custom-fields' ),
641 'instructions' => '',
642 'type' => 'radio',
643 'name' => 'return_format',
644 'choices' => array(
645 'object' => __( 'Post Object', 'secure-custom-fields' ),
646 'id' => __( 'Post ID', 'secure-custom-fields' ),
647 ),
648 'layout' => 'horizontal',
649 )
650 );
651 }
652
653 /**
654 * Renders the field settings used in the "Validation" tab.
655 *
656 * @since ACF 6.0
657 *
658 * @param array $field The field settings array.
659 * @return void
660 */
661 function render_field_validation_settings( $field ) {
662 $field['min'] = empty( $field['min'] ) ? '' : $field['min'];
663 $field['max'] = empty( $field['max'] ) ? '' : $field['max'];
664
665 acf_render_field_setting(
666 $field,
667 array(
668 'label' => __( 'Minimum Posts', 'secure-custom-fields' ),
669 'instructions' => '',
670 'type' => 'number',
671 'name' => 'min',
672 )
673 );
674
675 acf_render_field_setting(
676 $field,
677 array(
678 'label' => __( 'Maximum Posts', 'secure-custom-fields' ),
679 'instructions' => '',
680 'type' => 'number',
681 'name' => 'max',
682 )
683 );
684 }
685
686 /**
687 * Renders the field settings used in the "Presentation" tab.
688 *
689 * @since ACF 6.0
690 *
691 * @param array $field The field settings array.
692 * @return void
693 */
694 function render_field_presentation_settings( $field ) {
695 acf_render_field_setting(
696 $field,
697 array(
698 'label' => __( 'Elements', 'secure-custom-fields' ),
699 'instructions' => __( 'Selected elements will be displayed in each result', 'secure-custom-fields' ),
700 'type' => 'checkbox',
701 'name' => 'elements',
702 'choices' => array(
703 'featured_image' => __( 'Featured Image', 'secure-custom-fields' ),
704 ),
705 )
706 );
707 }
708
709 /**
710 * Renders the field settings used in the "Advanced" tab.
711 *
712 * @since ACF 6.2
713 *
714 * @param array $field The field settings array.
715 * @return void
716 */
717 public function render_field_advanced_settings( $field ) {
718 acf_render_bidirectional_field_settings( $field );
719 }
720
721 /**
722 * This filter is applied to the $value after it is loaded from the db and before it is returned to the template
723 *
724 * @type filter
725 * @since ACF 3.6
726 * @date 23/01/13
727 *
728 * @param $value (mixed) the value which was loaded from the database
729 * @param $post_id (mixed) the post_id from which the value was loaded
730 * @param $field (array) the field array holding all the field options
731 *
732 * @return $value (mixed) the modified value
733 */
734 function format_value( $value, $post_id, $field ) {
735
736 // bail early if no value
737 if ( empty( $value ) ) {
738 return $value;
739 }
740
741 // force value to array
742 $value = acf_get_array( $value );
743
744 // convert to int
745 $value = array_map( 'intval', $value );
746
747 // load posts if needed
748 if ( $field['return_format'] == 'object' ) {
749
750 // get posts
751 $value = acf_get_posts(
752 array(
753 'post__in' => $value,
754 'post_type' => $field['post_type'],
755 )
756 );
757 }
758
759 // return
760 return $value;
761 }
762
763
764 /**
765 * description
766 *
767 * @type function
768 * @date 11/02/2014
769 * @since ACF 5.0.0
770 *
771 * @param $post_id (int)
772 * @return $post_id (int)
773 */
774 function validate_value( $valid, $value, $field, $input ) {
775
776 // default
777 if ( empty( $value ) || ! is_array( $value ) ) {
778 $value = array();
779 }
780
781 // min
782 if ( count( $value ) < $field['min'] ) {
783 /* translators: %1$s: field label, %2$s: minimum number of selections */
784 $valid = _n( '%1$s requires at least %2$s selection', '%1$s requires at least %2$s selections', $field['min'], 'secure-custom-fields' );
785 $valid = sprintf( $valid, $field['label'], $field['min'] );
786 }
787
788 // return
789 return $valid;
790 }
791
792
793 /**
794 * Filters the field value before it is saved into the database.
795 *
796 * @since ACF 3.6
797 *
798 * @param mixed $value The value which will be saved in the database.
799 * @param integer $post_id The post_id of which the value will be saved.
800 * @param array $field The field array holding all the field options.
801 *
802 * @return mixed $value The modified value.
803 */
804 public function update_value( $value, $post_id, $field ) {
805
806 // Bail early if no value.
807 if ( empty( $value ) ) {
808 acf_update_bidirectional_values( array(), $post_id, $field );
809 return $value;
810 }
811
812 // Format array of values.
813 // - ensure each value is an id.
814 // - Parse each id as string for SQL LIKE queries.
815 if ( acf_is_sequential_array( $value ) ) {
816 $value = array_map( 'acf_idval', $value );
817 $value = array_map( 'strval', $value );
818
819 // Parse single value for id.
820 } else {
821 $value = acf_idval( $value );
822 }
823
824 acf_update_bidirectional_values( acf_get_array( $value ), $post_id, $field );
825
826 // Return value.
827 return $value;
828 }
829
830 /**
831 * Validates relationship fields updated via the REST API.
832 *
833 * @param boolean $valid The current validity boolean.
834 * @param integer $value The value of the field.
835 * @param array $field The field array.
836 * @return boolean|WP_Error
837 */
838 public function validate_rest_value( $valid, $value, $field ) {
839 return acf_get_field_type( 'post_object' )->validate_rest_value( $valid, $value, $field );
840 }
841
842 /**
843 * Return the schema array for the REST API.
844 *
845 * @param array $field
846 * @return array
847 */
848 public function get_rest_schema( array $field ) {
849 $schema = array(
850 'type' => array( 'integer', 'array', 'null' ),
851 'required' => ! empty( $field['required'] ),
852 'items' => array(
853 'type' => 'integer',
854 ),
855 );
856
857 if ( empty( $field['allow_null'] ) ) {
858 $schema['minItems'] = 1;
859 }
860
861 if ( ! empty( $field['min'] ) ) {
862 $schema['minItems'] = (int) $field['min'];
863 }
864
865 if ( ! empty( $field['max'] ) ) {
866 $schema['maxItems'] = (int) $field['max'];
867 }
868
869 return $schema;
870 }
871
872 /**
873 * @see \acf_field::get_rest_links()
874 * @param mixed $value The raw (unformatted) field value.
875 * @param integer|string $post_id
876 * @param array $field
877 * @return array
878 */
879 public function get_rest_links( $value, $post_id, array $field ) {
880 $links = array();
881
882 if ( empty( $value ) ) {
883 return $links;
884 }
885
886 foreach ( (array) $value as $object_id ) {
887 if ( ! $post_type = get_post_type( $object_id ) or ! $post_type = get_post_type_object( $post_type ) ) {
888 continue;
889 }
890 $rest_base = acf_get_object_type_rest_base( $post_type );
891 $links[] = array(
892 'rel' => $post_type->name === 'attachment' ? 'acf:attachment' : 'acf:post',
893 'href' => rest_url( sprintf( '/wp/v2/%s/%s', $rest_base, $object_id ) ),
894 'embeddable' => true,
895 );
896 }
897
898 return $links;
899 }
900
901 /**
902 * Apply basic formatting to prepare the value for default REST output.
903 *
904 * @param mixed $value
905 * @param string|integer $post_id
906 * @param array $field
907 * @return mixed
908 */
909 public function format_value_for_rest( $value, $post_id, array $field ) {
910 return acf_format_numerics( $value );
911 }
912
913 /**
914 * Formats the field value for JSON-LD output.
915 *
916 * @since 6.8.0
917 *
918 * @param mixed $value The value of the field.
919 * @param integer|string $post_id The ID of the post.
920 * @param array $field The field array.
921 * @return mixed
922 */
923 public function format_value_for_jsonld( $value, $post_id, $field ) {
924 $value = acf_format_numerics( $value );
925
926 if ( ! $value ) {
927 return $value;
928 }
929
930 if ( is_array( $value ) ) {
931 return array_map(
932 function ( $post_id ) {
933 return get_permalink( $post_id );
934 },
935 $value
936 );
937 }
938
939 return get_permalink( $value );
940 }
941
942 /**
943 * Returns an array of JSON-LD Property output types that are supported by this field type.
944 *
945 * @since 6.8
946 *
947 * @return string[]
948 */
949 public function get_jsonld_output_types(): array {
950 return array( 'Thing', 'URL' );
951 }
952 }
953
954
955 // initialize
956 acf_register_field_type( 'acf_field_relationship' );
957 endif; // class_exists check
958
959 ?>