WordPress und AJAX für Autocomplete – eine erste Annäherung

Einleitung

Ich hab mich kürzlich in einem Projekt mal ein bisschen mit den Grundlagen von AJAX beschäftigt, und finde es bietet wirklich faszinierende und in der Praxis gut anwendbare Möglichkeiten. Besonders g’wandt ist die Möglichkeit, zum Suchen bestimmter Datenbankeinträge ein Autocomplete oder Auto-Vervollständigen einzubauen. Der Benutzer tippt ein paar Buchstaben (in der Regel drei) in ein Textfeld ein, und man holt via AJAX die gematchten Einträge aus der Datenbank. Das, so hab ich mir gedacht, hätte ich gerne für eine komfortable Suche nach Rezepten im Inselfisch-Kochbuch, das sind nämlich mittlerweile über dreihundert und somit zuviel für ein Dropdown-Feld. Das könnte in etwa so aussehen:

form_leer

form_leer

Man stellt ein kleines Formular zur Verfügung, in das der Benutzer Text eingeben kann. Sobald in der Datenbank passende Einträge gefunden werden, werden diese als Auswahlliste angezeigt. Ich geb mal sal ein, hier ist das Ergebnis:

suche_sal

suche_sal

Das ist doch schon mal ganz schick, oder? Und nicht über die Nummern (#152) wundern, die brauchen wir später noch. Wenn der Benutzer einen Eintrag ausgewählt hat und auf Return drückt, soll dann das Rezept angezeigt werden, aber bis dahin ist es noch ein Stückchen weit. WordPress stellt nämlich eine ganz eigene Logik für die Verwendung von AJAX zur Verfügung, und die ist ein bisschen widerborstig. Aber professionelle Hilfe ist geboten:

Super Tutorial zum Thema

…von David Nash: https://davidnash.com.au/create-an-auto-complete-field-in-wordpress/

David erklärt wirklich Step by Step und fantastisch nachvollziehbar, auf was es ankommt. Das kann sich wirklich jeder selber reinziehen, das funktioniert direkt auf Anhieb.

Benötigtes Helferlein: jquery autocomplete

Kann man sich hier herunterladen: https://goodies.pixabay.com/jquery/auto-complete/demo.html

Man braucht nur die beiden Dateien jquery.auto-complete.css und jquery-autocomplete.js.

Kleine Ergänzungen zu Davids Tutorial

Das Eingabeformular

Um das Ganze auch ausprobieren zu können, braucht man natürlich ein Eingabeformular. Ich hab eins als Shortcode angelegt, da sieht in meiner functions.php so aus:

 function auto_form(){
    
    echo "<h3>Beispiel Rezeptsuche Autocomplete</h3>";
    
    echo "<form action='#' method='post'>";
    echo "<input type='text' size='40' name = 'auswahl' id = 'rezepte_auswahl'>";
    
    echo "</form>";
    
        
    if (isset($_POST['auswahl'])){
    echo "Vielen Dank! Ihre Auswahl :".$_POST['auswahl']."<br>";
    
   
    } //end if isset
 }
 add_shortcode('a_form', 'auto_form');

Wichtig ist hier die id des Eingabefeldes, die wird dann in unserer js-Datei referenziert:

jQuery(document).ready(function($) {    
    
    $('#rezepte_auswahl').autoComplete({
        source: function(name, response) {
            $.ajax({
                type: 'POST',
                dataType: 'json',
                url: '/wordpress/wp-admin/admin-ajax.php',
                data: 'action=get_listing_names&name='+name,
                success: function(data) {
                    response(data);
                }
            });
        }
    });
 
});

Wichtig ist hier auch die URL der admin-ajax.php, die muss man gegebenenfalls anpassen.

Nachtrag: ich hab mir eine andere Lösung ergooglet

Man kann mithilfe der WordPress-Funktion wp_localize_script() den Pfad zum Admin-Verzeichnis als Variable an das Script übergeben. Dazu ergänzt man die function mysite_js() wie folgt:

function mysite_js() {
    wp_enqueue_script('autocomplete', get_stylesheet_directory_uri().'/js/jquery.auto-complete.js', array('jquery'), false, false);
    
    wp_enqueue_script('mysite-js', get_stylesheet_directory_uri().'/js/mysite.js', array('jquery', 'autocomplete'), false, false);
    
    wp_localize_script( 'mysite-js', 'my_ajaxurl', admin_url( 'admin-ajax.php' ) );
 
    wp_enqueue_style('autocomplete.css', get_stylesheet_directory_uri().'/js/jquery.auto-complete.css');
 
}
add_action('wp_enqueue_scripts', 'mysite_js');

Dann kann man im Script mysite.js für die URL einfach die Variable my_ajaxurl einsetzen:

jQuery(document).ready(function($) {    
    
    $('#rezepte_auswahl').autoComplete({
        source: function(name, response) {
            $.ajax({
                type: 'POST',
                dataType: 'json',
                url: my_ajaxurl,
                //url: '/wordpress/wp-admin/admin-ajax.php',
                data: 'action=get_listing_names&name='+name,
                success: function(data) {
                    response(data);
                }
            });
        }
    });
 
});

Das aber nur als kleiner Tipp am Rande.

Ich hole mir noch die Post-ID

Dazu passe ich die Funktion ajax_listings() ein bisschen an:

function ajax_listings() {
    global $wpdb; //get access to the WordPress database object variable
 
    //get names of all businesses
    $name = $wpdb->esc_like(stripslashes($_POST['name'])).'%'; //escape for use in LIKE statement
    
    $sql = "select post_title, ID 
        from $wpdb->posts 
        where post_title like %s 
        and post_type='post' and post_status='publish'";
 
    $sql = $wpdb->prepare($sql, $name);
    
    $results = $wpdb->get_results($sql);
 
    //copy the business titles to a simple array
    $titles = array();
    foreach( $results as $r )
        $titles[] = addslashes($r->post_title." #".$r->ID);
        
    echo json_encode($titles); //encode into JSON format and output
    
    die(); //stop "0" from being output
}

In den Select kommt noch die ID mit rein, und die hänge ich in der Foreach-Schleife auch noch mit an die Ausgabe dran, mit einem # vorneweg.

Extrahieren der ID im Formular

Wenn der Benutzer einen Listeneintrag ausgewählt und auf Return gedrückt hat, soll ja etwas passieren: nämlich das gewählte Rezept ausgegeben werden. Das „Return-Drücken“ erwischt man mit einem If (ISSET…), dann zerlege ich mir die entsprechende Variable und hole mir nur die Zahl nach dem #.

function auto_form(){
    echo "<a name='form'></a>"; 
    echo "<h3>Beispiel Rezeptsuche Autocomplete</h3>";
    
    echo "<form action='#form' method='post'>";
    echo "<input type='text' size='40' name = 'auswahl' id = 'rezepte_auswahl'>";
   
    echo "</form>";
    
        
    if (isset($_POST['auswahl'])){
    echo "Vielen Dank! Ihre Auswahl :".$_POST['auswahl']."<br>";
    
    //***********Action
    $text = strstr($_POST['auswahl'], '#');
    $text=substr($text,1); 
    echo "Ich bin die Nummer ".$text;
    ///*****end Action
    } //end if isset
 }

Jetzt liegt die ID des gewählten Rezeptes auf der Variablen $text, mit der arbeiten wir weiter und geben einfach den post_content aus:

global $wpdb;
    $sql = "select post_content, ID 
        from $wpdb->posts 
        where ID like ".$text." 
        ";
    
    $results = $wpdb->get_results($sql);
    
    foreach ($results as $r){echo $r->post_content;}

Damit wird das Rezept angezeigt, sobald der Benutzer seine Auswahl getroffen und auf Return gedrückt hat:

salatbowle

salatbowle

Man könnte natürlich auch einen Link zum Rezept einbauen, aber ich lasse es mal so, dann muss der Benutzer nicht woanders hin wechseln, wenn er noch ein Rezept suchen möchte.

Man könnte auch noch die Suche ein bisschen in der Funktionalität verändern, wenn man zum Beispiel nicht nur nach Einträgen suchen möchte, die mit der eingegebenen Zeichenfolge beginnen, sondern alle, in denen die Zeichenfolge enthalten ist. Dann kann man in der function ajax_listings() die Suchvariable einfach anpassen und die Wildcard % noch vornedran setzen:

$name = '%'.$wpdb->esc_like(stripslashes($_POST['name'])).'%';

Das findet z.B. alle Einträge mit salat im Titel, auch nicht unpraktisch:

instring_salat

instring_salat

Alles in allem: eine superpraktische Funktionalität, und dank des grossartigen Tutorials von David Nash einfach umzusetzen. Gefällt mir ausserordentlich gut!

Kleiner Tipp am Ende:

Wenn es zwischendurch so aussieht, als ob WordPress die eigentlich geladenen js-Scripts nicht mehr kennt – Browsercache löschen!