Gehts auch in Joomla? Bewertungsformular mit Ajax

Was in WordPress und Drupal so reibungslos geklappt hat, sollte doch euch in Joomla zu realisieren sein: ein kleines Bewertungsformular mit Notenvergabe am Ende jedes Artikels, das sich beim Abschicken einer neuen Benotung automatisch aktualisiert. Aussehen soll das Ganze in etwa so:

bewertungformular1

bewertungformular1

Wie kriegt man so etwas ans Ende jedes Artikels?

Mit einem Override, und der ist in Joomla 3 nicht weiter schwierig zu erstellen. Man geht zu Erweiterungen/Templates/Templates und klickt beim aktiven Template auf Details und Dateien, dann auf den Reiter Overrides erstellen. Unter Komponenten/com_content article anklicken, dann sollte eine solche Nachricht erscheinen:

Nachricht

Es wurde ein Override erstellt in \templates\protostar\html\com_content\article
Ein neuer Override wurde erstellt.

Man kann den Override jetzt direkt im eingebauten Editor bearbeiten, ich nehme lieber den Notepad++. Um die richtige Position für das Formular zu ermitteln, sucht man nach folgender Div und klemmt danach einen Platzhalter rein:

<div itemprop="articleBody">
 <?php echo $this->item->text; ?>
 </div>
<h1>Hier kommt das Formular hin</h1>

Dann sollte in jedem Artikel so etwas auftauchen:

override_formularposition

override_formularposition

OK, die richtige Position hätten wir jetzt. Aber was genau soll da hin? Das einzig senkrechte wäre natürlich ein eigenes Modul, das die gesamte Logik für die Benotung enthält, und dafür müssen wir ein bisschen ausholen.

Wie man ein einfaches Joomla-Modul erstellt

habe ich in diesem Artikel ausführlich beschrieben, da halten wir uns mal dran und basteln ein eigenes Modul mit Namen mod_el_bewertung. Das soll erstmal nichts machen ausser einen Platzhaltertext ausgeben, dafür sieht die helper.php anfangs so aus:

<?php
/**
 * Helper class for EL Bewertung! module
 * 
 * @package Joomla.Tutorials
 * @subpackage Modules
 * @link http://docs.joomla.org/J3.x:Creating_a_simple_module/Developing_a_Basic_Module
 * @license GNU/GPL, see LICENSE.php
 * mod_helloworld is free software. This version may have been modified pursuant
 * to the GNU General Public License, and as distributed it includes or
 * is derivative of works licensed under the GNU General Public License or
 * other free or open source software licenses.
 */
class ModHelloWorldHelper
{
 /**

 * Retrieves the hello message
 *
 * @param array $params An object containing the module parameters
 *
 * @access public
 */ 
 public static function getHello($params)
 {
 return 'Hier soll das Formular hin!';
 
 }

Wenn alles geklappt hat, kann man das neue Modul jetzt aktivieren. Dazu unter Erweiterungen/Verwalten/überprüfen das richtige Modul suchen und installieren, dann unter Erweiterungen/Module/neu den eigenen Modultyp anwählen und einen Titel vergeben.

Modul im Override platzieren

 Dafür fügt man im Override der default.php folgende Zeile ein:

<?php echo JHtml::_('content.prepare', '{loadposition bewertungsformular}'); ?>

Nochmal zurück ins neuerstellte Modul, bei Modulposition bewertungsformular eingeben, Schreibweise beachten! Jetzt sollte die Modulüberschrift und der Platzhaltertext am Ende jedes Artikels auftauchen:

platzhalter

platzhalter

So, das waren mal die Vorarbeiten. Jetzt gehts zur Sache.

Der Code für das Formular

kommt in die helper.php in die public static function getHello() (ich war hier bloß zu faul um die Funktions- und Klassenbezeichnungen anzupassen). Wichtig ist am Anfang, dass wir uns die ID des aktuellen Beitrags holen und in die Div mit der ID ‚beitragsid‘ packen.

public static function getHello($params)
 {
 //ID des aktuellen Artikels bestimmen';
 $id = JRequest::getVar('id');
 echo 'ID dieses Beitrags: <span id = "beitragsid">'.$id.'</span>';
 
 echo '<div style="border:1px dotted #000; text-align:center; padding:10px;">';
 echo '<h4>Dir gefällt dieser Beitrag?</h4>';
 echo '<p>Dann bewerte ihn!</p>';
 echo '</div>';
 
 echo " <form action = '' method='post'>";
 
 echo "<fieldset>
 <div id = 'noten' class = 'noten' style = 'border:2px solid blue; padding :4px;'>
 <input type='radio' name='note' value='1'>sehr gut
 <input type='radio' name='note' value='2'>gut
 <input type='radio' name='note' value='3'>befriedigend<br>
 <input type='radio' name='note' value='4'>ausreichend
 <input type='radio' name='note' value='5'>mangelhaft
 <input type='radio' name='note' value='6'>ungenügend
 
 <input type ='button' name = 'absenden' class = 'bew-button' value = 'abschicken' onclick='el_bew_ajax()'/>
 
 </div>
 </fieldset>";
 echo "</form>";
 
 
 }

Das Javascript für den onclick-Aufruf packen wir der Einfachheit halber an den Anfang der  helper.php in Script-Tags, dazu gleich mehr. Jetzt brauchen wir erst noch die Tabelle für die Bewertungen, die sieht genauso aus wie in WordPress oder Drupal und hat genau drei Felder:

bewertungentabelle

bewertungentabelle

id ist Autowert, beitragsid und note schreiben wir dann gleich rein. Jetzt lesen wir erst einmal die schon vorhandenen Benotungen aus.

Bereits vorhandene Anzahl Bewertungen und Durchschnitt ermitteln

Dabei kommt uns JDatabase zu Hilfe, das geht so:

//Bereits vorhandene Benotungen abholen
 $db = JFactory::getDBO();
 $query = "SELECT * FROM bewertungen where beitragsid = ".$id."";
 $db->setQuery($query);
 $result = $db->execute();
 $anzahl = $db->getNumRows();
 //echo $anzahl." Bewertungen gefunden";
 
 $results = $db->loadObjectList();
 $durchschnitt = 0;
 foreach ($results as $zeile) :
 $durchschnitt = $durchschnitt+ intval($zeile->note);
 endforeach;
 
 if ($anzahl != 0){ 
 $durchschnitt = $durchschnitt / $anzahl;
 }
 echo "<span id = 'ausgabe'>".$anzahl." Bewertungen gefunden, Durchschnitt: ".$durchschnitt."<span>";

Wichtig ist hier, dass man sich mit dem $db->loadObjectList(); die Ergebnisliste der SQL-Abfrage holt, über die kann man dann mit dem Foreach iterieren. So, aber jetzt endlich:

Das Javascript

Das kann man nahezu unverändert aus der WordPress-Lösung übernehmen, man stellt es einfach an den Anfang der helper.php, zunächst mal nur mit Debug-Ausgabe von Beitragsid und Note:

<script>
 function el_bew_ajax(){
 
 
 //Beitragsid aus span holen
 var akt_id = document.getElementById("beitragsid").innerHTML;
 
 //Note aus den Radiobuttons holen
 var akt_note = jQuery("input:radio[name=note]:checked").val();
 
 Debug-Ausgabe
 alert(akt_note + ' ' + akt_id);
 
 //Check ob keine Note angewählt, Abbruch
 if (typeof akt_note === 'undefined'){
 alert('Bitte eine Note wählen!');
 return;}
 
 //***********************Hier kommt der ajax-call hin
 
</script>

Die Callback-Funktion

Für den Ajax brauchen wir natürlich erstmal die Callback-Funktion, die stecken wir in eine separate PHP-Datei.

<?php

$id = '';
if ( isset($_GET['id']) )
{
  $id = $_GET['id'];
}
$note = '';
if ( isset($_GET['note']) )
{
  $note = $_GET['note'];
}

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

//neue Note erstmal einfügen
$sql = "INSERT INTO bewertungen (beitragsid, note) VALUES (".$id.",".$note.")";
$neu = $pdo->query($sql);

//Suche nach aktueller id
$sql = "SELECT * FROM bewertungen where beitragsid = ".$id."";
$records = $pdo->query($sql);
$gefunden = $records->rowcount();

$durchschnitt = 0;
foreach ($records as $row) {
        
        $durchschnitt = $durchschnitt + $row['note'];
    }
if ($gefunden != 0){        
    $durchschnitt = $durchschnitt / $gefunden;
    }
echo $gefunden." Bewertungen vorhanden, Durchschnittsnote = ".$durchschnitt;

?>

Das ist genau das selbe straighte PHP, das ich auch in der Drupal-Lösung schon verwendet habe, man muss nur die Login-Informationen im PDO anpassen. Jetzt fehlt nur noch:

Das Script mit dem Ajax-Call

Ich hab mir dafür noch den Pfad zum Modulverzeichnis in eine versteckte Span geschrieben, das passiert in der helper.php vor dem Formular:

//Pfad zum Modulverzeichnis abholen
        $pfad = JURI::base().'modules/'.'mod_el_bewertung/';
        echo '<span id = "pfad" style = "display:none;">'.$pfad.'</span>';

Dann kann unser Script marschieren:

<script>
 function el_bew_ajax(){
            
            //Pfad zum Modulverzeichnis abholen
            var modulpfad = document.getElementById("pfad").innerHTML;
            
            //Beitragsid holen
            var akt_id = document.getElementById("beitragsid").innerHTML;
                        
            //Note aus den Radiobuttons holen
            var akt_note = jQuery("input:radio[name=note]:checked").val();
            
            //Debug-Ausgabe
            //alert(akt_note + ' ' + akt_id);
                        
            //Check ob keine Note angewählt, Abbruch
            if (typeof akt_note === 'undefined'){
            alert('Bitte eine Note wählen!');
            return;}
            
            //***********************ajax-call
            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("ausgabe").innerHTML = xmlhttp.responseText;
               //alert(xmlhttp.responseText);
              }
             }
             xmlhttp.open("GET",""+modulpfad+"callback_ajax_el.php?id="+akt_id+"&note="+akt_note,true);
             
             xmlhttp.send();
            
            
            
            //**********************end ajax call
}//end function el_bew_ajax

</script>

Fertig! Der Call ist scharfgeschaltet, beim abschicken einer neuen Bewertung aktualisiert sich die Anzeige.

anzeige

anzeige

Fazit

In Joomla eine einfaches Modul zu erstellen ist erstmal gewöhnungsbedürftig, aber wenn man die Logik einmal überrissen hat, kann man viel damit machen. Was mir nicht besonders gefällt, ist dass ich noch keine einfache Möglichkeit gefunden habe, einen Override nur für eine bestimmte Kategorie anzulegen, ich hätte zum Beispiel das Bewertungsformular gerne nur in der Kategorie ‚Rezepte‘. Ich hab mir da schonmal damit beholfen, in die Override-Datei eine Datenbankabfrage einzubauen, die zur aktuellen Beitragsid die Kategorie abholt, die ging so:

<?php
    $db=JFactory::getDbo();
    $article_id = JFactory::getApplication()->input->get(‚id‘);
    echo „aktuelle Artikelid= „.$article_id;
    
    $db->setQuery(’select catid from #__content where id= ‚.$article_id.“);
    $catid=$db->loadResult();
    echo „aktuelle catid= „.$catid;
    
    if ($catid == 8){
        echo JHtml::_(‚content.prepare‘, ‚{loadposition bewertungsformular}‘);
    }
    else {
        echo „Dies ist kein Rezept, Kategorie: „.$catid;
        }
    ?>

Aber das ist meiner Ansicht nach eine ziemliche Krücke. Ich muss mal ein bisschen forschen, ob das nicht eleganter geht.