BP_Search::do_search( mixed $args = '' )
Perform search and generate search results
Description
Parameters
- $args
-
(Optional)
Default value: ''
Source
File: bp-search/classes/class-bp-search.php
public function do_search( $args = '' ) {
global $wpdb;
$args = $this->sanitize_args( $args );
$defaults = array(
//the search term
'search_term' => '',
//Restrict search results to only this subset. eg: posts, members, groups, etc.
//See Setting > what to search?
'search_subset' => 'all',
//
//What all to search for. e.g: members.
//See Setting > what to search?
//The options passed here must be a subset of all options available on Setting > what to search, nothing extra can be passed here.
//
//This is different from search_subset.
//If search_subset is 'all', then search is performed for all searchable items.
//If search_subset is 'members' then only total match count for other searchable_items is calculated( so that it can be displayed in tabs)
//members(23) | posts(201) | groups(2) and so on.
// 'searchable_items' => BP_Search::instance()->option( 'items-to-search' ),
//how many search results to display per page
'per_page' => 20,
//current page
'current_page' => 1,
//should we calculate total match count for all different types?
//it should be set to false while calling this function for ajax search
'count_total' => true,
//template type to load for each item
//search results will be styled differently(minimal) while in ajax search
//options ''|'minimal'
'template_type' => '',
'forum_search' => false,
'number' => 3
);
$args = wp_parse_args( $args, $defaults );
if ( true === $args['forum_search'] ) {
$this->searchable_items = array( 'forum', 'topic', 'reply' );
}
$this->search_args = $args;//save it for using in other methods
//bail out if nothing to search for
if ( ! $args['search_term'] ) {
return;
}
if ( 'all' == $args['search_subset'] ) {
/**
* 1. Generate a 'UNION' sql query for all searchable items with only ID, RANK, TYPE(posts|members|..) as columns, order by RANK DESC.
* 3. Generate html for each of them
*/
/* an example UNION query :-
-----------------------------------------------------
(
SELECT
wp_posts.id , 'posts' as type, wp_posts.post_title LIKE '%ho%' AS relevance, wp_posts.post_date as entry_date
FROM
wp_posts
WHERE
1=1
AND (
(
(wp_posts.post_title LIKE '%ho%')
OR (wp_posts.post_content LIKE '%ho%')
)
)
AND wp_posts.post_type IN ('post', 'page', 'attachment')
AND (
wp_posts.post_status = 'publish'
OR wp_posts.post_author = 1
AND wp_posts.post_status = 'private'
)
)
UNION
(
SELECT
DISTINCT g.id, 'groups' as type, g.name LIKE '%ho%' AS relevance, gm2.meta_value as entry_date
FROM
wp_bp_groups_groupmeta gm1, wp_bp_groups_groupmeta gm2, wp_bp_groups g
WHERE
1=1
AND g.id = gm1.group_id
AND g.id = gm2.group_id
AND gm2.meta_key = 'last_activity'
AND gm1.meta_key = 'total_member_count'
AND ( g.name LIKE '%ho%' OR g.description LIKE '%ho%' )
)
ORDER BY
relevance DESC, entry_date DESC LIMIT 0, 10
----------------------------------------------------
*/
$sql_queries = [];
$total = [];
foreach ( $this->searchable_items as $search_type ) {
if ( ! isset( $this->search_helpers[ $search_type ] ) ) {
continue;
}
/**
* the following variable will be an object of current search type helper class
* e.g: an object of Bp_Search_Groups or Bp_Search_Posts etc.
* so we can safely call the public methods defined in those classes.
* This also means that all such classes must have a common set of methods.
*/
$obj = $this->search_helpers[ $search_type ];
$limit = isset( $_REQUEST['view'] ) ? " LIMIT " . ( $args['number'] ) : '';
$sql_queries[] = "( " . $obj->union_sql( $args['search_term'] ) . " $limit ) ";
$total[ $search_type ] = $obj->get_total_match_count( $args['search_term'] );
}
if ( empty( $sql_queries ) ) {
//thigs will get messy if program reaches here!!
return;
}
$pre_search_query = implode( ' UNION ', $sql_queries ) . " ORDER BY relevance, type DESC, entry_date DESC ";
if ( isset( $args['ajax_per_page'] ) && $args['ajax_per_page'] > 0 ) {
$pre_search_query .= " LIMIT {$args['ajax_per_page']} ";
}
$results = $wpdb->get_results( $pre_search_query );
/* $results will have a structure like below */
/*
id | type | relevance | entry_date
45 | groups | 1 | 2014-10-28 17:05:18
40 | posts | 1 | 2014-10-26 13:52:06
4 | groups | 0 | 2014-10-21 15:15:36
*/
if ( ! empty( $results ) ) {
$this->search_results['all'] = array(
'total_match_count' => 0,
'items' => array(),
'items_title' => array()
);
//segregate items of a type together and pass it to corresponsing search handler, so that an aggregate query can be done
//e.g one single wordpress loop can be done for all posts
foreach ( $results as $item ) {
$obj = $this->search_helpers[ $item->type ];
$obj->add_search_item( $item->id );
}
//now get html for each item
foreach ( $results as $item ) {
$obj = $this->search_helpers[ $item->type ];
$result = array(
'id' => $item->id,
'type' => $item->type,
'html' => $obj->get_html( $item->id, $args['template_type'] ),
'title' => $obj->get_title( $item->id )
);
$this->search_results['all']['items'][ $item->type . '_' . $item->id ] = $result;
}
//now we've html saved for search results
if ( ! empty( $this->search_results['all']['items'] ) && $args['template_type'] != 'ajax' ) {
/* ++++++++++++++++++++++++++++++++++
group items of same type together
++++++++++++++++++++++++++++++++++ */
//create another copy, of items, this time, items of same type grouped together
$ordered_items_group = array();
foreach ( $this->search_results['all']['items'] as $item_id => $item ) {
$type = $item['type'];
if ( ! isset( $ordered_items_group[ $type ] ) ) {
$ordered_items_group[ $type ] = array();
}
$item_id = absint( str_replace( $type . '_', '', $item_id ) );
$ordered_items_group[ $type ][ $item_id ] = $item;
}
$search_items = bp_search_items();
$search_url = $this->search_page_search_url();
foreach ( $ordered_items_group as $type => &$items ) {
//now prepend html (opening tags) to first item of each
$category_search_url = esc_url( add_query_arg( array( 'subset' => $type, 'bp_search' => 1 ), $search_url ) );
$label = isset( $search_items[ $type ] ) ? trim( $search_items[ $type ] ) : trim( $type );
$first_item = reset( $items );
$total_results = $total[ $type ];
$start_html = "<div class='results-group results-group-{$type} " . apply_filters( 'bp_search_class_search_wrap', 'bp-search-results-wrap', $label ) . "'>"
. "<header class='results-group-header clearfix'>"
. "<h3 class='results-group-title'><span>" . apply_filters( 'bp_search_label_search_type', $label ) . "</span></h3>"
. "<span class='total-results'>" . sprintf( _n( '%d result', '%d results', $total_results, 'buddyboss' ), $total_results ) . "</a>"
. "</header>"
. "<ul id='{$type}-stream' class='item-list {$type}-list bp-list " . apply_filters( 'bp_search_class_search_list', 'bp-search-results-list', $label ) . "'>";
$group_start_html = apply_filters( "bp_search_results_group_start_html", $start_html, $type );
$first_item['html'] = $group_start_html . $first_item['html'];
$items[ $first_item['id'] ] = $first_item;
//and append html (closing tags) to last item of each type
$last_item = end( $items );
$end_html = "</ul>";
if ( $total_results > 3 ) {
$end_html .= "<footer class='results-group-footer'>";
$end_html .= "<a href='" . $category_search_url . "' class='view-all-link'>" .
sprintf( esc_html__( 'View (%d) more', 'buddyboss' ), $total_results - $args['number'] ).
"</a>";
$end_html .= "</footer>";
}
$end_html .= "</div>";
$group_end_html = apply_filters( "bp_search_results_group_end_html", $end_html, $type );
$last_item['html'] = $last_item['html'] . $group_end_html;
$items[ $last_item['id'] ] = $last_item;
}
//replace orginal items with this new, grouped set of items
$this->search_results['all']['items'] = array();
foreach ( $ordered_items_group as $type => $grouped_items ) {
// Remove last item from list
if ( count( $grouped_items ) > 3 ) {
array_pop( $grouped_items );
}
foreach ( $grouped_items as $item_id => $item ) {
$this->search_results['all']['items'][ $type . '_' . $item_id ] = $item;
}
}
/* ________________________________ */
}
}
} else {
//if subset not in searchable items, bail out.
if ( ! in_array( $args['search_subset'], $this->searchable_items ) ) {
return;
}
if ( ! isset( $this->search_helpers[ $args['search_subset'] ] ) ) {
return;
}
/**
* 1. Search top top 20( $args['per_page'] ) item( posts|members|..)
* 2. Generate html for each of them
*/
//$args['per_page'] = get_option( 'posts_per_page' );
$obj = $this->search_helpers[ $args['search_subset'] ];
$pre_search_query = $obj->union_sql( $args['search_term'] ) . " ORDER BY relevance DESC, entry_date DESC ";
if ( $args['per_page'] > 0 ) {
$offset = ( $args['current_page'] * $args['per_page'] ) - $args['per_page'];
$pre_search_query .= " LIMIT {$offset}, {$args['per_page']} ";
}
$results = $wpdb->get_results( $pre_search_query );
/* $results will have a structure like below */
/*
id | type | relevance | entry_date
45 | groups | 1 | 2014-10-28 17:05:18
40 | posts | 1 | 2014-10-26 13:52:06
4 | groups | 0 | 2014-10-21 15:15:36
*/
if ( ! empty( $results ) ) {
$obj = $this->search_helpers[ $args['search_subset'] ];
$this->search_results[ $args['search_subset'] ] = array(
'total_match_count' => 0,
'items' => array()
);
//segregate items of a type together and pass it to corresponsing search handler, so that an aggregate query can be done
//e.g one single wordpress loop can be done for all posts
foreach ( $results as $item ) {
$obj->add_search_item( $item->id );
}
//now get html for each item
foreach ( $results as $item ) {
$html = $obj->get_html( $item->id, $args['template_type'] );
$result = array(
'id' => $item->id,
'type' => $args['search_subset'],
'html' => $obj->get_html( $item->id, $args['template_type'] ),
'title' => $obj->get_title( $item->id ),
);
$this->search_results[ $args['search_subset'] ]['items'][ $item->id ] = $result;
}
//now prepend html (opening tags) to first item of each type
$first_item = reset( $this->search_results[ $args['search_subset'] ]['items'] );
$start_html = "<div class='results-group results-group-{$args['search_subset']} " . apply_filters( 'bp_search_class_search_wrap', 'bp-search-results-wrap', $args['search_subset'] ) . "'>"
. "<ul id='{$args['search_subset']}-stream' class='item-list {$args['search_subset']}-list bp-list " . apply_filters( 'bp_search_class_search_list', 'bp-search-results-list', $args['search_subset'] ) . "'>";
$group_start_html = apply_filters( "bp_search_results_group_start_html", $start_html, $args['search_subset'] );
$first_item['html'] = $group_start_html . $first_item['html'];
$this->search_results[ $args['search_subset'] ]['items'][ $first_item['id'] ] = $first_item;
//and append html (closing tags) to last item of each type
$last_item = end( $this->search_results[ $args['search_subset'] ]['items'] );
$end_html = "</ul></div>";
$group_end_html = apply_filters( "bp_search_results_group_end_html", $end_html, $args['search_subset'] );
$last_item['html'] = $last_item['html'] . $group_end_html;
$this->search_results[ $args['search_subset'] ]['items'][ $last_item['id'] ] = $last_item;
}
}
//html for search results is generated.
//now, lets calculate the total number of search results, for all different types
if ( $args['count_total'] ) {
$all_items_count = 0;
foreach ( $this->searchable_items as $search_type ) {
if ( ! isset( $this->search_helpers[ $search_type ] ) ) {
continue;
}
$obj = $this->search_helpers[ $search_type ];
$total_match_count = $obj->get_total_match_count( $this->search_args['search_term'] );
$this->search_results[ $search_type ]['total_match_count'] = $total_match_count;
$all_items_count += $total_match_count;
}
$this->search_results['all']['total_match_count'] = $all_items_count;
}
}
Changelog
| Version | Description |
|---|---|
| BuddyBoss 1.0.0 | Introduced. |
Questions?
We're always happy to help with code or other questions you might have! Search our developer docs, contact support, or connect with our sales team.