Source: includes/classes/Feature/Facets/Types/Meta/Renderer.php

  1. <?php
  2. /**
  3. * Class responsible for rendering the filters.
  4. *
  5. * @since 4.3.0
  6. * @package elasticpress
  7. */
  8. namespace ElasticPress\Feature\Facets\Types\Meta;
  9. use ElasticPress\Features;
  10. if ( ! defined( 'ABSPATH' ) ) {
  11. exit; // Exit if accessed directly.
  12. }
  13. /**
  14. * Facets render class
  15. */
  16. class Renderer extends \ElasticPress\Feature\Facets\Renderer {
  17. /**
  18. * Holds the meta field selected.
  19. *
  20. * @var string
  21. */
  22. protected $meta_field = '';
  23. /**
  24. * Whether the term count should be displayed or not.
  25. *
  26. * @var bool
  27. */
  28. protected $display_count = false;
  29. /**
  30. * Output the widget or block HTML.
  31. *
  32. * @param array $args Widget args
  33. * @param array $instance Instance settings
  34. */
  35. public function render( $args, $instance ) {
  36. $instance = wp_parse_args(
  37. $instance,
  38. [
  39. 'title' => '',
  40. ]
  41. );
  42. $this->meta_field = $instance['facet'];
  43. $this->display_count = $instance['displayCount'];
  44. if ( ! $this->should_render() ) {
  45. return;
  46. }
  47. $args = wp_parse_args(
  48. $args,
  49. [
  50. 'before_widget' => '',
  51. 'before_title' => '',
  52. 'after_title' => '',
  53. 'after_widget' => '',
  54. ]
  55. );
  56. $feature = Features::factory()->get_registered_feature( 'facets' );
  57. $facet_type = $feature->types['meta'];
  58. $selected_meta = $this->get_selected_meta();
  59. $selected_filters = $feature->get_selected();
  60. /**
  61. * Get all the terms so we know if we should output the widget
  62. */
  63. $raw_values = $facet_type->get_meta_values( $instance['facet'] );
  64. $values = [];
  65. foreach ( $raw_values as $raw_value ) {
  66. $values[ $raw_value ] = [
  67. 'value' => $raw_value,
  68. 'name' => $raw_value,
  69. 'count' => 0,
  70. 'is_selected' => in_array( $raw_value, $selected_meta, true ),
  71. ];
  72. if ( ! empty( $GLOBALS['ep_facet_aggs'][ $facet_type->get_filter_name() . $this->meta_field ][ $raw_value ] ) ) {
  73. $values[ $raw_value ]['count'] = (int) $GLOBALS['ep_facet_aggs'][ $facet_type->get_filter_name() . $this->meta_field ][ $raw_value ];
  74. }
  75. }
  76. /**
  77. * Filter meta values, their labels, and count.
  78. *
  79. * If you need to display a value with a different label:
  80. * ```
  81. * add_filter(
  82. * 'ep_facet_meta_all_values',
  83. * function( $values, $meta_field ) {
  84. * if ( 'my_field' !== $meta_field ) {
  85. * return $values;
  86. * }
  87. * if ( isset( $values['unreadable_value'] ) ) {
  88. * $values['unreadable_value']['name'] = 'My Readable Value';
  89. * }
  90. * return $values;
  91. * },
  92. * 10,
  93. * 2
  94. * );
  95. * ```
  96. *
  97. * @hook ep_facet_meta_values_with_count
  98. * @since 4.3.0
  99. * @param {array} $values Values with names and counts
  100. * @param {string} $meta_field Meta field
  101. * @param {array} $instance Block info
  102. * @return {array} New values
  103. */
  104. $values = apply_filters( 'ep_facet_meta_all_values', $values, $this->meta_field, $instance );
  105. if ( empty( $values ) ) {
  106. return;
  107. }
  108. echo wp_kses_post( $args['before_widget'] );
  109. if ( ! empty( $instance['title'] ) ) {
  110. echo wp_kses_post( $args['before_title'] . apply_filters( 'widget_title', $instance['title'] ) . $args['after_title'] );
  111. }
  112. /**
  113. * Filter facet search threshold
  114. *
  115. * @hook ep_facet_search_threshold
  116. * @param {int} $search_threshold Search threshold
  117. * @param {string} $taxonomy Current taxonomy
  118. * @param {string} $context Hint about where the value will be used
  119. * @param {array} $instance Block instance
  120. * @return {int} New threshold
  121. */
  122. $search_threshold = apply_filters( 'ep_facet_search_threshold', 15, $this->meta_field, 'meta', $instance );
  123. ?>
  124. <div class="terms <?php if ( count( $values ) > $search_threshold ) : ?>searchable<?php endif; ?>">
  125. <?php if ( count( $values ) > $search_threshold ) : ?>
  126. <input class="facet-search" type="search" placeholder="<?php echo esc_attr( $instance['searchPlaceholder'] ); ?>">
  127. <?php endif; ?>
  128. <div class="inner">
  129. <?php
  130. $orderby = $instance['orderby'] ?? 'count';
  131. $order = $instance['order'] ?? 'desc';
  132. $values = $this->order_values( $values, $orderby, $order );
  133. foreach ( $values as $raw_value => $item ) {
  134. $field_filters = $selected_filters;
  135. if ( $item['is_selected'] ) {
  136. unset( $field_filters[ $facet_type->get_filter_type() ][ $this->meta_field ]['terms'][ $raw_value ] );
  137. } else {
  138. $field_filters[ $facet_type->get_filter_type() ][ $this->meta_field ]['terms'][ $raw_value ] = 1;
  139. }
  140. // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
  141. echo $this->get_facet_item_value_html(
  142. $item,
  143. $feature->build_query_url( $field_filters )
  144. );
  145. // phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped
  146. }
  147. ?>
  148. </div>
  149. </div>
  150. <?php
  151. // Enqueue Script & Styles
  152. wp_enqueue_script( 'elasticpress-facets' );
  153. wp_enqueue_style( 'elasticpress-facets' );
  154. echo wp_kses_post( $args['after_widget'] );
  155. }
  156. /**
  157. * Get the markup for an individual facet item.
  158. *
  159. * @param array $item Facet item.
  160. * @param string $url Filter URL.
  161. * @return string HTML for an individual facet term.
  162. */
  163. public function get_facet_item_value_html( $item, string $url ): string {
  164. $href = sprintf(
  165. 'href="%s"',
  166. esc_url( $url )
  167. );
  168. $label = $item['name'];
  169. if ( $this->display_count ) {
  170. $label .= ' <span>(' . $item['count'] . ')</span>';
  171. }
  172. /**
  173. * Filter the label for an individual meta value.
  174. *
  175. * @since 4.3.0
  176. * @hook ep_facet_meta_value_label
  177. * @param {string} $label Facet meta value label.
  178. * @param {array} $item Value array. It contains `value`, `name`, `count`, and `is_selected`.
  179. * @return {string} Individual facet meta value label.
  180. */
  181. $label = apply_filters( 'ep_facet_meta_value_label', $label, $item );
  182. /**
  183. * Filter the accessible label for an individual facet meta value link.
  184. *
  185. * Used as the aria-label attribute for filter links. The accessible
  186. * label should include additional context around what action will be
  187. * performed by visiting the link, such as whether the filter will be
  188. * added or removed.
  189. *
  190. * @since 4.3.0
  191. * @hook ep_facet_meta_value_accessible_label
  192. * @param {string} $label Facet meta value accessible label.
  193. * @param {array} $item Value array. It contains `value`, `name`, `count`, and `is_selected`.
  194. * @return {string} Individual facet term accessible label.
  195. */
  196. $accessible_label = apply_filters(
  197. 'ep_facet_meta_value_accessible_label',
  198. $item['is_selected']
  199. /* translators: %s: Filter term name. */
  200. ? sprintf( __( 'Remove filter: %s', 'elasticpress' ), $label )
  201. /* translators: %s: Filter term name. */
  202. : sprintf( __( 'Apply filter: %s', 'elasticpress' ), $label ),
  203. $item
  204. );
  205. $link = sprintf(
  206. '<a aria-label="%1$s" %2$s rel="nofollow"><div class="ep-checkbox %3$s" role="presentation"></div>%4$s</a>',
  207. esc_attr( $accessible_label ),
  208. $item['count'] ? $href : 'aria-role="link" aria-disabled="true"',
  209. $item['is_selected'] ? 'checked' : '',
  210. wp_kses_post( $label )
  211. );
  212. $html = sprintf(
  213. '<div class="term level-%1$d %2$s %3$s" data-term-name="%4$s" data-term-slug="%5$s">%6$s</div>',
  214. 0,
  215. $item['is_selected'] ? 'selected' : '',
  216. ! $item['count'] ? 'empty-term' : '',
  217. esc_attr( strtolower( $item['value'] ) ),
  218. esc_attr( strtolower( $item['value'] ) ),
  219. $link
  220. );
  221. /**
  222. * Filter the HTML for an individual facet meta value.
  223. *
  224. * For term search to work correctly the outermost wrapper of the term
  225. * HTML must have data-term-name and data-term-slug attributes set to
  226. * lowercase versions of the term name and slug respectively.
  227. *
  228. * @since 4.3.0
  229. * @hook ep_facet_meta_value_html
  230. * @param {string} $html Facet meta value HTML.
  231. * @param {array} $item Value array. It contains `value`, `name`, `count`, and `is_selected`.
  232. * @param {string} $url Filter URL.
  233. * @return {string} Individual facet meta value HTML.
  234. */
  235. return apply_filters( 'ep_facet_meta_value_html', $html, $item, $url );
  236. }
  237. /**
  238. * Determine if the block/widget should or not be rendered.
  239. *
  240. * @return boolean
  241. */
  242. protected function should_render(): bool {
  243. global $wp_query;
  244. if ( empty( $this->meta_field ) ) {
  245. return false;
  246. }
  247. $feature = Features::factory()->get_registered_feature( 'facets' );
  248. if ( $wp_query->get( 'ep_facet', false ) && ! $feature->is_facetable( $wp_query ) ) {
  249. return false;
  250. }
  251. $es_success = ( ! empty( $wp_query->elasticsearch_success ) ) ? true : false;
  252. if ( ! $es_success ) {
  253. return false;
  254. }
  255. return true;
  256. }
  257. /**
  258. * Get selected values.
  259. *
  260. * @return array
  261. */
  262. protected function get_selected_meta(): array {
  263. $feature = Features::factory()->get_registered_feature( 'facets' );
  264. $selected_filters = $feature->get_selected();
  265. $selected_meta = [];
  266. if ( isset( $selected_filters['meta'][ $this->meta_field ] ) && isset( $selected_filters['meta'][ $this->meta_field ]['terms'] ) ) {
  267. $selected_meta = array_map( 'trim', array_keys( $selected_filters['meta'][ $this->meta_field ]['terms'] ) );
  268. }
  269. return $selected_meta;
  270. }
  271. /**
  272. * DEPRECATED. Get the markup for an individual facet item.
  273. *
  274. * @param array $value Value.
  275. * @param string $url Filter URL.
  276. * @return string HTML for an individual facet term.
  277. */
  278. public function get_meta_value_html( array $value, string $url ): string {
  279. _deprecated_function( __METHOD__, '4.7.0', '\ElasticPress\Feature\Facets\Types\Meta\Renderer::get_facet_item_value_html()' );
  280. return $this->get_facet_item_value_html( $value, $url );
  281. }
  282. }