Wordpress: Suche mit Custom Fields oder ACF Feldern

Dies ist ein Beispiel wie man in der Wordpress Suche nach ACF Fields oder eigene Felder suchen lassen kann. Dieser Code ist eine PHP Klasse, die man am besten in eine eigene Datei packt und durch die functions.php included oder in ein eigenes Plugin schreibt.

Der Code Ursprung basiert auf diesem Blog Beitrag auf outsourcify.net. Er ist aber so umgeschrieben, dass er in einer eigenen Klasse arbeitet. Dadurch kommen sich mögliche andere Funktionen nicht in den weg. Außerdem kann man in dieser Version Post-Types noch mit angeben.

Zusätzlich ist noch ein Fix eingebaut, damit das Script auch Funktionen läuft, die WP Filter supressen. Lösung kam aus einem Blog Beitrag (alex.blog) von 2010.

Hinweise zur Nutzung:

  • Manche AJAX-Aufrufe werden nicht als Suche interpretiert. Dann bitte in der Methode is_search() innerhalb der Klasse eine weitere Abfrage hinzufügen. In diesem Beispiel ist eine Abfrage drin für das Flatsome Theme
  • get_posts() unterdrückt Filter. darum ist in der Methode manipulate_wp_query() ein $wp_query→set( 'suppress_filters', false ); eingebaut - das kann aber bedeuten, dass eventuell Suchergebnisse anders ausgegeben werden als reguläre ungefilterte Ausgaben
<?php
 
/*
	HOOK FILTER IN SEARCH
 */
$post_types = [ 'post', 'page', 'dv_betriebe' ];
new SearchWithCustomFields( $post_types );
 
 
 
/**
 *
 * === Code Ursprung
 * Code basiert auf einem Beispiel von outsourcify.net
 * @link: https://outsourcify.net/how-to-search-in-acfs-custom-fields-in-wordpress/
 *
 * === Original Bschreibung:
 * Extend WordPress search to include custom fields
 * @link: http://adambalee.com
 *
 */
class SearchWithCustomFields
{
 
	private $wp_filter_priority         = 100;
	//private $acf_post_types             = [ 'acf-field-group', 'acf-field' ]; // ungenuzt
	private $limit_search_to_post_types = []; // wp_query » post_types
	private $results_per_page           = -1; // wp_query » posts_per_page
 
 
	function __construct( $post_types = [ 'post', 'page' ] )
	{
		$this->set_post_types( $post_types );
 
		add_filter( 'pre_get_posts',  [ $this, 'manipulate_wp_query' ],                    $this->wp_filter_priority );
		add_filter( 'posts_join',     [ $this, 'sql_query_join_post_and_meta_on_search' ], $this->wp_filter_priority );
		add_filter( 'posts_where',    [ $this, 'sql_query_where_replace' ],                $this->wp_filter_priority );
		add_filter( 'posts_distinct', [ $this, 'search_for_distinctive_results' ],         $this->wp_filter_priority );
		add_filter( 'posts_results',  [ $this, 'filter_results' ], $this->wp_filter_priority );
	}
 
	public function set_post_types( $post_types = [] ) { $this->limit_search_to_post_types = $post_types; }
 
 
 
	/**
	 * [manipulate_query_var description]
	 *
	 * @link https://developer.wordpress.org/reference/hooks/pre_get_posts/
	 *
	 * @param  object|wp_query   $wp_query               [description]
	 * @return object|wp_query   [description]
	 */
	public function manipulate_wp_query( &$wp_query ) {
 
		if ( $this->is_search() ) {
			$wp_query->set( 'posts_per_page', $this->results_per_page );
			$wp_query->set( 'post_type', $this->limit_search_to_post_types );
 
			// wichtig, weil get_posts( $args ) Filter unterdrückt und es sonst nich geht
			$wp_query->set( 'suppress_filters', false );
		}
 
		// Return query
		return $wp_query;
	}
 
 
 
	/**
	*
	* Für die Post-Metas einer temporären Tabelle hinzu, damit diese auch durchsuch werden
	*
	* @link: http://codex.wordpress.org/Plugin_API/Filter_Reference/posts_join
	*/
	public function sql_query_join_post_and_meta_on_search( $join_sql_query )
	{
	    global $wpdb;
 
	    if( $this->is_search() ){
	        $join_sql_query .=' LEFT JOIN '.$wpdb->postmeta. ' ON '. $wpdb->posts . '.ID = ' . $wpdb->postmeta . '.post_id ';
	    }
 
	    return $join_sql_query;
	}
 
 
	/**
	 *
	 * Damit nicht nur im Title, sondern auch in einem Metafeld gesucht wird
	 *
	 * @link http://codex.wordpress.org/Plugin_API/Filter_Reference/posts_where
	 */
	public function sql_query_where_replace( $where_sql_query ) {
	    global $pagenow, $wpdb;
	    if ( $this->is_search() ) {
	        $where_sql_query = preg_replace(
	            "/\(\s*".$wpdb->posts.".post_title\s+LIKE\s*(\'[^\']+\')\s*\)/",
	            "(".$wpdb->posts.".post_title LIKE $1) OR (".$wpdb->postmeta.".meta_value LIKE $1)", $where_sql_query );
	    }
	    return $where_sql_query;
	}
 
 
 
	/**
	 * Duplikate verhindern
	 *
	 * @link http://codex.wordpress.org/Plugin_API/Filter_Reference/posts_distinct
	 */
	public function search_for_distinctive_results( $distinct_mode_string ) {
 
	    if ( $this->is_search() ) {
	        return "DISTINCT";
	    }
	    return $distinct_mode_string;
	}
 
 
 
	/**
	 *
	 *
	 * @link https://developer.wordpress.org/reference/hooks/posts_results/
	 */
	public function filter_results( $post_objects_array ) {
 
	    return $post_objects_array ;
	}
 
 
 
	/**
	 * Alias Methode von WP Funktion is_search()
	 * Ist hier eine eigene Methode, falls noch mehr Bedingen hinzu kommen.
	 *
	 * @link: https://developer.wordpress.org/reference/functions/is_search/
	 *
	 * @return boolean   - true alls es sich um eine Suche handelt
	 */
	private function is_search()
	{
		$is_search_by_wpquery    = is_search();
		$is_admin                = is_admin();
		$is_acf_call             = $this->is_acf_call();
		$is_flatsome_ajax_search = false;
 
		/* Die Flatsome Theme Ajax Suche wird nicht als Suche erkannt, darum der Work Around */
		if( wp_doing_ajax() ){
			// $_REQUEST['action'] = "flatsome_ajax_search_products"
			// $_REQUEST['query'] = "Search Term Example"
			$is_flatsome_ajax_search = ( $_REQUEST['action'] === 'flatsome_ajax_search_products' ) ? true : false;;
		}
 
		$is_search = ( ( $is_search_by_wpquery & !$is_admin & !$is_acf_call ) | $is_flatsome_ajax_search ) ? true : false;
 
		return $is_search;
	}
 
	/**
	 *
	 *
	 * Prüft ob die Suche durch ein ACF Query ausgelöst wurde
	 *
	 * ACF & die Beschränkung nach Post_Types vertägt sich nicht, weil
	 * ACF mit eigenen Posttypes kommt. Darum muss geprüft werden, dass
	 * keine Beschränkung in der Suche vorgenommen wird, wenn der call
	 * über ACF kommt.
	 *
	 * Bisher noch keine bessere Lösung gefunden, als den Callstack
	 * abzufragen.
	 *
	 * @return boolean [description]
	 */
	private function is_acf_call()
	{
		$backtrace   = debug_backtrace();
		$is_acf_call = false;
		foreach ($backtrace as $key => $stack) {
			$is_acf_file     = (strpos( $stack["file"], 'advanced-custom-fields-pro' ) !== 0 ) ? true : false;
			$is_acf_function = (strpos( $stack["function"], 'get_field' ) !== 0 ) ? true : false;
 
			if( $is_acf_file | $is_acf_function ){
				$is_acf_call = true;
				break;
			}
		}
 
		return $is_acf_call;
	}
}

Page Tools