acf_field_relationship

The Advanced Custom Fields acf field relationship class.

Defined (1)

The class is defined in the following location(s).

/core/fields/relationship.php  
  1. class acf_field_relationship extends acf_field 
  2. /** 
  3. * __construct 
  4. * Set name / label needed for actions / filters 
  5. * @since 3.6 
  6. * @date 23/01/13 
  7. */ 
  8.  
  9. function __construct() 
  10. // vars 
  11. $this->name = 'relationship'; 
  12. $this->label = __("Relationship", 'acf'); 
  13. $this->category = __("Relational", 'acf'); 
  14. $this->defaults = array( 
  15. 'post_type' => array('all'),  
  16. 'max' => '',  
  17. 'taxonomy' => array('all'),  
  18. 'filters' => array('search'),  
  19. 'result_elements' => array('post_title', 'post_type'),  
  20. 'return_format' => 'object' 
  21. ); 
  22. $this->l10n = array( 
  23. 'max' => __("Maximum values reached ( {max} values )", 'acf') 
  24. ); 
  25.  
  26.  
  27. // do not delete! 
  28. parent::__construct(); 
  29.  
  30.  
  31. // extra 
  32. add_action('wp_ajax_acf/fields/relationship/query_posts', array($this, 'query_posts')); 
  33. add_action('wp_ajax_nopriv_acf/fields/relationship/query_posts', array($this, 'query_posts')); 
  34.  
  35.  
  36. /** 
  37. * load_field() 
  38. *  
  39. * This filter is appied to the $field after it is loaded from the database 
  40. *  
  41. * @type filter 
  42. * @since 3.6 
  43. * @date 23/01/13 
  44. *  
  45. * @param $field - the field array holding all the field options 
  46. *  
  47. * @return $field - the field array holding all the field options 
  48. */ 
  49.  
  50. function load_field( $field ) 
  51. // validate post_type 
  52. if( !$field['post_type'] || !is_array($field['post_type']) || in_array('', $field['post_type']) ) 
  53. $field['post_type'] = array( 'all' ); 
  54.  
  55.  
  56. // validate taxonomy 
  57. if( !$field['taxonomy'] || !is_array($field['taxonomy']) || in_array('', $field['taxonomy']) ) 
  58. $field['taxonomy'] = array( 'all' ); 
  59.  
  60.  
  61. // validate result_elements 
  62. if( !is_array( $field['result_elements'] ) ) 
  63. $field['result_elements'] = array(); 
  64.  
  65. if( !in_array('post_title', $field['result_elements']) ) 
  66. $field['result_elements'][] = 'post_title'; 
  67.  
  68.  
  69. // filters 
  70. if( !is_array( $field['filters'] ) ) 
  71. $field['filters'] = array(); 
  72.  
  73.  
  74. // return 
  75. return $field; 
  76.  
  77.  
  78. /** 
  79. * get_result 
  80. * description 
  81. * @type function 
  82. * @date 5/02/2015 
  83. * @since 5.1.5 
  84. * @param $post_id (int) 
  85. * @return $post_id (int) 
  86. */ 
  87.  
  88. function get_result( $post, $field, $the_post, $options = array() ) { 
  89.  
  90. // right aligned info 
  91. $title = '<span class="relationship-item-info">'; 
  92.  
  93. if( in_array('post_type', $field['result_elements']) ) { 
  94.  
  95. $post_type_object = get_post_type_object( $post->post_type ); 
  96. $title .= $post_type_object->labels->singular_name; 
  97.  
  98.  
  99.  
  100. // WPML 
  101. if( !empty($options['lang']) ) { 
  102.  
  103. $title .= ' (' . $options['lang'] . ')'; 
  104.  
  105. } elseif( defined('ICL_LANGUAGE_CODE') ) { 
  106.  
  107. $title .= ' (' . ICL_LANGUAGE_CODE . ')'; 
  108.  
  109.  
  110. $title .= '</span>'; 
  111.  
  112.  
  113. // featured_image 
  114. if( in_array('featured_image', $field['result_elements']) ) { 
  115.  
  116. $image = ''; 
  117.  
  118. if( $post->post_type == 'attachment' ) { 
  119.  
  120. $image = wp_get_attachment_image( $post->ID, array(21, 21) ); 
  121.  
  122. } else { 
  123.  
  124. $image = get_the_post_thumbnail( $post->ID, array(21, 21) ); 
  125.  
  126.  
  127. $title .= '<div class="result-thumbnail">' . $image . '</div>'; 
  128.  
  129.  
  130.  
  131. // title 
  132. $post_title = get_the_title( $post->ID ); 
  133.  
  134.  
  135. // empty 
  136. if( $post_title === '' ) { 
  137.  
  138. $post_title = __('(no title)', 'acf'); 
  139.  
  140.  
  141.  
  142. $title .= $post_title; 
  143.  
  144.  
  145. // status 
  146. if( get_post_status( $post->ID ) != "publish" ) { 
  147.  
  148. $title .= ' (' . get_post_status( $post->ID ) . ')'; 
  149.  
  150.  
  151.  
  152. // filters 
  153. $title = apply_filters('acf/fields/relationship/result', $title, $post, $field, $the_post); 
  154. $title = apply_filters('acf/fields/relationship/result/name=' . $field['_name'] , $title, $post, $field, $the_post); 
  155. $title = apply_filters('acf/fields/relationship/result/key=' . $field['key'], $title, $post, $field, $the_post); 
  156.  
  157.  
  158. // return 
  159. return $title; 
  160.  
  161.  
  162.  
  163. /** 
  164. * query_posts 
  165. * @description:  
  166. * @since: 3.6 
  167. * @created: 27/01/13 
  168. */ 
  169.  
  170. function query_posts() 
  171. // vars 
  172. $r = array( 
  173. 'next_page_exists' => 1,  
  174. 'html' => '' 
  175. ); 
  176.  
  177.  
  178. // options 
  179. $options = array( 
  180. 'post_type' => 'all',  
  181. 'taxonomy' => 'all',  
  182. 'posts_per_page' => 10,  
  183. 'paged' => 1,  
  184. 'orderby' => 'title',  
  185. 'order' => 'ASC',  
  186. 'post_status' => 'any',  
  187. 'suppress_filters' => false,  
  188. 's' => '',  
  189. 'lang' => false,  
  190. 'update_post_meta_cache' => false,  
  191. 'field_key' => '',  
  192. 'nonce' => '',  
  193. 'ancestor' => false,  
  194. ); 
  195.  
  196. $options = array_merge( $options, $_POST ); 
  197.  
  198.  
  199. // validate 
  200. if( ! wp_verify_nonce($options['nonce'], 'acf_nonce') ) 
  201. die(); 
  202.  
  203.  
  204. // WPML 
  205. if( $options['lang'] ) 
  206. global $sitepress; 
  207.  
  208. if( !empty($sitepress) ) 
  209. $sitepress->switch_lang( $options['lang'] ); 
  210.  
  211.  
  212. // convert types 
  213. $options['post_type'] = explode(', ', $options['post_type']); 
  214. $options['taxonomy'] = explode(', ', $options['taxonomy']); 
  215.  
  216.  
  217. // load all post types by default 
  218. if( in_array('all', $options['post_type']) ) 
  219. $options['post_type'] = apply_filters('acf/get_post_types', array()); 
  220.  
  221.  
  222. // attachment doesn't work if it is the only item in an array??? 
  223. if( is_array($options['post_type']) && count($options['post_type']) == 1 ) 
  224. $options['post_type'] = $options['post_type'][0]; 
  225.  
  226.  
  227. // create tax queries 
  228. if( ! in_array('all', $options['taxonomy']) ) 
  229. // vars 
  230. $taxonomies = array(); 
  231. $options['tax_query'] = array(); 
  232.  
  233. foreach( $options['taxonomy'] as $v ) 
  234.  
  235. // find term (find taxonomy!) 
  236. // $term = array( 0 => $taxonomy, 1 => $term_id ) 
  237. $term = explode(':', $v);  
  238.  
  239.  
  240. // validate 
  241. if( !is_array($term) || !isset($term[1]) ) 
  242. continue; 
  243.  
  244.  
  245. // add to tax array 
  246. $taxonomies[ $term[0] ][] = $term[1]; 
  247.  
  248.  
  249.  
  250. // now create the tax queries 
  251. foreach( $taxonomies as $k => $v ) 
  252. $options['tax_query'][] = array( 
  253. 'taxonomy' => $k,  
  254. 'field' => 'id',  
  255. 'terms' => $v,  
  256. ); 
  257.  
  258. unset( $options['taxonomy'] ); 
  259.  
  260.  
  261. // load field 
  262. $field = array(); 
  263. if( $options['ancestor'] ) 
  264. $ancestor = apply_filters('acf/load_field', array(), $options['ancestor'] ); 
  265. $field = acf_get_child_field_from_parent_field( $options['field_key'], $ancestor ); 
  266. else 
  267. $field = apply_filters('acf/load_field', array(), $options['field_key'] ); 
  268.  
  269.  
  270. // get the post from which this field is rendered on 
  271. $the_post = get_post( $options['post_id'] ); 
  272.  
  273.  
  274. // filters 
  275. $options = apply_filters('acf/fields/relationship/query', $options, $field, $the_post); 
  276. $options = apply_filters('acf/fields/relationship/query/name=' . $field['_name'], $options, $field, $the_post ); 
  277. $options = apply_filters('acf/fields/relationship/query/key=' . $field['key'], $options, $field, $the_post ); 
  278.  
  279.  
  280. // query 
  281. $wp_query = new WP_Query( $options ); 
  282.  
  283.  
  284. // global 
  285. global $post; 
  286.  
  287.  
  288. // loop 
  289. while( $wp_query->have_posts() ) { 
  290.  
  291. $wp_query->the_post(); 
  292.  
  293.  
  294. // get title 
  295. $title = $this->get_result($post, $field, $the_post, $options); 
  296.  
  297.  
  298. // update html 
  299. $r['html'] .= '<li><a href="' . get_permalink($post->ID) . '" data-post_id="' . $post->ID . '">' . $title . '<span class="acf-button-add"></span></a></li>'; 
  300.  
  301.  
  302.  
  303. // next page 
  304. if( (int)$options['paged'] >= $wp_query->max_num_pages ) { 
  305.  
  306. $r['next_page_exists'] = 0; 
  307.  
  308.  
  309.  
  310. // reset 
  311. wp_reset_postdata(); 
  312.  
  313.  
  314. // return JSON 
  315. echo json_encode( $r ); 
  316.  
  317. die(); 
  318.  
  319.  
  320.  
  321. /** 
  322. * create_field() 
  323. * Create the HTML interface for your field 
  324. * @param $field - an array holding all the field's data 
  325. * @type action 
  326. * @since 3.6 
  327. * @date 23/01/13 
  328. */ 
  329.  
  330. function create_field( $field ) 
  331. // global 
  332. global $post; 
  333.  
  334.  
  335. // no row limit? 
  336. if( !$field['max'] || $field['max'] < 1 ) 
  337. $field['max'] = 9999; 
  338.  
  339.  
  340. // class 
  341. $class = ''; 
  342. if( $field['filters'] ) 
  343. foreach( $field['filters'] as $filter ) 
  344. $class .= ' has-' . $filter; 
  345.  
  346. $attributes = array( 
  347. 'max' => $field['max'],  
  348. 's' => '',  
  349. 'paged' => 1,  
  350. 'post_type' => implode(', ', $field['post_type']),  
  351. 'taxonomy' => implode(', ', $field['taxonomy']),  
  352. 'field_key' => $field['key'] 
  353. ); 
  354.  
  355.  
  356. // Lang 
  357. if( defined('ICL_LANGUAGE_CODE') ) 
  358. $attributes['lang'] = ICL_LANGUAGE_CODE; 
  359.  
  360.  
  361. // parent 
  362. preg_match('/\[(field_.*?)\]/', $field['name'], $ancestor); 
  363. if( isset($ancestor[1]) && $ancestor[1] != $field['key']) 
  364. $attributes['ancestor'] = $ancestor[1]; 
  365.  
  366. ?> 
  367. <div class="acf_relationship<?php echo $class; ?>"<?php foreach( $attributes as $k => $v ): ?> data-<?php echo $k; ?>="<?php echo $v; ?>"<?php endforeach; ?>> 
  368.  
  369.  
  370. <!-- Hidden Blank default value --> 
  371. <input type="hidden" name="<?php echo $field['name']; ?>" value="" /> 
  372.  
  373.  
  374. <!-- Left List --> 
  375. <div class="relationship_left"> 
  376. <table class="widefat"> 
  377. <thead> 
  378. <?php if(in_array( 'search', $field['filters']) ): ?> 
  379. <tr> 
  380. <th> 
  381. <input class="relationship_search" placeholder="<?php _e("Search...", 'acf'); ?>" type="text" id="relationship_<?php echo $field['name']; ?>" /> 
  382. </th> 
  383. </tr> 
  384. <?php endif; ?> 
  385. <?php if(in_array( 'post_type', $field['filters']) ): ?> 
  386. <tr> 
  387. <th> 
  388. <?php  
  389.  
  390. // vars 
  391. $choices = array( 
  392. 'all' => __("Filter by post type", 'acf') 
  393. ); 
  394.  
  395.  
  396. if( in_array('all', $field['post_type']) ) 
  397. $post_types = apply_filters( 'acf/get_post_types', array() ); 
  398. $choices = array_merge( $choices, $post_types); 
  399. else 
  400. foreach( $field['post_type'] as $post_type ) 
  401. $choices[ $post_type ] = $post_type; 
  402.  
  403.  
  404. // create field 
  405. do_action('acf/create_field', array( 
  406. 'type' => 'select',  
  407. 'name' => '',  
  408. 'class' => 'select-post_type',  
  409. 'value' => '',  
  410. 'choices' => $choices,  
  411. )); 
  412.  
  413. ?> 
  414. </th> 
  415. </tr> 
  416. <?php endif; ?> 
  417. </thead> 
  418. </table> 
  419. <ul class="bl relationship_list"> 
  420. <li class="load-more"> 
  421. <div class="acf-loading"></div> 
  422. </li> 
  423. </ul> 
  424. </div> 
  425. <!-- /Left List --> 
  426.  
  427. <!-- Right List --> 
  428. <div class="relationship_right"> 
  429. <ul class="bl relationship_list"> 
  430. <?php 
  431.  
  432. if( $field['value'] ) 
  433. foreach( $field['value'] as $p ) 
  434. $title = $this->get_result($p, $field, $post); 
  435.  
  436.  
  437. echo '<li> 
  438. <a href="' . get_permalink($p->ID) . '" class="" data-post_id="' . $p->ID . '">' . $title . '<span class="acf-button-remove"></span></a> 
  439. <input type="hidden" name="' . $field['name'] . '[]" value="' . $p->ID . '" /> 
  440. </li>'; 
  441.  
  442.  
  443.  
  444. ?> 
  445. </ul> 
  446. </div> 
  447. <!-- / Right List --> 
  448.  
  449. </div> 
  450. <?php 
  451.  
  452.  
  453.  
  454. /** 
  455. * create_options() 
  456. * Create extra options for your field. This is rendered when editing a field. 
  457. * The value of $field['name'] can be used (like bellow) to save extra data to the $field 
  458. * @type action 
  459. * @since 3.6 
  460. * @date 23/01/13 
  461. * @param $field - an array holding all the field's data 
  462. */ 
  463.  
  464. function create_options( $field ) 
  465. // vars 
  466. $key = $field['name']; 
  467.  
  468. ?> 
  469. <tr class="field_option field_option_<?php echo $this->name; ?>"> 
  470. <td class="label"> 
  471. <label><?php _e("Return Format", 'acf'); ?></label> 
  472. <p><?php _e("Specify the returned value on front end", 'acf') ?></p> 
  473. </td> 
  474. <td> 
  475. <?php 
  476. do_action('acf/create_field', array( 
  477. 'type' => 'radio',  
  478. 'name' => 'fields['.$key.'][return_format]',  
  479. 'value' => $field['return_format'],  
  480. 'layout' => 'horizontal',  
  481. 'choices' => array( 
  482. 'object' => __("Post Objects", 'acf'),  
  483. 'id' => __("Post IDs", 'acf') 
  484. )); 
  485. ?> 
  486. </td> 
  487. </tr> 
  488. <tr class="field_option field_option_<?php echo $this->name; ?>"> 
  489. <td class="label"> 
  490. <label for=""><?php _e("Post Type", 'acf'); ?></label> 
  491. </td> 
  492. <td> 
  493. <?php  
  494.  
  495. $choices = array( 
  496. 'all' => __("All", 'acf') 
  497. ); 
  498. $choices = apply_filters('acf/get_post_types', $choices); 
  499.  
  500.  
  501. do_action('acf/create_field', array( 
  502. 'type' => 'select',  
  503. 'name' => 'fields['.$key.'][post_type]',  
  504. 'value' => $field['post_type'],  
  505. 'choices' => $choices,  
  506. 'multiple' => 1,  
  507. )); 
  508.  
  509. ?> 
  510. </td> 
  511. </tr> 
  512. <tr class="field_option field_option_<?php echo $this->name; ?>"> 
  513. <td class="label"> 
  514. <label><?php _e("Filter from Taxonomy", 'acf'); ?></label> 
  515. </td> 
  516. <td> 
  517. <?php  
  518. $choices = array( 
  519. '' => array( 
  520. 'all' => __("All", 'acf') 
  521. ); 
  522. $simple_value = false; 
  523. $choices = apply_filters('acf/get_taxonomies_for_select', $choices, $simple_value); 
  524.  
  525.  
  526. do_action('acf/create_field', array( 
  527. 'type' => 'select',  
  528. 'name' => 'fields['.$key.'][taxonomy]',  
  529. 'value' => $field['taxonomy'],  
  530. 'choices' => $choices,  
  531. 'multiple' => 1,  
  532. )); 
  533. ?> 
  534. </td> 
  535. </tr> 
  536. <tr class="field_option field_option_<?php echo $this->name; ?>"> 
  537. <td class="label"> 
  538. <label><?php _e("Filters", 'acf'); ?></label> 
  539. </td> 
  540. <td> 
  541. <?php  
  542. do_action('acf/create_field', array( 
  543. 'type' => 'checkbox',  
  544. 'name' => 'fields['.$key.'][filters]',  
  545. 'value' => $field['filters'],  
  546. 'choices' => array( 
  547. 'search' => __("Search", 'acf'),  
  548. 'post_type' => __("Post Type Select", 'acf'),  
  549. )); 
  550. ?> 
  551. </td> 
  552. </tr> 
  553. <tr class="field_option field_option_<?php echo $this->name; ?>"> 
  554. <td class="label"> 
  555. <label><?php _e("Elements", 'acf'); ?></label> 
  556. <p><?php _e("Selected elements will be displayed in each result", 'acf') ?></p> 
  557. </td> 
  558. <td> 
  559. <?php  
  560. do_action('acf/create_field', array( 
  561. 'type' => 'checkbox',  
  562. 'name' => 'fields['.$key.'][result_elements]',  
  563. 'value' => $field['result_elements'],  
  564. 'choices' => array( 
  565. 'featured_image' => __("Featured Image", 'acf'),  
  566. 'post_title' => __("Post Title", 'acf'),  
  567. 'post_type' => __("Post Type", 'acf'),  
  568. ),  
  569. 'disabled' => array( 
  570. 'post_title' 
  571. )); 
  572. ?> 
  573. </td> 
  574. </tr> 
  575. <tr class="field_option field_option_<?php echo $this->name; ?>"> 
  576. <td class="label"> 
  577. <label><?php _e("Maximum posts", 'acf'); ?></label> 
  578. </td> 
  579. <td> 
  580. <?php  
  581. do_action('acf/create_field', array( 
  582. 'type' => 'number',  
  583. 'name' => 'fields['.$key.'][max]',  
  584. 'value' => $field['max'],  
  585. )); 
  586. ?> 
  587. </td> 
  588. </tr> 
  589. <?php 
  590.  
  591.  
  592.  
  593. /** 
  594. * format_value() 
  595. * This filter is appied to the $value after it is loaded from the db and before it is passed to the create_field action 
  596. * @type filter 
  597. * @since 3.6 
  598. * @date 23/01/13 
  599. * @param $value - the value which was loaded from the database 
  600. * @param $post_id - the $post_id from which the value was loaded 
  601. * @param $field - the field array holding all the field options 
  602. * @return $value - the modified value 
  603. */ 
  604.  
  605. function format_value( $value, $post_id, $field ) 
  606. // empty? 
  607. if( !empty($value) ) 
  608. // Pre 3.3.3, the value is a string coma seperated 
  609. if( is_string($value) ) 
  610. $value = explode(', ', $value); 
  611.  
  612.  
  613. // convert to integers 
  614. if( is_array($value) ) 
  615. $value = array_map('intval', $value); 
  616.  
  617. // convert into post objects 
  618. $value = $this->get_posts( $value ); 
  619.  
  620.  
  621.  
  622. // return value 
  623. return $value;  
  624.  
  625.  
  626. /** 
  627. * format_value_for_api() 
  628. * This filter is appied to the $value after it is loaded from the db and before it is passed back to the api functions such as the_field 
  629. * @type filter 
  630. * @since 3.6 
  631. * @date 23/01/13 
  632. * @param $value - the value which was loaded from the database 
  633. * @param $post_id - the $post_id from which the value was loaded 
  634. * @param $field - the field array holding all the field options 
  635. * @return $value - the modified value 
  636. */ 
  637.  
  638. function format_value_for_api( $value, $post_id, $field ) 
  639. // empty? 
  640. if( !$value ) 
  641. return $value; 
  642.  
  643.  
  644. // Pre 3.3.3, the value is a string coma seperated 
  645. if( is_string($value) ) 
  646. $value = explode(', ', $value); 
  647.  
  648.  
  649. // empty? 
  650. if( !is_array($value) || empty($value) ) 
  651. return $value; 
  652.  
  653.  
  654. // convert to integers 
  655. $value = array_map('intval', $value); 
  656.  
  657.  
  658. // return format 
  659. if( $field['return_format'] == 'object' ) 
  660. $value = $this->get_posts( $value );  
  661.  
  662.  
  663. // return 
  664. return $value; 
  665.  
  666.  
  667.  
  668. /** 
  669. * get_posts 
  670. * This function will take an array of post_id's ($value) and return an array of post_objects 
  671. * @type function 
  672. * @date 7/08/13 
  673. * @param $post_ids (array) the array of post ID's 
  674. * @return (array) an array of post objects 
  675. */ 
  676.  
  677. function get_posts( $post_ids ) 
  678. // validate 
  679. if( empty($post_ids) ) 
  680. return $post_ids; 
  681.  
  682.  
  683. // vars 
  684. $r = array(); 
  685.  
  686.  
  687. // find posts (DISTINCT POSTS) 
  688. $posts = get_posts(array( 
  689. 'numberposts' => -1,  
  690. 'post__in' => $post_ids,  
  691. 'post_type' => apply_filters('acf/get_post_types', array()),  
  692. 'post_status' => 'any',  
  693. )); 
  694.  
  695.  
  696. $ordered_posts = array(); 
  697. foreach( $posts as $p ) 
  698. // create array to hold value data 
  699. $ordered_posts[ $p->ID ] = $p; 
  700.  
  701.  
  702. // override value array with attachments 
  703. foreach( $post_ids as $k => $v) 
  704. // check that post exists (my have been trashed) 
  705. if( isset($ordered_posts[ $v ]) ) 
  706. $r[] = $ordered_posts[ $v ]; 
  707.  
  708.  
  709. // return 
  710. return $r; 
  711.  
  712.  
  713. /** 
  714. * update_value() 
  715. * This filter is appied to the $value before it is updated in the db 
  716. * @type filter 
  717. * @since 3.6 
  718. * @date 23/01/13 
  719. * @param $value - the value which will be saved in the database 
  720. * @param $post_id - the $post_id of which the value will be saved 
  721. * @param $field - the field array holding all the field options 
  722. * @return $value - the modified value 
  723. */ 
  724.  
  725. function update_value( $value, $post_id, $field ) 
  726. // validate 
  727. if( empty($value) ) 
  728. return $value; 
  729.  
  730.  
  731. if( is_string($value) ) 
  732. // string 
  733. $value = explode(', ', $value); 
  734.  
  735. elseif( is_object($value) && isset($value->ID) ) 
  736. // object 
  737. $value = array( $value->ID ); 
  738.  
  739. elseif( is_array($value) ) 
  740. // array 
  741. foreach( $value as $k => $v ) { 
  742.  
  743. // object? 
  744. if( is_object($v) && isset($v->ID) ) 
  745. $value[ $k ] = $v->ID; 
  746.  
  747.  
  748.  
  749. // save value as strings, so we can clearly search for them in SQL LIKE statements 
  750. $value = array_map('strval', $value); 
  751.  
  752.  
  753. return $value; 
  754.