From 9bd771ddf474ab90b6e068ab9677617ea85d6d33 Mon Sep 17 00:00:00 2001 From: lithrel <lithrel@randomdomainname.net> Date: Wed, 27 Apr 2022 08:51:19 +0200 Subject: [PATCH] PLANET-6552: Extract search filters Extract search filters to their own classes Use consistent taxonomy --- src/Search.php | 142 +++++----------------------- src/Search/Filters/Categories.php | 39 ++++++++ src/Search/Filters/ContentTypes.php | 116 +++++++++++++++++++++++ src/Search/Filters/PostTypes.php | 53 +++++++++++ src/Search/Filters/Tags.php | 48 ++++++++++ templates/search.twig | 16 ++-- 6 files changed, 288 insertions(+), 126 deletions(-) create mode 100644 src/Search/Filters/Categories.php create mode 100644 src/Search/Filters/ContentTypes.php create mode 100644 src/Search/Filters/PostTypes.php create mode 100644 src/Search/Filters/Tags.php diff --git a/src/Search.php b/src/Search.php index 34eea89282..c81ac4cda4 100644 --- a/src/Search.php +++ b/src/Search.php @@ -727,85 +727,21 @@ protected function set_general_context( &$context ) { * @throws UnexpectedValueException When filter type is not recognized. */ protected function set_filters_context( &$context ) { - // Retrieve P4 settings in order to check that we add only categories that are children of the Issues category. - $options = get_option( 'planet4_options' ); + // Categories. + $context['categories'] = Search\Filters\Categories::get_filters(); + uasort( $context['categories'], fn ( $a, $b ) => strcmp( $a['name'], $b['name'] ) ); - // Category <-> Issue. - // Consider Issues that have multiple Categories. - $categories = get_categories( [ 'child_of' => $options['issues_parent_category'] ] ); - if ( $categories ) { - foreach ( $categories as $category ) { - $context['categories'][ $category->term_id ] = [ - 'term_id' => $category->term_id, - 'name' => $category->name, - 'results' => 0, - ]; - } - } + // Post Types. + $context['post_types'] = Search\Filters\PostTypes::get_filters(); - // Tag <-> Campaign. - $tags = get_terms( - [ - 'taxonomy' => 'post_tag', - 'hide_empty' => false, - ] + // Content Types. + $context['content_types'] = Search\Filters\ContentTypes::get_filters( + self::should_include_archive() ); - if ( $tags ) { - foreach ( (array) $tags as $tag ) { - // Tag filters. - $context['tags'][ $tag->term_id ] = [ - 'term_id' => $tag->term_id, - 'name' => $tag->name, - 'results' => 0, - ]; - } - } - // Page Type <-> Category. - $page_types = get_terms( - [ - 'taxonomy' => 'p4-page-type', - 'hide_empty' => false, - ] - ); - if ( $page_types ) { - foreach ( (array) $page_types as $page_type ) { - // p4-page-type filters. - $context['page_types'][ $page_type->term_id ] = [ - 'term_id' => $page_type->term_id, - 'name' => $page_type->name, - 'results' => 0, - ]; - } - } - - // Post Type (+Action) <-> Content Type. - $context['content_types']['0'] = [ - 'name' => __( 'Action', 'planet4-master-theme' ), - 'results' => 0, - ]; - $context['content_types']['4'] = [ - 'name' => __( 'Campaign', 'planet4-master-theme' ), - 'results' => 0, - ]; - $context['content_types']['1'] = [ - 'name' => __( 'Document', 'planet4-master-theme' ), - 'results' => 0, - ]; - $context['content_types']['2'] = [ - 'name' => __( 'Page', 'planet4-master-theme' ), - 'results' => 0, - ]; - $context['content_types']['3'] = [ - 'name' => __( 'Post', 'planet4-master-theme' ), - 'results' => 0, - ]; - if ( self::should_include_archive() ) { - $context['content_types']['5'] = [ - 'name' => __( 'Archive', 'planet4-master-theme' ), - 'results' => 0, - ]; - } + // Tag <-> Campaign. + $context['tags'] = Search\Filters\Tags::get_filters(); + uasort( $context['tags'], fn ( $a, $b ) => strcmp( $a['name'], $b['name'] ) ); // Keep track of which filters are already checked. if ( $this->filters ) { @@ -819,7 +755,7 @@ protected function set_filters_context( &$context ) { $context['tags'][ $filter['id'] ]['checked'] = 'checked'; break; case 'ptype': - $context['page_types'][ $filter['id'] ]['checked'] = 'checked'; + $context['post_types'][ $filter['id'] ]['checked'] = 'checked'; break; case 'ctype': $context['content_types'][ $filter['id'] ]['checked'] = 'checked'; @@ -830,34 +766,11 @@ protected function set_filters_context( &$context ) { } } } - - // Sort associative array with filters alphabetically. - if ( $context['categories'] ?? false ) { - uasort( - $context['categories'], - function ( $a, $b ) { - return strcmp( $a['name'], $b['name'] ); - } - ); - } - if ( $context['tags'] ?? false ) { - uasort( - $context['tags'], - function ( $a, $b ) { - return strcmp( $a['name'], $b['name'] ); - } - ); - } } /** * Sets the context for the results of the Search. * - * Categories are Issues. - * Tags are Campaigns. - * Page types are Categories. - * Post_types are Content Types. - * * @param array $context Associative array with the data to be passed to the view. */ protected function set_results_context( &$context ) { @@ -908,33 +821,24 @@ protected function set_results_context( &$context ) { } foreach ( $aggs['p4-page-type']['buckets'] as $p4_post_type_agg ) { - $p4_post_type_id = (int) $p4_post_type_agg['key']; - - $p4_post_type = self::get_p4_post_type( $p4_post_type_id ); - - $context['page_types'][ $p4_post_type_id ] = [ - 'term_id' => $p4_post_type_id, - 'name' => $p4_post_type->name ?? null, - 'results' => $p4_post_type_agg['doc_count'], - ]; + if ( ! isset( $context['post_types'][ $p4_post_type_agg['key'] ] ) ) { + continue; + } + $context['post_types'][ $p4_post_type_agg['key'] ]['results'] = $p4_post_type_agg['doc_count']; } foreach ( $aggs['categories']['buckets'] as $category_agg ) { - // TODO get the parent from ES so no fetch is needed here. - $category = get_category( $category_agg['key'] ); - - // Category <-> Issue. - // Consider Issues that have multiple Categories. - if ( $category->parent === (int) $options['issues_parent_category'] ) { - $context['categories'][ $category->term_id ]['results'] = $category_agg['doc_count']; + if ( ! isset( $context['categories'][ $category_agg['key'] ] ) ) { + continue; } + $context['categories'][ $category_agg['key'] ]['results'] = $category_agg['doc_count']; } foreach ( $aggs['tags']['buckets'] as $tag_agg ) { - // Tag filters. - $tag = get_tag( $tag_agg['key'] ); - - $context['tags'][ $tag->term_id ]['results'] = $tag_agg['doc_count']; + if ( ! isset( $context['tags'][ $tag_agg['key'] ] ) ) { + continue; + } + $context['tags'][ $tag_agg['key'] ]['results'] = $tag_agg['doc_count']; } } diff --git a/src/Search/Filters/Categories.php b/src/Search/Filters/Categories.php new file mode 100644 index 0000000000..de691c5720 --- /dev/null +++ b/src/Search/Filters/Categories.php @@ -0,0 +1,39 @@ +<?php + +declare(strict_types=1); + +namespace P4\MasterTheme\Search\Filters; + +/** + * Categories used for search. + */ +class Categories { + /** + * @return array<WP_Term> + */ + public static function get_all(): array { + return get_categories(); + } + + /** + * @return array{int, array{term_id: int, name: string, results: int}} + */ + public static function get_filters(): array { + $categories = self::get_all(); + $filters = []; + + foreach ( $categories as $category ) { + if ( 'uncategorised' === $category->slug ) { + continue; + } + + $filters[ $category->term_id ] = [ + 'term_id' => $category->term_id, + 'name' => $category->name, + 'results' => 0, + ]; + } + + return $filters; + } +} diff --git a/src/Search/Filters/ContentTypes.php b/src/Search/Filters/ContentTypes.php new file mode 100644 index 0000000000..854d9e1439 --- /dev/null +++ b/src/Search/Filters/ContentTypes.php @@ -0,0 +1,116 @@ +<?php + +declare(strict_types=1); + +namespace P4\MasterTheme\Search\Filters; + +/** + * Content type used for search. + * Native types (post, page, attachment, etc.). + * Custom types (p4_action, etc.). + */ +class ContentTypes { + public const ACT_PAGE = 'action'; + public const POST = 'post'; + public const PAGE = 'page'; + public const CAMPAIGN = 'campaign'; + public const ATTACHMENT = 'attachment'; + public const ARCHIVE = 'archive'; + + /** + * Get all content types. + * + * @return array<WP_Post_Type> + */ + public static function get_all(): array { + $config = self::get_config(); + $types = get_post_types( + [ + 'public' => true, + 'exclude_from_search' => false, + ], + false + ); + + return array_filter( + $types, + function ( $type ) use ( $config ) { + return isset( $config[ $type->name ] ); + } + ); + } + + /** + * @param bool $include_archive Include archive type. + * + * @return array{string, array{id: int, name: string, results: int}} + */ + public static function get_filters( + bool $include_archive = false + ): array { + $types = self::get_all(); + $config = self::get_config(); + + // ACT_PAGE are sub-pages of the Act page and not a real type, adding manually. + $filters = [ + 0 => [ + 'id' => 0, + 'name' => __( 'Action', 'planet4-master-theme' ), + 'results' => 0, + ], + ]; + + foreach ( $types as $name => $type ) { + if ( self::ARCHIVE === $name && ! $include_archive ) { + continue; + } + + $type_data = $config[ $type->name ] ?? null; + if ( ! $type_data ) { + continue; + } + + $filters[ $type_data['id'] ] = [ + 'id' => $type_data['id'], + 'name' => $type_data['label'], + 'results' => 0, + ]; + } + + return $filters; + } + + /** + * Get all content type config for search + * + * @return array{string, array{id: int, label: string}} + */ + public static function get_config(): array { + return [ + self::ACT_PAGE => [ + 'id' => 0, + 'label' => __( 'Action', 'planet4-master-theme' ), + ], + self::ATTACHMENT => [ + 'id' => 1, + 'label' => __( 'Document', 'planet4-master-theme' ), + ], + self::PAGE => [ + 'id' => 2, + 'label' => __( 'Page', 'planet4-master-theme' ), + ], + self::POST => [ + 'id' => 3, + 'label' => __( 'Post', 'planet4-master-theme' ), + ], + self::CAMPAIGN => [ + 'id' => 4, + 'label' => __( 'Campaign', 'planet4-master-theme' ), + ], + self::ARCHIVE => [ + 'id' => 5, + 'label' => __( 'Archive', 'planet4-master-theme' ), + ], + ]; + } +} diff --git a/src/Search/Filters/PostTypes.php b/src/Search/Filters/PostTypes.php new file mode 100644 index 0000000000..1f04dd299d --- /dev/null +++ b/src/Search/Filters/PostTypes.php @@ -0,0 +1,53 @@ +<?php + +declare(strict_types=1); + +namespace P4\MasterTheme\Search\Filters; + +use WP_Term; + +/** + * Post types used for search (Press release, News, Report, etc.). + * Configured in `Posts > Posts Types` + */ +class PostTypes { + /** + * @return WP_Term[] + */ + public static function get_all(): array { + $types = get_terms( + [ + 'taxonomy' => 'p4-page-type', + 'hide_empty' => false, + ] + ); + + if ( is_wp_error( $types ) + || ! is_array( $types ) + || empty( $types ) + || ! ( $types[0] instanceof WP_Term ) + ) { + return []; + } + + return $types; + } + + /** + * @return array{int, array{term_id: int, name: string, results: int}} + */ + public static function get_filters(): array { + $post_types = self::get_all(); + $filters = []; + + foreach ( $post_types as $post_type ) { + $filters[ $post_type->term_id ] = [ + 'term_id' => $post_type->term_id, + 'name' => $post_type->name, + 'results' => 0, + ]; + } + + return $filters; + } +} diff --git a/src/Search/Filters/Tags.php b/src/Search/Filters/Tags.php new file mode 100644 index 0000000000..6762d4ae3f --- /dev/null +++ b/src/Search/Filters/Tags.php @@ -0,0 +1,48 @@ +<?php + +declare(strict_types=1); + +namespace P4\MasterTheme\Search\Filters; + +use WP_Term; + +/** + * Tags used for search. + */ +class Tags { + /** + * @return WP_Term[] + */ + public static function get_all(): array { + $tags = get_terms( + [ + 'taxonomy' => 'post_tag', + 'hide_empty' => false, + ] + ); + + if ( is_wp_error( $tags ) ) { + return []; + } + + return $tags; + } + + /** + * @return array{int, array{term_id: int, name: string, results: int}} + */ + public static function get_filters(): array { + $tags = self::get_all(); + $filters = []; + + foreach ( $tags as $tag ) { + $filters[ $tag->term_id ] = [ + 'term_id' => $tag->term_id, + 'name' => $tag->name, + 'results' => 0, + ]; + } + + return $filters; + } +} diff --git a/templates/search.twig b/templates/search.twig index f5971592d2..1f149bcf4b 100644 --- a/templates/search.twig +++ b/templates/search.twig @@ -279,28 +279,30 @@ </div> </div> {% endif %} - {% if ( page_types|length > 0 ) %} + {% if ( post_types|length > 0 ) %} <div class="filteritem"> <a data-bs-toggle="collapse" href="#item-category" class="{{ collapsed }}" aria-expanded="{{ expanded }}"> {{ __( 'Post Type', 'planet4-master-theme' ) }} <span></span> </a> <div id="item-category" class="collapse {{ show }}" role="tabpanel"> <ul class="list-unstyled"> - {% for page_type in page_types %} + {% for post_type in post_types %} + {% if ( post_type.results > 0 ) %} <li> <label class="custom-control"> <input type="checkbox" - name="f[ptype][{{ page_type.name }}]" - value="{{ page_type.term_id }}" + name="f[ptype][{{ post_type.name }}]" + value="{{ post_type.term_id }}" class="p4-custom-control-input" data-ga-category="Search Page" data-ga-action="Post Type Filter" - data-ga-label="{{ page_type.name|e('wp_kses_post')|raw }}" - {{ page_type.checked }} {{ page_type.results > 0 ? '' : 'disabled' }} /> - <span class="custom-control-description">{{ page_type.name|e('wp_kses_post')|raw }} ({{ page_type.results }})</span> + data-ga-label="{{ post_type.name|e('wp_kses_post')|raw }}" + {{ post_type.checked }} {{ post_type.results > 0 ? '' : 'disabled' }} /> + <span class="custom-control-description">{{ post_type.name|e('wp_kses_post')|raw }} ({{ post_type.results }})</span> </label> </li> + {% endif %} {% endfor %} </ul> </div>