From a1a1c823229ef3401250505ca936e4c090130ba0 Mon Sep 17 00:00:00 2001 From: Felipe Elia Date: Wed, 25 Jan 2023 17:35:11 -0300 Subject: [PATCH 01/10] Meta range facet - Initial Commit --- assets/js/blocks/facets/meta-range/block.json | 26 ++ assets/js/blocks/facets/meta-range/edit.js | 95 +++++++ assets/js/blocks/facets/meta-range/index.js | 15 ++ includes/classes/Feature/Facets/Facets.php | 12 +- .../Feature/Facets/Types/Meta/FacetType.php | 5 +- .../Feature/Facets/Types/MetaRange/Block.php | 231 ++++++++++++++++++ .../Facets/Types/MetaRange/FacetType.php | 194 +++++++++++++++ .../Facets/Types/MetaRange/Renderer.php | 101 ++++++++ package.json | 1 + 9 files changed, 676 insertions(+), 4 deletions(-) create mode 100644 assets/js/blocks/facets/meta-range/block.json create mode 100644 assets/js/blocks/facets/meta-range/edit.js create mode 100644 assets/js/blocks/facets/meta-range/index.js create mode 100644 includes/classes/Feature/Facets/Types/MetaRange/Block.php create mode 100644 includes/classes/Feature/Facets/Types/MetaRange/FacetType.php create mode 100644 includes/classes/Feature/Facets/Types/MetaRange/Renderer.php diff --git a/assets/js/blocks/facets/meta-range/block.json b/assets/js/blocks/facets/meta-range/block.json new file mode 100644 index 0000000000..1233331963 --- /dev/null +++ b/assets/js/blocks/facets/meta-range/block.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 2, + "title": "Facet by Meta Range (ElasticPress)", + "textdomain": "elasticpress", + "name": "elasticpress/facet-meta-range", + "icon": "feedback", + "category": "widgets", + "attributes": { + "facet": { + "type": "string", + "default": "" + }, + "min": { + "type": "integer" + }, + "max": { + "type": "integer" + } + }, + "supports": { + "html": false + }, + "editorScript": "ep-facets-meta-range-block-script", + "style": "file:/../../../../../dist/css/facets-styles.css" +} \ No newline at end of file diff --git a/assets/js/blocks/facets/meta-range/edit.js b/assets/js/blocks/facets/meta-range/edit.js new file mode 100644 index 0000000000..af0dacd948 --- /dev/null +++ b/assets/js/blocks/facets/meta-range/edit.js @@ -0,0 +1,95 @@ +/* global facetMetaBlock */ + +import { InspectorControls, useBlockProps } from '@wordpress/block-editor'; +import { PanelBody, TextControl, Spinner, Placeholder, SelectControl } from '@wordpress/components'; +import { + Fragment, + useEffect, + useState, + useCallback, + createInterpolateElement, +} from '@wordpress/element'; +import apiFetch from '@wordpress/api-fetch'; +import { __ } from '@wordpress/i18n'; + +const FacetBlockEdit = (props) => { + const { attributes, setAttributes } = props; + const [metaKeys, setMetaKeys] = useState([]); + const [preview, setPreview] = useState(''); + const [loading, setLoading] = useState(false); + const { facet, min, max } = attributes; + + const blockProps = useBlockProps(); + + const load = useCallback(async () => { + const metaKeys = await apiFetch({ + path: '/elasticpress/v1/facets/meta-range/keys', + }); + setMetaKeys(metaKeys); + }, [setMetaKeys]); + + useEffect(load, [load]); + + useEffect(() => { + setLoading(true); + const params = new URLSearchParams({ + facet, + min, + max, + }); + apiFetch({ + path: `/elasticpress/v1/facets/meta-range/block-preview?${params}`, + }) + .then((preview) => setPreview(preview)) + .finally(() => setLoading(false)); + }, [facet, min, max]); + + return ( + + + + sync your content', + 'elasticpress', + ), + // eslint-disable-next-line jsx-a11y/anchor-has-content, jsx-a11y/control-has-associated-label + { a: }, + )} + value={facet} + options={[ + ...metaKeys.map((metaKey) => ({ + label: metaKey, + value: metaKey, + })), + ]} + onChange={(value) => setAttributes({ facet: value })} + /> + setAttributes({ min: value })} + /> + setAttributes({ min: value })} + /> + + + +
+ {loading && ( + + + + )} + {/* eslint-disable-next-line react/no-danger */} + {!loading &&
} +
+ + ); +}; +export default FacetBlockEdit; diff --git a/assets/js/blocks/facets/meta-range/index.js b/assets/js/blocks/facets/meta-range/index.js new file mode 100644 index 0000000000..f2e7dac9cd --- /dev/null +++ b/assets/js/blocks/facets/meta-range/index.js @@ -0,0 +1,15 @@ +/** + * WordPress dependencies. + */ +import { registerBlockType } from '@wordpress/blocks'; + +/** + * Internal dependencies. + */ +import edit from './edit'; +import block from './block.json'; + +registerBlockType(block, { + edit, + save: () => {}, +}); diff --git a/includes/classes/Feature/Facets/Facets.php b/includes/classes/Feature/Facets/Facets.php index 84d3f105b1..d250f7a4d5 100644 --- a/includes/classes/Feature/Facets/Facets.php +++ b/includes/classes/Feature/Facets/Facets.php @@ -50,8 +50,9 @@ public function __construct() { ]; $types = [ - 'taxonomy' => __NAMESPACE__ . '\Types\Taxonomy\FacetType', - 'meta' => __NAMESPACE__ . '\Types\Meta\FacetType', + 'taxonomy' => __NAMESPACE__ . '\Types\Taxonomy\FacetType', + 'meta' => __NAMESPACE__ . '\Types\Meta\FacetType', + 'meta-range' => __NAMESPACE__ . '\Types\MetaRange\FacetType', ]; /** @@ -351,12 +352,17 @@ public function get_aggs( $response, $query, $query_args, $query_object ) { continue; } - if ( ! is_array( $agg ) || empty( $agg['buckets'] ) ) { + if ( ! is_array( $agg ) || ( empty( $agg['buckets'] ) && empty( $agg['value'] ) ) ) { continue; } $GLOBALS['ep_facet_aggs'][ $key ] = []; + if ( ! empty( $agg['value'] ) ) { + $GLOBALS['ep_facet_aggs'][ $key ] = $agg['value']; + continue; + } + foreach ( $agg['buckets'] as $bucket ) { $GLOBALS['ep_facet_aggs'][ $key ][ $bucket['key'] ] = $bucket['doc_count']; } diff --git a/includes/classes/Feature/Facets/Types/Meta/FacetType.php b/includes/classes/Feature/Facets/Types/Meta/FacetType.php index ed60ee0cea..f6ac0a0516 100644 --- a/includes/classes/Feature/Facets/Types/Meta/FacetType.php +++ b/includes/classes/Feature/Facets/Types/Meta/FacetType.php @@ -251,7 +251,10 @@ public function get_facets_meta_fields() { continue; } - if ( false === strpos( $instance['content'], 'elasticpress/facet-meta' ) ) { + if ( + false !== strpos( $instance['content'], 'elasticpress/facet-meta-range' ) + || false === strpos( $instance['content'], 'elasticpress/facet-meta' ) + ) { continue; } diff --git a/includes/classes/Feature/Facets/Types/MetaRange/Block.php b/includes/classes/Feature/Facets/Types/MetaRange/Block.php new file mode 100644 index 0000000000..138553a69c --- /dev/null +++ b/includes/classes/Feature/Facets/Types/MetaRange/Block.php @@ -0,0 +1,231 @@ + [ $this, 'render_block' ], + ] + ); + + wp_localize_script( 'ep-facets-meta-range-block-script', 'facetMetaBlock', [ 'syncUrl' => Utils\get_sync_url() ] ); + } + + + /** + * Setup REST endpoints for the feature. + */ + public function setup_endpoints() { + register_rest_route( + 'elasticpress/v1', + 'facets/meta-range/keys', + [ + 'methods' => 'GET', + 'permission_callback' => [ $this, 'check_facets_meta_rest_permission' ], + 'callback' => [ $this, 'get_rest_registered_metakeys' ], + ] + ); + register_rest_route( + 'elasticpress/v1', + 'facets/meta-range/block-preview', + [ + 'methods' => 'GET', + 'permission_callback' => [ $this, 'check_facets_meta_rest_permission' ], + 'callback' => [ $this, 'render_block_preview' ], + 'args' => [ + 'facet' => [ + 'sanitize_callback' => 'sanitize_text_field', + ], + 'min' => [ + 'sanitize_callback' => 'absint', + ], + 'max' => [ + 'sanitize_callback' => 'absint', + ], + ], + + ] + ); + } + + /** + * Check permissions of the /facets/taxonomies REST endpoint. + * + * @return WP_Error|true + */ + public function check_facets_meta_rest_permission() { + if ( ! current_user_can( 'edit_theme_options' ) ) { + return new \WP_Error( 'ep_rest_forbidden', esc_html__( 'Sorry, you cannot view this resource.', 'elasticpress' ), array( 'status' => 401 ) ); + } + + return true; + } + + /** + * Render the block. + * + * @param array $attributes Block attributes. + */ + public function render_block( $attributes ) { + $attributes = $this->parse_attributes( $attributes ); + + /** This filter is documented in includes/classes/Feature/Facets/Types/Taxonomy/Block.php */ + $renderer_class = apply_filters( 'ep_facet_renderer_class', __NAMESPACE__ . '\Renderer', 'meta-range', 'block', $attributes ); + $renderer = new $renderer_class(); + + ob_start(); + ?> +
+ render( [], $attributes ); ?> +
+ get_registered_feature( 'search' ); + + $attributes = $this->parse_attributes( + [ + 'facet' => $request->get_param( 'facet' ), + 'min' => $request->get_param( 'min' ), + 'max' => $request->get_param( 'max' ), + ] + ); + + add_filter( + 'ep_facet_meta_fields', + function ( $meta_fields ) use ( $attributes ) { + $meta_fields = [ $attributes['facet'] ]; + return $meta_fields; + } + ); + + $wp_query = new \WP_Query( + [ + 'post_type' => $search->get_searchable_post_types(), + 'per_page' => 1, + ] + ); + + /** This filter is documented in includes/classes/Feature/Facets/Types/Taxonomy/Block.php */ + $renderer_class = apply_filters( 'ep_facet_renderer_class', __NAMESPACE__ . '\Renderer', 'meta-block', 'block', $attributes ); + $renderer = new $renderer_class(); + + ob_start(); + $renderer->render( [], $attributes ); + $block_content = ob_get_clean(); + + if ( empty( $block_content ) ) { + if ( empty( $attributes['facet'] ) ) { + return esc_html__( 'Preview not available', 'elasticpress' ); + } + + return sprintf( + /* translators: Meta field name */ + esc_html__( 'Preview for %s not available', 'elasticpress' ), + esc_html( $request->get_param( 'facet' ) ) + ); + } + + $block_content = preg_replace( '/href="(.*?)"/', 'href="#"', $block_content ); + return '
' . $block_content . '
'; + } + + /** + * Return an array of registered meta keys. + * + * @return array + */ + public function get_rest_registered_metakeys() { + $post_indexable = \ElasticPress\Indexables::factory()->get( 'post' ); + + try { + $meta_keys = $post_indexable->get_distinct_meta_field_keys(); + } catch ( \Throwable $th ) { + $meta_keys = []; + } + + return $meta_keys; + } + + /** + * Utilitary method to set default attributes. + * + * @param array $attributes Attributes passed + * @return array + */ + protected function parse_attributes( $attributes ) { + $attributes = wp_parse_args( + $attributes, + [ + 'facet' => '', + 'min' => 0, + 'max' => 100, + + ] + ); + if ( empty( $attributes['facet'] ) ) { + $registered_metakeys = $this->get_rest_registered_metakeys(); + if ( ! empty( $registered_metakeys ) ) { + $attributes['facet'] = reset( $registered_metakeys ); + } + } + return $attributes; + } +} diff --git a/includes/classes/Feature/Facets/Types/MetaRange/FacetType.php b/includes/classes/Feature/Facets/Types/MetaRange/FacetType.php new file mode 100644 index 0000000000..997decaf38 --- /dev/null +++ b/includes/classes/Feature/Facets/Types/MetaRange/FacetType.php @@ -0,0 +1,194 @@ +block = new Block(); + $this->block->setup(); + } + + /** + * Get the facet filter name. + * + * @return string The filter name. + */ + public function get_filter_name() : string { + /** + * Filter the facet filter name that's added to the URL + * + * @hook ep_facet_meta_range_filter_name + * @since 4.5.0 + * @param {string} Facet filter name + * @return {string} New facet filter name + */ + return apply_filters( 'ep_facet_meta_range_filter_name', 'ep_meta_range_filter_' ); + } + + /** + * Get the facet filter type. + * + * @return string The filter name. + */ + public function get_filter_type() : string { + /** + * Filter the facet filter type. Used by the Facet feature to organize filters. + * + * @hook ep_facet_meta_range_filter_type + * @since 4.5.0 + * @param {string} Facet filter type + * @return {string} New facet filter type + */ + return apply_filters( 'ep_facet_meta_range_filter_type', 'meta-range' ); + } + + /** + * Add selected filters to the Facet filter in the ES query + * + * @param array $filters Current Facet filters + * @return array + */ + public function add_query_filters( $filters ) { + $feature = Features::factory()->get_registered_feature( 'facets' ); + + $selected_filters = $feature->get_selected(); + if ( empty( $selected_filters ) || empty( $selected_filters[ $this->get_filter_type() ] ) ) { + return $filters; + } + + $meta_fields = $selected_filters[ $this->get_filter_type() ]; + + echo '
';
+		print_r( $meta_fields );
+		echo '
'; + + foreach ( $meta_fields as $meta_field => $values ) { + $values = array_keys( $values['terms'] ); + if ( 2 !== count( $values ) ) { + continue; + } + + list( $min, $max ) = $values; + + $filter = []; + if ( $min ) { + $filter['gte'] = $min; + } + if ( $max ) { + $filter['lte'] = $max; + } + $filters[] = [ + 'range' => [ + 'meta.' . $meta_field . '.long' => $filter, + ], + ]; + } + + return $filters; + } + + /** + * Add meta fields to facets aggs + * + * @param array $facet_aggs Facet Aggs array. + * @return array + */ + public function set_wp_query_aggs( $facet_aggs ) { + $facets_meta_fields = $this->get_facets_meta_fields(); + + foreach ( $facets_meta_fields as $meta_field ) { + /** + * Retrieve aggregations based on a custom field. This field must exist on the mapping and be numeric + * so ES can apply min and max to it. + * + * `meta..value` is *not* available, as that throws a `Fielddata is disabled on text fields by default` error. + * + * @since 4.5.0 + * @hook ep_facet_meta_range_use_field + * @param {string} $es_field The Elasticsearch field to use for this meta field + * @param {string} $meta_field The meta field key + * @return {string} The chosen ES field + */ + $facet_field = apply_filters( 'ep_facet_meta_range_use_field', 'long', $meta_field ); + + $facet_aggs[ $this->get_filter_name() . $meta_field . '_min' ] = array( + 'min' => array( + 'field' => 'meta.' . $meta_field . '.' . $facet_field, + ), + ); + + $facet_aggs[ $this->get_filter_name() . $meta_field . '_max' ] = array( + 'max' => array( + 'field' => 'meta.' . $meta_field . '.' . $facet_field, + ), + ); + } + + return $facet_aggs; + } + + /** + * Get all fields selected in all Facet blocks + * + * @return array + */ + public function get_facets_meta_fields() { + $facets_meta_fields = []; + + $widget_block_instances = ( new \WP_Widget_Block() )->get_settings(); + foreach ( $widget_block_instances as $instance ) { + if ( ! isset( $instance['content'] ) ) { + continue; + } + + if ( false === strpos( $instance['content'], 'elasticpress/facet-meta-range' ) ) { + continue; + } + + if ( ! preg_match_all( '/"facet":"(.*?)"/', $instance['content'], $matches ) ) { + continue; + } + + $facets_meta_fields = array_merge( $facets_meta_fields, $matches[1] ); + } + + /** + * Filter meta fields to be used in aggregations related to meta range blocks. + * + * @since 4.5.0 + * @hook ep_facet_meta_range_fields + * @param {string} $facets_meta_fields Array of meta field keys + * @return {string} The array of meta field keys + */ + return apply_filters( 'ep_facet_meta_range_fields', $facets_meta_fields ); + } +} diff --git a/includes/classes/Feature/Facets/Types/MetaRange/Renderer.php b/includes/classes/Feature/Facets/Types/MetaRange/Renderer.php new file mode 100644 index 0000000000..ca4376f18a --- /dev/null +++ b/includes/classes/Feature/Facets/Types/MetaRange/Renderer.php @@ -0,0 +1,101 @@ +meta_field = $instance['facet']; + + if ( ! $this->should_render() ) { + return; + } + + $feature = Features::factory()->get_registered_feature( 'facets' ); + + $facet_type = $feature->types['meta-range']; + + $min_field_name = $facet_type->get_filter_name() . $this->meta_field . '_min'; + $max_field_name = $facet_type->get_filter_name() . $this->meta_field . '_max'; + + $min = 0; + if ( ! empty( $GLOBALS['ep_facet_aggs'][ $min_field_name ] ) ) { + $min = $GLOBALS['ep_facet_aggs'][ $min_field_name ]; + } + + $max = 0; + if ( ! empty( $GLOBALS['ep_facet_aggs'][ $max_field_name ] ) ) { + $max = $GLOBALS['ep_facet_aggs'][ $max_field_name ]; + } + + $selected_filters = $feature->get_selected(); + unset( $selected_filters[ $facet_type->get_filter_type() ][ $this->meta_field ] ); + $form_action = $feature->build_query_url( $selected_filters ); + ?> +
+ + + +
+

From to

+ meta_field ) ) { + return false; + } + + $feature = Features::factory()->get_registered_feature( 'facets' ); + if ( $wp_query->get( 'ep_facet', false ) && ! $feature->is_facetable( $wp_query ) ) { + return false; + } + + $es_success = ( ! empty( $wp_query->elasticsearch_success ) ) ? true : false; + if ( ! $es_success ) { + return false; + } + + return true; + } +} diff --git a/package.json b/package.json index 4e0008851e..29cb207cdf 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "ordering-script": "./assets/js/ordering/index.js", "facets-block-script": "./assets/js/blocks/facets/taxonomy/index.js", "facets-meta-block-script": "./assets/js/blocks/facets/meta/index.js", + "facets-meta-range-block-script": "./assets/js/blocks/facets/meta-range/index.js", "related-posts-block-script": "./assets/js/blocks/related-posts/index.js", "settings-script": "./assets/js/settings.js", "sync-script": "./assets/js/sync/index.js", From aa16a6fb4306c2b8472433b4bf71244094b5ccb8 Mon Sep 17 00:00:00 2001 From: Felipe Elia Date: Thu, 26 Jan 2023 10:12:11 -0300 Subject: [PATCH 02/10] Apply new filter and keep prev selected --- .../Feature/Facets/Types/MetaRange/Block.php | 11 ------ .../Facets/Types/MetaRange/FacetType.php | 38 ++++++++++--------- .../Facets/Types/MetaRange/Renderer.php | 24 +++++++----- 3 files changed, 34 insertions(+), 39 deletions(-) diff --git a/includes/classes/Feature/Facets/Types/MetaRange/Block.php b/includes/classes/Feature/Facets/Types/MetaRange/Block.php index 138553a69c..aa701e57d0 100644 --- a/includes/classes/Feature/Facets/Types/MetaRange/Block.php +++ b/includes/classes/Feature/Facets/Types/MetaRange/Block.php @@ -80,12 +80,6 @@ public function setup_endpoints() { 'facet' => [ 'sanitize_callback' => 'sanitize_text_field', ], - 'min' => [ - 'sanitize_callback' => 'absint', - ], - 'max' => [ - 'sanitize_callback' => 'absint', - ], ], ] @@ -143,8 +137,6 @@ public function render_block_preview( $request ) { $attributes = $this->parse_attributes( [ 'facet' => $request->get_param( 'facet' ), - 'min' => $request->get_param( 'min' ), - 'max' => $request->get_param( 'max' ), ] ); @@ -215,9 +207,6 @@ protected function parse_attributes( $attributes ) { $attributes, [ 'facet' => '', - 'min' => 0, - 'max' => 100, - ] ); if ( empty( $attributes['facet'] ) ) { diff --git a/includes/classes/Feature/Facets/Types/MetaRange/FacetType.php b/includes/classes/Feature/Facets/Types/MetaRange/FacetType.php index 997decaf38..ebae178a85 100644 --- a/includes/classes/Feature/Facets/Types/MetaRange/FacetType.php +++ b/includes/classes/Feature/Facets/Types/MetaRange/FacetType.php @@ -80,35 +80,37 @@ public function get_filter_type() : string { public function add_query_filters( $filters ) { $feature = Features::factory()->get_registered_feature( 'facets' ); - $selected_filters = $feature->get_selected(); - if ( empty( $selected_filters ) || empty( $selected_filters[ $this->get_filter_type() ] ) ) { + $all_selected_filters = $feature->get_selected(); + if ( empty( $all_selected_filters ) || empty( $all_selected_filters[ $this->get_filter_type() ] ) ) { return $filters; } - $meta_fields = $selected_filters[ $this->get_filter_type() ]; - - echo '
';
-		print_r( $meta_fields );
-		echo '
'; + $selected_filters = $all_selected_filters[ $this->get_filter_type() ]; + foreach ( $selected_filters as $selected_filter => $values ) { + $filter = []; + $min_or_max = substr( $selected_filter, -4 ); + $field_name = substr( $selected_filter, 0, -4 ); + + if ( ! in_array( $min_or_max, [ '_min', '_max' ], true ) ) { + continue; + } - foreach ( $meta_fields as $meta_field => $values ) { $values = array_keys( $values['terms'] ); - if ( 2 !== count( $values ) ) { + if ( empty( $values ) ) { continue; } + $value = reset( $values ); - list( $min, $max ) = $values; - - $filter = []; - if ( $min ) { - $filter['gte'] = $min; + if ( '_min' === $min_or_max ) { + $filter['gte'] = $value; } - if ( $max ) { - $filter['lte'] = $max; + if ( '_max' === $min_or_max ) { + $filter['lte'] = $value; } + $filters[] = [ 'range' => [ - 'meta.' . $meta_field . '.long' => $filter, + 'meta.' . $field_name . '.double' => $filter, ], ]; } @@ -138,7 +140,7 @@ public function set_wp_query_aggs( $facet_aggs ) { * @param {string} $meta_field The meta field key * @return {string} The chosen ES field */ - $facet_field = apply_filters( 'ep_facet_meta_range_use_field', 'long', $meta_field ); + $facet_field = apply_filters( 'ep_facet_meta_range_use_field', 'double', $meta_field ); $facet_aggs[ $this->get_filter_name() . $meta_field . '_min' ] = array( 'min' => array( diff --git a/includes/classes/Feature/Facets/Types/MetaRange/Renderer.php b/includes/classes/Feature/Facets/Types/MetaRange/Renderer.php index ca4376f18a..d75c49c2ec 100644 --- a/includes/classes/Feature/Facets/Types/MetaRange/Renderer.php +++ b/includes/classes/Feature/Facets/Types/MetaRange/Renderer.php @@ -38,28 +38,32 @@ public function render( $args, $instance ) { return; } - $feature = Features::factory()->get_registered_feature( 'facets' ); - + $feature = Features::factory()->get_registered_feature( 'facets' ); $facet_type = $feature->types['meta-range']; $min_field_name = $facet_type->get_filter_name() . $this->meta_field . '_min'; - $max_field_name = $facet_type->get_filter_name() . $this->meta_field . '_max'; - - $min = 0; - if ( ! empty( $GLOBALS['ep_facet_aggs'][ $min_field_name ] ) ) { + if ( empty( $GLOBALS['ep_facet_aggs'][ $min_field_name ] ) ) { + return; + } else { $min = $GLOBALS['ep_facet_aggs'][ $min_field_name ]; } - $max = 0; - if ( ! empty( $GLOBALS['ep_facet_aggs'][ $max_field_name ] ) ) { + $max_field_name = $facet_type->get_filter_name() . $this->meta_field . '_max'; + if ( empty( $GLOBALS['ep_facet_aggs'][ $max_field_name ] ) ) { + return; + } else { $max = $GLOBALS['ep_facet_aggs'][ $max_field_name ]; } $selected_filters = $feature->get_selected(); unset( $selected_filters[ $facet_type->get_filter_type() ][ $this->meta_field ] ); - $form_action = $feature->build_query_url( $selected_filters ); + $form_action = wp_parse_url( $feature->build_query_url( $selected_filters ) ); + wp_parse_str( $form_action['query'], $filter_fields ); ?> -
+ + $value ) { ?> + +