Autocomplete mit AJAX in Joomla

Was mit WordPress und mit Drupal geht, das sollte sich doch auch mit Joomla hinkriegen lassen: eine Artikelsuche mit Autovervollständigen per AJAX. Ich habe dafür mal die Joomla 3 Standard-SampleSite genommen, die kann man bei der Installation als Option anwählen und sie hat schön viel Content.

Mein Arbeitspferd: die PHP-Bridge

Die geniale PHP-Bridge von Henry Schorradt ist wie immer mein Mittel der Wahl, wenn es darum geht Joomla eigenen PHP-Code unterzuschieben. Erster Schritt also: einen eigenen Menüeintrag erstellen, bei Modulzuweisung die PHP-Bridge auswählen und entsprechend anpassen. Doku hierzu im obigen Link auf Henrys Seite.

Was wir alles brauchen:

  • das Formular für die Texteingabe
  • das JavaScript für das Handling des AJAX-Requests mit den aus dem Formular übergebenen Daten
  • die PHP-Datei, die uns die Ergebnisse des Calls zurückliefert

Das Formular und das JavaScript stecken wir beide in die PHP-Datei, die mit unserer PHP-Bridge verknüpft ist. Das Formular steht dann innerhalb der Switch und sieht zum Beispiel so aus:

<?php
//$mode = Scripthandler

switch($mode){ case 'autocom':

echo     "<h1>Beispiel Artikelsuche mit AJAX in Joomla</h1>";
echo "<form action='#' method='post'>";
echo "<input type='text' size='80' name = 'ausgesucht' value='' autocomplete='off'
 onkeyup='rezepte_suche(this.value)' list = 'suchliste'>";

echo "<datalist id='suchliste'>";
 
 echo "<div id =liste>";
 echo "</div>";

 echo "</datalist>";

echo "</form>";

break;}//switch
?>

Eigentlich ein ganz normales Formular mit einem Eingabefeld vom Typ Text. Aber hier passieren noch vier eminent wichtige Dinge:

  1. onkeyup = ruft die js-Funktion rezepte_suche() auf, die steckt in der rezepte_suche.js, die wir nachher noch laden. Die Funktion kriegt als Parameter this.value mit, also den aktuellen Wert des Textfeldes. Onkeyup feuert, wenn der Benutzer eine Taste gedrückt und wieder losgelassen hat.
  2. Der Parameter list = ‚suchliste‚ des Textfeldes definiert, aus welchem HTML-Element die Datenliste für das Dropdownfeld genommen werden soll, nämlich aus der Datalist mit der id = „suchliste“
  3. Innerhalb der Datalist steckt eine Div mit der id= ‚liste‘, in die schreibt nachher unser AJAX-Call sein Ergebnis, dazu gleich mehr.
  4. Wichtige Ergänzung: das Texteingabefeld sollte unbedingt den Parameter autocomplete = ‚off‘ mitkriegen,  sonst pfuscht der Browser mit Einträgen hinein, die er aus anderen Formularen gecached hat.

Das Formular sollte jetzt in etwa so aussehen:

joomla_form

joomla_form

Das JavaScript

…packen wir mit in die PHP-Datei der Bridge, das kommt vor den Switch und sieht so aus:

<script type="text/javascript">


function rezepte_suche(inhalt)
{
 if (inhalt=="")
 {
  //Hinweis wenn der Feldinhalt leer ist;
  //document.getElementById("ausgabe").innerHTML="keine Eingabe";
  return;
 }
 if (window.XMLHttpRequest)
 {
  // AJAX nutzen mit IE7+, Chrome, Firefox, Safari, Opera
  xmlhttp=new XMLHttpRequest();
 }
 else
 {
  // AJAX mit IE6, IE5
  xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
 }
 xmlhttp.onreadystatechange=function()
 {
  if (xmlhttp.readyState==4 && xmlhttp.status==200)
  {
   
   document.getElementById("liste").innerHTML=xmlhttp.responseText;
  
  }
 }

 xmlhttp.open("GET","http://localhost/joomla30/templates/protostar/php/rezeptesuche.php?q="+inhalt,true);
 xmlhttp.send();
}
</script>

Die ist nicht auf meinem Mist gewachsen, das ist eine Standardprozedur für einen AJAX-Call die ich mir zusammengegooglet und nach meinen Bedürfnissen angepaßt habe. Sie enthält genau eine Funktion, nämlich die rezepte_suche mit dem Parameter inhalt, in dem steckt der vom Benutzer eingegebene Inhalt des Textfeldes aus dem Formular.

Was passiert hier? Zuerst wird abgefragt, ob inhalt leer ist und in dem Fall nur ein return ausgelöst.

Wenn inhalt nicht leer ist, greifen zwei IF-Abfragen, die Browserabhängig die Variable xmlhttp unterschiedlich belegen. Wir schauen mal nur den ersten Zweig an:

xmlhttp=new XMLHttpRequest();

Hier wird ein neues Objekt vom Typ XMLHttpRequest angelegt, das wird im Folgenden weiterverarbeitet. Wer dazu Genaueres wissen möchte dem sei diese Seite der wikibooks empfohlen.

Wenn sich am Status des Formularfeldes etwas ändert, feuert folgende Funktion:

xmlhttp.onreadystatechange=function()
 {
  if (xmlhttp.readyState==4 && xmlhttp.status==200)
  {
   
   document.getElementById("liste").innerHTML=xmlhttp.responseText;
  }
 }

Die div mit der id liste kriegt einen neuen Inhalt, nämlich den Rückgabewert des Requests. Wir der aussieht, wird in unserer PHP-Datei geregelt, dazu gleich mehr.

Wichtig ist jetzt noch der open-Aufruf:

 xmlhttp.open("GET","http://localhost/joomla30/templates/protostar/php/rezeptesuche.php?q="+inhalt,true);

Ich hab hier den Pfad zur PHP-Datei  fest verdrahtet, das läßt sich sicher auch eleganter lösen, aber für Testzwecke tuts das. Die PHP-Datei kriegt als Parameter unseren inhalt mit, das ist immer noch der Inhalt des Formularfeldes so wie ihn der Benutzer eingegeben hat. Fehlt nur noch der Auslöser:

xmlhttp.send();

Und Schuß! Das setzt den Request ab.

Die aufgrufene PHP-Datei

Die benimmt sich nicht anders, als wenn sie von einem Formular aus aufgerufen würde, und sie übernimmt den Parameter inhalt in der Variablen q. Ansonsten ist sie recht übersichtlich:

<?php

$rezept = '';
if ( isset($_GET['q']) )
{
  $rezept = $_GET['q'];
}

$pdo = new PDO('mysql:host=localhost;dbname=joomla', '*******', '******');

//Suche nach enthaltenem String %suchstring%
$sql = "SELECT * FROM rrzol_content where title like '%".$rezept."%' order by title";
$records = $pdo->query($sql);

foreach ($records as $row) {
        
        echo "<option>".$row['title']." #".$row['id']."</option><br>";
        
    }


?>

Ich hole mir erstmal den Inhalt von q auf die Variable $rezept. Dann verbinde ich mich mit der Joomla-Datenbank und hole mir aus der content-Tabelle mit dem Select alle Datensätze die den Inhalt von $rezept im Titel enthalten. Innerhalb der Foreach-Schleife wirds dann nochmal interessant, ich klemme vor und nach dem Titel des Beitrags die <option>-Tags dran, die brauchen wir nachher im Formular für die Datalist. Ausserdem nehme ich noch die id mit, die brauchen wir dann für die Ausgabe des Beitrags. Das wars dann aber auch schon.

Das Formular ist jetzt scharfgeschaltet

Wenn der Benutzer jetzt zum Beispiel con in das Textfeld eingibt, erhält er eine Liste aller Beitragstitel, in denen der String con vorkommt:

auswahl_con

auswahl_con

Jetzt muss nur noch etwas passieren, wenn er einen der Einträge auswählt und auf Return drückt. Ich hab das so gelöst: in der PHP Bridge kommt nach dem Formular noch folgender Code:

if (isset($_POST['ausgesucht'])){
    
    $suchtitel = substr($_POST['ausgesucht'], 0, strpos($_POST['ausgesucht'], '#', 0));
    //echo $suchtitel;
    echo "Vielen Dank! Ihre Auswahl: <h2>".$suchtitel."</h2><br>";

    $text = strstr($_POST['ausgesucht'], '#');
    $text=substr($text,1);
        
    $url = JRoute::_('index.php?option=com_content&view=article&id='.$text);
    
    echo "<h2><a href = '".$url."'>Link zum Artikel</a></h2>";
    
    
} //ende von if isset

Ich zerlege zunächst einmal die Eingabe aus dem Textfeld und klemme für die Ausgabe des Titels alles vor dem # ab. Dann hole ich mir die Zahl nach dem #, das ist die ID des Beitrags. Zu dem hole ich mir mit dem JRoute::… die URL, aus der bastle ich mir den Link zum Beitrag. Fertig!

contacts

contacts

Das Ganze hat natürlich noch Optimierungspotential, zum Beispiel hält niemand den Benutzer davon ab, einfach irgendeinen String einzugeben und dann auf Return zu drücken, dann läuft natürlich der Link auf einen häßlichen Fehler. Aber das sind Details, die man noch verfeinern kann wenn man möchte, das sollte nicht allzu schwierig sein. Viel Spaß jedenfalls beim Nachbauen!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.