Nummerische Pagination mit Werten aus Array (pure PHP)

Das hat jetzt mit WordPress nur noch insofern etwas zu tun, als es einem auch hier mal unterkommen kann, dass die Daten für eine seitenweise nummerierte Ausgabe in Form eines Arrays ankommen und nicht direkt mit SQL aus der Datenbank abgefragt werden. Typischerweise passiert sowas, wenn man über eine API auf die Datenbank zugreift und die Ergebnisse der Abfrage als JSON zurückgegeben werden. Das kann man mit Hilfe von json_decode() flugs in ein Array umwandeln und mit einem foreach() wie gehabt seitenweise ausgeben. Wenn allerdings die Datensätze pro Seite einen Rest ergeben, fliegt PHP bei der letzten Seite auf die Nase und spuckt so etwas aus:

Notice: Undefined offset: 334 in C:\xampp\htdocs\kleine_api\err_num_page.php on line 74

Die kleine Tücke: der Rest

Wir haben, sagen wir mal, 334 Datensätze. Und wir legen die Anzahl der Datensätze pro Seite auf 10 fest. Das gibt 33 ganze Seiten a 10 Datensätze, und auf der letzten Seite haben wir nur noch vier Datensätze. Da muss man jetzt eine Sonderbehandlung für den Rest einbauen, weil unser Array ja nur einen Index bis 333 zuläßt – Offset by one, den 334. Array-Entrag gibt es nicht mehr. Dieses Dilemma hat man nicht, wenn man direkt auf die Datenbank zugreift und mit einem SQL-Limit arbeiten kann, da ist es egal wenn am Ende die Seite nicht ganz voll wird, es gibt keine Fehlermeldung.

Aber, wir arbeiten ja mit einem Array, und da muss man die ganze Logik ein bisschen umstricken. Also, frisch ans Werk!

Das Array

… enthält 334 Kochrezepte und sieht nach einem json_decode in etwa so aus:

array(1) 
{ ["rezepte"]=> array(334) {
 [0]=> array(3) { ["ID"]=> string(4) "1828" ["post_title"]=> string(31) "Pudding nach Art des Hauses Leu" ["post_content"]=> string(37) "Vanille oder Schoko oder Johannisbeer" }
 [1]=> array(3) { ["ID"]=> string(4) "1827" ["post_title"]=> string(5) "Milch" ["post_content"]=> string(23) "reicht nicht bis morgen" } 
 [2]=> array(3) { ["ID"]=> string(4) "1825" ["post_title"]=> string(14) "Petersilwurzel" ["post_content"]=> string(29) "hab ich abgeerntet, ist nicht" }

Das heißt, um beispielsweise an die ID und den Titel eines Datensatzes heranzukommen, muss ich folgende Syntax verwenden:

$akt_id = $dataObject['rezepte'][$i]['ID'];
$akt_titel = $dataObject['rezepte'][$i]['post_title'];

Das ist jetzt noch nicht weiter tragisch, ich muss nur daran denken dass mein Array bei 0 anfängt, das heißt bei einer Anzahl Datensätze pro Seite von X darf ich nur von 0 bis X-1 ausgeben. Praktisch sieht das z.B.bei 10 Datensätzen pro Seite so aus:

Seite 1: Datensätze 0…9
Seite 2: Datensätze 10…19
Seite 3: Datensätze 20…29

letzte Seite: der Rest

Das heisst aber, ich muss für meine ganzen Seiten eine andere Logik anwenden als für die letzte Seite, auf der auch weniger als 10 Datensätze stehen können.

Aber fangen wir erstmal vorne an:

Ermitteln der Seitenzahl und des Restes

Mein Array mit den 334 Datensätzen heißt $dataObject und wurde aus einem JSON in ein Array umgewandelt. Jetzt rechne ich erstmal aus, wieviele ganze Seiten das ergibt, wenn ich hier beispielsweise 10 Datensätze pro Seite ausgebe, und welcher Rest bleibt.

<?php

//JSON alle Rezepte abholen und in Array umwandeln
$json = file_get_contents('http://localhost/kleine_api/rezepte/rezeptelesen.php');
$dataObject = json_decode($json,true);

//Rezepte pro Seite ausrechnen
$pro_seite = 10; //Wert kann beliebig geändert werden
$anzahl = count($dataObject['rezepte']);
echo "Datens&auml;tze gesamt = ".$anzahl."<br>";
echo "Das macht ".floor($anzahl/$pro_seite)." ganze Seiten
 bei ".$pro_seite." Rezepten pro Seite, Rest: ".$anzahl%$pro_seite."<br>";

Ich habe mit Absicht eine sehr geschwätzige echo-Ausgabe eingebaut, zur besseren Kontrolle. Der Output sieht jetzt mal so aus:

echo_kennzahlen

echo_kennzahlen

Damit kann ich mir jetzt schon mal die Buttons für die einzelnen Seiten zusammenbauen. Das mache ich wieder mit einem Formular. Ich zähle mein i von Seite 1 bis zur letzten Seite inklusive hoch, als Value kriegt jeder Button den aktuellen Wert von i. Ausserdem kriegt jeder Button noch eine ID, die brauchen wir später noch.

//Formular mit Buttons für die Seitenzahlen, Start 1, Ende: nächsthöhere Ganzzahl
for ($i=1; $i <=ceil($anzahl/$pro_seite); $i++){
    echo "<form action = 'array_num_page.php' method = 'get'>";
    echo "<input type='submit' id='el_num_button_".$i."' name=akt_i value='".$i."'>";
}
echo "</form>";

Das sieht jetzt schonmal so aus:

buttons

buttons

Jetzt gehts daran zu ermitteln, welche Seite gerade aktuell ist. Ich starte als Default mit der 1, und je nachdem welchen Button der Anwender angeklickt hat hole ich mir den Wert aus dem Formular.

//Checken ob eine Seite geklickt wurde
$seitenstart=1;
if (isset($_GET['akt_i'])){
    $seitenstart = $_GET['akt_i'];
    }else{
    $seitenstart=1;
    }
//versteckte Ausgabe für farbige Markierung der aktuellen Seite mit Script    
echo "<div id ='seitenstart' style='display:none;'>".$seitenstart."</div>";

Meinen Seitenstart schreibe ich mir noch in eine versteckte Div, die brauche ich nachher für die farbige Markierung der aktuellen Seite.

Jetzt gehts erst mal an die Ausgabe.

Die ganzen Seiten

Hier darf ich nur bis zur letzten ganzen Seite hochzählen, die letzte Seite mit den weniger Datensätzen kommt später dran:

//Ausgabe aller ganzen Seiten von 1 bis zum letzten ganzzahligen Vielfachen
if (($seitenstart > 0) and ($seitenstart < floor($anzahl/$pro_seite))){

//Tabelle für Ausgabe aller Rezepte zusammenbauen
echo "<table border : 1px>";
echo "<th>ID</th><th>Titel</th><th>Content</th> 
/*Ausgabe als Tabelle*/

Jetzt wird es ein bisschen knifflig. Ich bleibe mal bei einem Wert pro Seite von 10 Datensätzen, es klappt aber auch mit jedem anderen Wert.

Auf Seite eins muss ich bei 0 anfangen und bis eins weniger als die Anzahl der Datensätze pro Seite hochzählen, also bis 9. Auf Seite zwei muss ich bei 10 anfangen und bis 19 hochzählen, usw.:

//Bei 0 anfangen, bis max. eins weniger als die Anzahl der ganzen Seiten hochzählen (0..9, 10..19 usw...)
for ($i=($seitenstart-1)*$pro_seite; $i <= $pro_seite*$seitenstart-1; $i++){

Die ganze for-Schleife mit der Ausgabe der einzelnen Werte aus dem Array in Tabellenzeilen sieht so aus:

//Bei 0 anfangen, bis max. eins weniger als die Anzahl der ganzen Seiten hochzählen (0..9, 10..19 usw...)
for ($i=($seitenstart-1)*$pro_seite; $i <= $pro_seite*$seitenstart-1; $i++){

echo "<tr>";
//Werte aus dem Array auf Var. legen
$akt_id = $dataObject['rezepte'][$i]['ID'];
$akt_titel = $dataObject['rezepte'][$i]['post_title'];
//Content tags raus und auf 100 Zeichen kürzen
$akt_content = substr(strip_tags($dataObject['rezepte'][$i]['post_content']),0,200);

echo "<td>".$akt_id."</td><td>".$akt_titel."</td><td>".$akt_content."</td>";
echo "</tr>";
}

Damit haben wir die Ausgabe aller ganzen Seiten erledigt. Jetzt kümmern wir uns um den Rest. Da nehmen wir nur noch die Indizes aus dem Array, die grösser als der letzte Wert vor dem letzten ganzzahligen Vielfachen der Seitenzahl sind, und marschieren nach oben durch bis 1 vor der Gesamtzahl der Datensätze.

//****Ausgabe Rest wenn Seitenzahl grösser als das letzte ganzzahlige Vielfache ist
if($seitenstart > floor($anzahl/$pro_seite)){
...

//starten beim letzten ganzzahligen Vielfachen der Gesamt-Seitenzahl, 
//stoppen eins unter Gesamtzahl
for ($i=floor($anzahl/$pro_seite)*$pro_seite; $i < $anzahl; $i++){

Voila, das wars! Der ganze Code mit Ausgabe der Tabelle sieht so aus:

//**************************Ausgabe Rest wenn Seitenzahl grösser als das letzte ganzzahlige Vielfache ist
if($seitenstart > floor($anzahl/$pro_seite)){
    

//Tabelle für Ausgabe des Rests zusammenbauen
echo "<table border : 1px>";
echo "<th>ID</th><th>Titel</th><th>Content</th>";

//starten beim letzten ganzzahligen Vielfachen der Gesamt-Seitenzahl, stoppen eins unter Gesamtzahl
for ($i=floor($anzahl/$pro_seite)*$pro_seite; $i < $anzahl; $i++){

echo "<tr>";
//Werte aus dem Array auf Var. legen
$akt_id = $dataObject['rezepte'][$i]['ID'];
$akt_titel = $dataObject['rezepte'][$i]['post_title'];
//Content tags raus und auf 100 Zeichen kürzen
$akt_content = substr(strip_tags($dataObject['rezepte'][$i]['post_content']),0,200);

echo "<td>".$akt_id."</td><td>".$akt_titel."</td><td>".$akt_content."</td>";
echo "</tr>";
}
echo "</table>";
    
}

Noch ein Javascript-Zuckerl: aktuelle Seite farbig markieren

Ich hab mir ja zu jedem Seitenzahl-Button eine ID generiert, das sah so aus:

echo "<input type='submit' id='el_num_button_".$i."' name=akt_i value='".$i."'>";

Und ich habe mir den Wert der aktuellen Seite (default 1) in eine versteckte div geschrieben:

//versteckte Ausgabe für farbige Markierung der aktuellen Seite mit Script    
echo "<div id ='seitenstart' style='display:none;'>".$seitenstart."</div>";

Damit habe ich mir ein kleines Javascripterl gebastelt, das die aktuelle Seite farbig markiert:

<script>

    $(document).ready(function() {
    var dieid = document.getElementById('seitenstart').innerHTML;
    //alert(dieid);
    document.getElementById('el_num_button_'+dieid).style.backgroundColor = "lightgreen";
    }); 


</script>

Ich klaube mir die aktuelle Seitenzahl aus der verstecken Div und spreche damit die zugehörige Button-ID an, die style ich mir nach Belieben, das war auch schon alles. Hübscher kleiner Effekt!

seite_farbig

seite_farbig

 

Kontrastfarbe automatisch berechnen mit jquery/Javascript

Das hat jetzt mit WordPress nur insoweit was zu tun, als ich eine Anforderung vom Kunden hatte, ein Info-Widget mit in zufälligen Intervallen wechselnden Farben zu hinterlegen, der wollte das halt gern so haben. Dabei stellte sich aber gleich die Herausforderung, dass der Text (default schwarz) bei dunklen Hintergrundfarben natürlich schlecht bis gar nicht zu lesen war. Will heissen, die Textfarbe sollte dann auch dynamisch angepasst werden, und das mach ich mit jquery/Javascript. Dafür hab ich mir eine Lösungsstrategie zusammengegooglet, die man vielleicht mal irgendwo anders brauchen kann, deswegen kommt sie hier rein.

Zufällige Farbe für den Hintergrund bestimmen

Dafür habe ich mir dieses recht überschaubare Snippet ergooglet, das sehr zuverlässig funktioniert:

//******Zufällige Hex-Farbe erzeugen*********************/
    function randomColor(){
    
    var x=Math.round(0xffffff * Math.random()).toString(16);
    var y=(6-x.length);
    var z="000000";
    var z1 = z.substring(0,y);
    var color= "#" + z1 + x;
    
    return color;
    }

Relative (wahrgenommene) Helligkeit einer Farbe bestimmen

Dafür gibt es vom w3c eine Faustformel, die ich für den Kontrast bei barrierefreien Seiten immer wieder mal brauche, die geht so:

//***********relative Helligkeit von r,g,b bestimmen
    function Brightness(r, g, b)
    {
       return Math.sqrt(
          r * r * .241 + 
          g * g * .691 + 
          b * b * .068);
    }

Man füttert sie mit den RGB-Werten einer Farbe und kriegt einen Wert zwischen 0 und 255 heraus, der die relative Helligkeit einer Farbe bezeichnet. Man nimmt dann als Kontrastfarbe für die untere Hälfte des Farbspektrums weiß, für die obere Hälfte schwarz. Den Schwellwert von 130 kann man nach Geschmack noch ein bisschen nach oben oder unten anpassen.

//****************Textfarbe je nach Schwellwert zurückgeben
    function textfarbe(helligkeit){
    if (helligkeit > 130){return 'black'}
    if (helligkeit <= 130){return 'white'}

    }

Wir haben Hex und brauchen RGB

Deswegen gibts noch eine Umwandlungsfunktion, die uns den Hex-String zerlegt und die RGB_Werte liefert:

//************rgb-Werte aus hex holen
    function hexToR(h) {return parseInt((cutHex(h)).substring(0,2),16)}
    function hexToG(h) {return parseInt((cutHex(h)).substring(2,4),16)}
    function hexToB(h) {return parseInt((cutHex(h)).substring(4,6),16)}
    function cutHex(h) {return (h.charAt(0)=="#") ? h.substring(1,7):h}

var R = hexToR(aktfarbe);
var G = hexToG(aktfarbe);
var B = hexToB(aktfarbe);

Mit den drei Variablen kann man dann die Funktion Brightness() aufrufen, und mit der wiederum die Funktion textfarbe(), die uns dann schlussendlich die passende Kontrastfarbe für unsere zufällige Hintergrundfarbe liefert. Ich gebe zu es ist ein bisschen von hinten durch die Brust ins Auge, aber es funktioniert. Hier mal das ganze Script zum Nachvollziehen:

Das Script

<script>
    $(document).ready(function(){
    setInterval(farbwechsler, 5000)
    farbwechsler();
    
    //******Zufällige Hex-Farbe erzeugen*********************/
    function randomColor(){
    
    var x=Math.round(0xffffff * Math.random()).toString(16);
    var y=(6-x.length);
    var z="000000";
    var z1 = z.substring(0,y);
    var color= "#" + z1 + x;
    
    return color;
    }

    //************rgb-Werte aus hex holen
    function hexToR(h) {return parseInt((cutHex(h)).substring(0,2),16)}
    function hexToG(h) {return parseInt((cutHex(h)).substring(2,4),16)}
    function hexToB(h) {return parseInt((cutHex(h)).substring(4,6),16)}
    function cutHex(h) {return (h.charAt(0)=="#") ? h.substring(1,7):h}
    
    //***********relative Helligkeit von r,g,b bestimmen
    function Brightness(r, g, b)
    {
       return Math.sqrt(
          r * r * .241 + 
          g * g * .691 + 
          b * b * .068);
    }
    
    //****************Textfarbe je nach Schwellwert zurückgeben
    function textfarbe(helligkeit){
    if (helligkeit > 130){return 'black'}
    if (helligkeit < 130){return 'white'}

    }
        
function farbwechsler(){

var aktfarbe = randomColor();

var R = hexToR(aktfarbe);
var G = hexToG(aktfarbe);
var B = hexToB(aktfarbe);


           document.getElementById("zufall").style.backgroundColor = aktfarbe;
           document.getElementById("inhalt").innerHTML ="Hex: "+ aktfarbe;
           document.getElementById("rgb").innerHTML = "rgb: "+R+", "+G+", "+B;
           document.getElementById("brightness").innerHTML ="Helligkeit: "+ Brightness(R,G,B);
           document.getElementById("schrift").style.backgroundColor = aktfarbe;
           document.getElementById("schrift").style.color = textfarbe(Brightness(R,G,B));
           }

    
  })
</script>

Ich hab mal die Funktion farbwechsler() auf alle 5 Sekunden gesetzt, für den Demo-Effekt.

Und hier noch das passende HTML zum Ausprobieren:

<body>

<h1>Beispiel Contrast Colors mit Javascript</h1>
<div id = "zufall">Schriftfarbe statisch</div>
<div id="inhalt">...</div>
<div id="rgb">...</div>
<div id="brightness">...</div>
<div id="schrift"><h1>Schriftfarbe dynamisch</h1></div>

</body>

Das wars! So gibt es immer lesbare Schrift auch auf zufällig generierten Hintergrundfarben.

Ne kleine Fingerübung: Beitragshitparade mit AJAX-Refresh

Weil ich mich dabei erwischt habe, dass ich immer wieder F5 drücke, wenn ich meine Beitragshitparade anschaue, bin ich auf die Idee gekommen, den Refresh der Anzeige alle x Sekunden automatisch einzubauen. Das geht in WordPress ganz flott, hatten wir auch alles so ähnlich schonmal, aber weil man so etwas immer wieder mal brauchen kann, hier der Code.

Wir basteln uns dafür natürlich ein Plugin

In das kommt zuallererst ein sehr kurzer Shortcode, der macht eigentlich nichts anderes als eine benannte Div  für die Ausgabe bereitzustellen.

<?php
/*
Plugin Name: Ajax Hitparade
Plugin URI: http://localhost/wp_ajax/wp-content/plugins/wp-ajax
Description: Ausgabe der beliebtesten Beiträge mit Ajax Refresh
Version: 1.0
Author: Evi Leu
Author URI: http://www.evileu.de
*/


 function a_ausgabe(){
  return "Die 10 beliebtesten Rezepte<br><div id='ajax_ausgabe'>...</div>";
  
 }
 add_shortcode('ajax_ausgabe', 'a_ausgabe');

Dann geben wir WordPress bekannt, dass wir einen Ajax-Call ausführen wollen:

/**************ajax-action für wordpress definieren*/ 
add_action( 'wp_ajax_hitparade_action', 'hitparade_action' );
add_action( 'wp_ajax_nopriv_hitparade_action', 'hitparade_action' );


Das Javascript

Jetzt binden wir unsere js-Datei mit dem Script ein, und geben ihr mit dem wp_localize_script den Pfad zur Ajax-URL von WordPress mit:

/*********Externes Script einbinden und Pfad zur admin-ajax.php übergeben*/
function myhitparade_js() {
      
    wp_enqueue_script('myhitparade-js', plugins_url().'/ajax-hitparade/wp_ajax_hitparade.js', array('jquery'), false, false);
    
    wp_localize_script( 'myhitparade-js', 'my_ajaxurl', admin_url( 'admin-ajax.php' ) );
 
   }
add_action('wp_enqueue_scripts', 'myhitparade_js');

Die Script-Datei wp_ajax_hitparade.js liegt in unserem Plugin-Verzeichnis und sieht so aus:

$(document).ready(function() {
     setInterval(myFunction, 1000)  
       
    });
    
        
        function myFunction(){
                
        
        var data = {
            'action': 'hitparade_action',
            
            };

        // der volle Pfad zur admin-ajax.php liegt auf my_ajaxurl
        jQuery.post(my_ajaxurl, data, function(response) {
                    
            document.getElementById("ajax_ausgabe").innerHTML = response;
            
        });
        } //*********************End myFunction

Der Witz ist hier natürlich das setInterval(), das führt unsere Funktion alle X Millisekunden aus. Ansonsten geben wir der Funktion nur mit, dass sie die Callback-Funktion hitparade_action ausführen soll, es gibt keine zu übergebenden Parameter ausser der Ajax-URL. Das Ergebnis des Calls wird in die div ajax_ausgabe geschrieben.

Die Callback-Funktion

Kommt als Letztes in unser Plugin, sie ist sehr simpel und besteht nur aus einer Query auf die Tabelle Counter und einem foreach für die Ausgabe:

/********Callback-Funktion**************/
function hitparade_action() {
    global $wpdb; 
  
    $alleposts = $wpdb->get_results( "SELECT * from counter ORDER BY zaehler DESC limit 10");
    foreach($alleposts as $einpost){
        
        echo $einpost->titel." ".$einpost->zaehler."</br>";
}
    /**********************************************/
    
    
    wp_die(); // this is required to terminate immediately and return a proper response
} //**********End function hitparade_action

Das sollts gewesen sein! Das Script checkt alle 1000 Millisekunden die Datenbank neu ab und aktualisiert die Anzeige dementsprechend. Viel Spaß beim Nachbauen!

Die Beitrags-Hitparade V.01

Das, meine verehrten Besucher, sind die am öftesten aufgerufenen Beiträge der letzten Tage, ermittelt mit dem Rezeptecounter-Plugin. Es befindet sich noch im Beta-Stadium, aber ich bin schon ganz zufrieden damit, so hab ich einen echten Überblick, welche Themen am meisten gesucht werden. Klickt nur fleissig weiter, ich zähl jetzt mit! 🙂

Die beliebtesten Beiträge

Ein kleiner Chat in PHP – wieder mal was für Minimalisten (6520)
Kraut und Rüben auf der Datenbank: wo wooCommerce die Produktdaten speichert (4308)
Noch mehr postmeta: benutzerdefinierte Felder in wooCommerce (4266)
Kontrastfarbe automatisch berechnen mit jquery/Javascript (4120)
Joomla-Template 2: die index.php (4028)
Schmankerl für alte Datenbanker: Zugriff auf externe Daten mit dem wpdb-Objekt (3914)
Alphabetische Paginierung aus Array: die Tücken des PHP-sort() (3280)
Ein einfaches Joomla-Template erstellen (2793)
PHP 5.6 forever? (2764)
HTML5 Datalist Value – ganz so einfach ist es nicht (2749)

 

Wen es interessiert: ich habe hier einen Durchschnitt von ca 100 Beitragsaufrufen am Tag, am Wochenende waren es sogar über 200 – nicht schlecht für einen kleinen privaten Plauderblog, finde ich.

PHP 5.6 forever? Und kleine Auswertung des Rezeptecounters

Also, ich geb zu das hat mit WordPress nur am Rande was zu tun, aber ich denke mir dass andere Leute vor dem selben Dilemma stehen und werde deswegen ein paar Worte zu dem Thema verlieren.

Man ist ja mittlerweile bei PHP 7.x angelangt, und es hat sich seit den 5er Versionen einiges getan, besonders in Sachen Datenbankzugriff. PDO und mysqli hat mysql abgelöst, und wir sind nicht abwärtskompatibel. Das würde mir noch nicht mal viel ausmachen, weil ich auf meiner evileu.de kaum PHP-Skripte für den Datenbankzugriff benutze, das läuft eigentlich alles über WordPress. Aber an manchen Stellen kneifts dann doch, und besonders für meine kleinen SEO-Statistiken hätt ich dann doch das eine oder andere PHP-Script gebraucht. Lokal (XAMPP) arbeite ich schon lang mit PHP 7 und hab eigentlich gar nicht mehr darüber nachgedacht ob ich die 5er Version nochmal brauchen könnte.

Aber die Live-Seite evileu de läuft bei meinem Provider (Strato) noch auf PHP 5.6, und das wird wohl auch so bleiben. Ein Anruf bei der Hotline hat ergeben, daß von einer Umstellung auf PHP 7.x dringendst abgeraten wird, weil es keine Garantie dafür gibt, dass vorhandene Webseiten dann noch laufen. Der Hotliner meinte sogar, dass in den meisten Fällen die Webseiten neu aufgesetzt werden müssten… Vielleicht bin ich da auch nur übervorsichtig, WordPress sollte ja eigentlich mit den neuesten PHP-Versionen klarkommen, aber „der Teufel ist ein Oachkatzl“ – weiss man’s genau?

Jedenfalls rät Strato von einer Umstellung dringend ab, und ich bekam auch die Auskunft dass bei denen noch 99,9% aller Webseiten auf PHP 5.6 laufen. Tscha, da werde ich mich wohl der Mehrheit anschliessen, schon auch weil es bei Umstellungsproblemen der PHP-Version von Strato keinen Support gibt, jedenfalls nicht an der kostenfreien Hotline. Bin bloss gespannt wie lange WordPress die Grätsche noch mitmacht – irgendwann wird es eine Version geben, die nur noch auf PHP 7.x läuft, und dann wirds echt spannend.

Was mach ich bis dahin? Plugins und Shortcodes statt PHP-Scripte nutzen, wo immer es möglich ist, weil ich beim Programmieren Knoten in die Tippfinger kriege, wenn ich ständig zwischen PHP 5 und 7 wechseln muss. Gefallen tut mir das nicht, aber ich hab ja etwa ein Dutzend WordPress-Blogs am Laufen, und da will ich nicht das geringste Risiko eingehen. Also, PHP 5.6 forever – obs mir gefällt oder nicht.

PHP-5-Script für die Auswertung des Rezeptecounters

Für alle, die es auch nicht mehr parat haben, wie das mit dem mysql_connect damals so ging, hier ein kleines Script zur Auswertung der counter-Tabelle (siehe dieser Beitrag zum Rezeptecounter) Die Tabelle ist ja recht einfach aufgebaut:

tabelle_counter

tabelle_counter

Fehlte eigentlich nur noch die Aufsummierung der Gesamtzahl der Aufrufe, das erledigen wir hier gleich in einem Aufwasch:

$dbh = mysql_connect("[hostname]", "[db user]", "[password]");
$query = "[db name]";
if (!mysql_query($query, $dbh)) die("Fehler bei der Datenbankverbindung.");

//Gesamtaufrufe summieren***********************
$abfrage = "select sum(zaehler) as summe from counter";
$ergebnis = mysql_query($abfrage,$dbh);

while($row = mysql_fetch_assoc($ergebnis))
{
echo "<h2>Seitenaufrufe seit 20.6.2018: ".$row['summe']."</h2>";
}

//*********************

$sql = "SELECT * FROM counter ORDER BY zaehler DESC";

$result = mysql_query($sql);

while ($row = mysql_fetch_assoc($result)) {
    echo $row["cid"]." ";
    echo $row["rezeptid"]." ";
    echo $row["titel"]." ";
    echo "Z&auml;hler: ".$row["zaehler"]."<br>";
}

mysql_free_result($result);
?>

Das liefert einen ganz netten Überblick, hier die Zahlen von hier im Bistro seit gestern:

ausgabe_rezeptecounter

ausgabe_rezeptecounter

Ich gebe zu man könnte es schöner formatieren, aber mir langt das für einen groben Überblick. Ist echt eine Alternative zum Visitor Counter, der ja leider wegen der DSGVO weg musste.

Der Shortcode für die Rezepthitparade

… musste auch nur um zwei Zeilen ergänzt werden, damit er die Gesamtzahl der Aufrufe mit ausgibt:

function el_rezepthitparade(){
    global $wpdb;
    echo "<h2>Die 10 beliebtesten Rezepte</h2>";
    
    $gesamt = $wpdb->get_var("SELECT SUM(zaehler) FROM counter");
    echo $gesamt." Rezeptaufrufe seit Beginn der Zählung Juni 2018<br>";
    
    $alleposts = $wpdb->get_results( "SELECT * FROM counter 
    ORDER BY zaehler DESC LIMIT 10");
    foreach ($alleposts as $einpost){
        
        $pfad = get_the_permalink($einpost->rezeptid);
        echo "<a href = '".$pfad."'>".$einpost->titel."</a> (".$einpost->zaehler.")<br>";
    }
}
add_shortcode('hitparade','el_rezepthitparade');

Den get_var kann man hier einwandfrei nutzen, weil nur ein Wert von der Query zurückgegeben wird, den kann man dann auch gleich direkt mit echo ausgeben.

Nachtrag für Zählung der Seitenaufrufe

Mein Rezeptecounter ist schon fleissig im Einsatz, und ich bin mit den aussagekräftigen Ergebnissen sehr zufrieden. Nur noch eine kleine Ergänzung: in meinem Handarbeitsblog ist es für mich auch interessant zu wissen, welche statischen Seiten wie oft aufgerufen werden. Die kommen mit einer ganz kleinen Ergänzung der Zähler-Function mit in die counter-Tabelle. Statt nur if (is_single())… frage ich ab if(is:single() or is_page()), und schon sind die statischen Seiten auch mit in der Statistik.

Noch’n Nachschlag: Aufrufe pro Tag

Ich hab in meinem Archiv gekramt und eine Funktion aus dem Selfphp-Forum ausgebuddelt, mit der man die exakte Anzahl von Tagen zwischen zwei gegebenen Tagesdaten ermitteln kann. Die nutzen wir doch gleich mal um einen Tagesdurchschnitt auszurechnen. Das Startdatum der Zählung wird einfach fest eingegeben, das Enddatum (aktueller Tag) mit der date()-Funktion erzeugt, die Gesamtzahl der Aufrufe haben wir ja aus der Query, die liegt auf der Variablen $gesamt.

$gesamt = $row['summe'];
$timeA = '20.06.2018'; 
 $timeB = date("d.m.Y"); 

 $differenz = seDay($timeA,$timeB,"dmY","."); 
 
echo '<h3>In ' . $differenz . ' Tagen '.$gesamt.
" Aufrufe, das macht im Durchschnitt ".round(($gesamt/$differenz))." Aufrufe pro Tag</h3>";

Die Funktion seDay könnt ihr hier nachschlagen:  http://www.selfphp.de/kochbuch/kochbuch.php?code=11

Viel Spaß beim Nachbauen!

WordPress Beiträge: einfache nummerische Pagination

Dies ist als Ableger des vorigen Beitrags entstanden, eine simple nummerische Pagination der Rezepte ist auch nicht wesentlich schwieriger als die alphabetische Pagination und kann ganz ähnlich konstruiert werden. Ich mache das wieder mit einem Shortcode in einem Plugin. Zuerst wird die Anzahl der pro Seite auszugebenden Rezepte festgelegt und ermittelt, wie viele Seiten das werden:

function el_num_pagination(){

//Anzahl Rezepte pro Seite festlegen
$pro_seite = 15;
    
//Anzahl aller veröffentlichten Rezepte bestimmen
global $wpdb;

         $alleposts = $wpdb->get_results( "SELECT * from wp_posts 
         where post_type like 'post' and post_status like 'publish'");
        
        $gefunden = $wpdb->num_rows;
        echo $gefunden." Rezepte insgesamt<br>";
        
        //ceil: nächsthöhere Ganzzahl
        $anzahl_seiten = ceil($gefunden/$pro_seite);
        //Debug-Ausgabe
        echo "Das sind: ".$anzahl_seiten." Seiten bei ".$pro_seite." Rezepten pro Seite";

Dann baue ich mir das Formular mit den Buttons für die Seitenzahlen zusammen:

//Formular mit Buttons
echo "<form action = '#' method = 'post'>";

for ($i=1; $i <=$anzahl_seiten; $i++){
    echo "<input type='submit' id='el_num_button' name='".$i."' value='".$i."'>";
}
echo "</form>";

Die Buttons werden in der style.css noch ein bisschen hübscher formatiert, die müssen nur ein wenig breiter werden:

#el_num_button{
    height:30px;
    width:24px;
    padding:2px;
    border: 2px solid white;
    margin 2px;
    padding: 1px 1px 1px;
}

Dann laufe ich wieder durch alle Seitenzahlen durch und frage mit dem if(isset()) ab, ob eine Seitenzahl angeklickt wurde. Falls ja, wird die Ausgabefunktion mit zwei Parametern aufgerufen, der Nummer der aktuellen Seite und der Anzahl der Rezepte pro Seite:

for ($j = 1; $j <= $anzahl_seiten; $j++){    
        if (isset($_POST[''.$j.''])){
            
            return el_num_aufruf($j,$pro_seite);
        }
    }

In der Ausgabefunktion nutze ich die Tatsache, dass man dem SQL LIMIT einen Offset mitgeben kann. Dieser muss bei 0 (Null) auf der Seite 1 anfangen, auf Seite 2 ist er dann einmal die Anzahl der Beiträge pro Seite, auf Seite drei zweimal etc… deswegen die Konstruktion mit dem $akt_seite-1:

function el_num_aufruf($akt_seite, $aufderseite){
    
        
    global $wpdb;
        
        //Limit Offset eins weniger als aktuelle Seite
        $hilf = ($akt_seite-1)*$aufderseite;
        
        $alleposts = $wpdb->get_results( "SELECT * from wp_posts 
         where post_type like 'post' 
         and post_status like 'publish'
         LIMIT ".$hilf.", ".$aufderseite." ");
        $gefunden = $wpdb->num_rows;
        
        foreach ($alleposts as $einpost){
            
            $pfad = get_the_permalink($einpost->ID);
            echo "<a href = '".$pfad."'>".$einpost->post_title."</a><br>";
            
        }
    
} //end function el_num_aufruf

Am Ende gebe ich die Rezepte mit den Links wieder mit einem Foreach aus. Das wars!

15_pro_seite

15_pro_seite

Das funktioniert auch mit anderen Werten für die Ausgabe pro Seite, ich nehm mal 7:

7_pro_seite

7_pro_seite

Oder wesentlich mehr, 30, da sind wir komplett flexibel:

30_pro_seite

30_pro_seite

Man könnte jetzt natürlich noch ein Auswahlfeld für den Benutzer einbauen, so dass er selbst wählen kann wieviele Rezepte pro Seite er angezeigt haben möchte, aber damit kann sich jeder selber amüsieren. Ich werde eh die alfabetische Pagination nehmen, die taugt mir besser für meine Anwender.

WordPress Beiträge: alphabetische Pagination ganz spartanisch

Ich habe mich in einem kleinen PHP-Projekt kürzlich ausführlich mit seitenweiser und alfabetischer Pagination bei der Ausgabe von Datenbankabfragen beschäftigt, und bin dabei auf eine Idee gekommen, wie man das in WordPress relativ einfach umsetzen kann. Ich geh mal wieder auf meine Rezepte los, das sind über 300 Stück, da ist das alte Inhaltsverzeichnis doch inzwischen ein wenig lang.

Die Aufgabenstellung

Ich möchte eine Anzeige aller Buchstaben A-Z, und wenn man auf einen Buchstaben klickt, soll eine Liste aller Rezepte ausgegeben werden, die mit diesem Buchstaben anfangen. Klingt simpel, ist auch nicht arg schwierig unzusetzen. Ich mache ein Plugin daraus, und packe die ganze Sache in einen Shortcode.

Die Anzeige: ich nehme ein Formular

Dafür konstruiere ich mir ein Hilfs-Array, in das erstmal alle Buchstaben von a-z reinkommen. Groß/Kleinschreibung ist uninteressant, da MySQL-LIKE nicht case sensitive ist. Das Formular sieht erstmal so aus:

//Buchstaben a-z in Array schreiben
$letters = array();
for ($i = 'a', $j = 1; $j <= 26; $i++, $j++) {
    $letters[$j] = $i;    
}
//Formular mit Buttons
echo "<form action = '#' method = 'post'>";

for ($i=1; $i <=26; $i++){
    echo "<input type='submit' id='el_button' name='".$letters[$i]."' value='".$letters[$i]."'>";
}
    echo "</form>";

Ich steppe durch mein Buchstabenarray durch und lege für jeden Buchstaben einen Button mit dem Namen des aktuellen Buchstabens und der id el_button an. Das erzeugt 26 Buttons, die sehen erstmal noch recht häßlich aus:

buttons_ungestylt

buttons_ungestylt

Ein kleiner Eingriff in die style.css des Child Themes verschönert das Ganze beträchtlich.

#el_button{
    height:30px;
    width:18px;
    padding:2px;
    border: 2px solid white;
    margin 2px;
    padding: 1px 1px 1px;
}

Das Ergebnis kann sich schon besser sehen lassen:

buttons_gestylt

buttons_gestylt

Jetzt klemme ich mir wieder mein Buchstabenarray und steppe es wieder von 1 bis 26 durch. Für jeden Buchstaben frage ich mit einem if isset(…) ab, ob der entsprechende Button angeklickt wurde, und rufe falls ja meine Ausgabefunktion auf, die kriegt den aktuellen Buchstaben als Parameter übergeben.

for ($j = 1; $j <= 26; $j++){    
        if (isset($_POST[''.$letters[$j].''])){
            
            return el_aufruf("".$letters[$j]."");
        }
    }

Die Ausgabefunktion

… geht mit dem übergebenen Buchstaben auf die Tabelle wp_posts los und holt mir mit dem post_title LIKE ‚x%‘ alle veröffentlichten Rezepte, die mit diesem Buuchstaben anfangen. Ausgegeben wird die Sache wieder mal mit einem foreach, und ich hole mir gleich noch den Permalink und bastle einen Link zum Rezept daraus.

function el_aufruf($stabe){
    global $wpdb;
         $alleposts = $wpdb->get_results( "SELECT * from wp_posts 
         where post_title like '".$stabe."%' 
         and post_type like 'post' 
         and post_status like 'publish'
         order by post_title");
        $gefunden = $wpdb->num_rows;
        
        echo "<h2>".$gefunden." Rezepte zum Buchstaben ".strtoupper($stabe)."</h2>";
        
        foreach ($alleposts as $einpost){
            
            $pfad = get_the_permalink($einpost->ID);
            echo "<a href = '".$pfad."'>".$einpost->post_title."</a><br>";
            
        }
    
} //end function el_aufruf

Das wars schon! Hier als Beispiel die Ausgabe zum Buchstaben M:

buchstabe_m

buchstabe_m

Kleiner Aufwand, praktisch brauchbares Ergebnis. Wenn man unterschiedliche Post Types hat, könnte man die Ausgabe auch hierauf noch einschränken, das ist vielleicht mal ganz nützlich. Ich hab aber nur die einfachen Posts als Rezepte, das ist für meine Zwecke völlig ausreichend.

Shortcodes: echo vs. return

Ich arbeite ja gern mit Shortcodes und packe da auch oft komplexere Funktionalitäten rein, weil sie so praktisch sind. Wenn man allerdings Shortcodes in Beiträge einbinden will, die auch noch Text enthalten, muss man ein bisschen aufpassen mit den Ausgaben, wo man echo und wo man return verwendet, sonst kann es zu unerwünschten Effekten kommen. Ich mach mal ein einfaches Beispiel.

Simpler Shortcode mit return:

function el_harmlosertext(){
    return "<strong>Ich bin ein komplett harmloser ShortcodeText</strong>";
}
add_shortcode('harmlos','el_harmlosertext');

Den kann man im Fliesstext platzieren wo man will, er wird an der richtigen Stelle ausgeführt. Im Editor sieht das so aus:

editor

editor

Das erzeugt diese Bildschirmausgabe:

bildschirm

bildschirm

Ändert man jedoch den Shortcode und macht aus dem return ein echo, funktioniert das nicht mehr, die Ausgabe erfolgt vor dem Fliesstext des Artikels.

vor_fliesstext

vor_fliesstext

Das ist etwas irritierend, und komplett lästig wird es, wenn man zum Beispiel die Ergebnisse einer Datenbankabfrage ausgeben möchte, die nicht so ohne weiteres in ein return-Statement zu packen sind. Ich stricke mal eine kurze Liste aller bisher veröffentlichten Posts, und möchte diese am Ende meines Beitrags ausgeben. Der Shortcode sieht erstmal so aus:

function el_mit_datenbankabfrage(){
    
     global $wpdb;
            
      //Bisherige Anzahl posts holen
        $alleposts = $wpdb->get_results( "SELECT * from wp_posts where post_status like 'publish'");
        $anzahl = $wpdb->num_rows;
        echo $anzahl." Datensätze gefunden und mit echo ausgegeben<br>";
        $ausgabe = array();
        $i=0;
        foreach($alleposts as $einpost){
            echo $einpost->post_title."<br>";
            
        }
       
}
add_shortcode('db_abfrage','el_mit_datenbankabfrage');

Hier kann man das echo nicht einfach mit einem return ersetzen, weil sonst der foreach nach dem ersten Durchgang abbricht. So lassen kann man es aber auch nicht, weil die Liste so wie der Shortcode jetzt aussieht immer vor dem Fliesstext des Artikels ausgegeben wird:

echo_vor_text

echo_vor_text

Abhilfe: wir packen die Ausgaben des foreach in einen String und verketten den innerhalb der Schleife. Die Funktion des Shortcodes wird wie folgt geändert:

function el_mit_datenbankabfrage(){
    
     global $wpdb;
            
      //Bisherige Anzahl posts holen
        $alleposts = $wpdb->get_results( "SELECT * from wp_posts where post_status like 'publish'");
        $anzahl = $wpdb->num_rows;
        echo $anzahl." Datensätze gefunden und mit echo ausgegeben<br>";
        $ausgabe = "";
       
        foreach($alleposts as $einpost){
            $ausgabe=$ausgabe.$einpost->post_title."<br>";
            
        }
        
    return "Mit return ausgegeben:<br>".$ausgabe;
}

Diesen String gebe ich dann mit dem return der Shortcode-Funktion zurück, und meine Liste wird brav ans Ende des Beitrags gestellt, da wo ich sie auch haben will.

return

return

Ich könnte die Ausgabe auch mitten im Text platzieren, funktioniert auch. Man sieht hier auch noch, dass die mit echo ausgegebene Anzahl der Datensätze gnadenlos vor dem Beitragstext landet:

mitten_im_text

mitten_im_text

Das ist alles natürlich nur relevant, wenn man den Shortcode auf einer Seite oder auf einem Beitrag platzieren möchte, die sonst auch noch Text enthalten, bei sonst leeren Posts/Pages ist es egal. Wissen muss man halt, dass return und echo hier völlig unterschiedliche Effekte in der Ausgabe erzielen, dann kann man auch gegensteuern.

Rezeptecounter: so wird ein Widget draus

Wie man in WordPress ein eigenes Widget erstellt, habe ich in diesem Beitrag: Widget-Basteln macht Spaß schon mal ausführlich vorgestellt, deswegen machen wir das hier im Schnelldurchgang. Der Code läßt sich relativ leicht auf die Anforderungen für die Rezepthitparade anpassen. Wir brauchen zwei Eingabefelder, eines für den Titel des Widgets, und eins für die auszugebende Anzahl an Rezepten. In den OpCode des Widgets kommt unsere Ausgabelogik, die können wir 1:1 aus dem vorigen Beitrag übernehmen.

Nur kurz zur Erinnerung: Widgets werden von der Struktur her genau wie Plugins in einer eigenen PHP-Datei angelegt, die kommt auch ins Plugins-Verzeichnis. Hier mal der Code ohne die Ausgabefunktionalität:

<?php
/*
Plugin Name: Hitparaden Widget
Plugin URI: http://evileu.de/wordpress
Description: Gibt die am öftesten aufgerufenen Rezepte aus, Anzahl vom Benutzer wählbar
Author: Evi Leu
Version: 1.0
Author URI: http://evileu.de
*/


class HitparadenWidget extends WP_Widget
{
  function HitparadenWidget()
  {
    $widget_ops = array('classname' => 'HitparadenWidget', 'description' => 'Zeigt die am öftesten aufgerufenen Rezepte an' );
    $this->WP_Widget('HitparadenWidget', 'Hitparade', $widget_ops);
  }
 
  function form($instance)
  {
    $instance = wp_parse_args( (array) $instance, array( 'title' => '' ) );
    $title = $instance['title'];
        
    $instance = wp_parse_args( (array) $instance, array( 'anzahl' => '' ) );
    $anzahl = $instance['anzahl'];
    
    
?>
  <p><label for="<?php echo $this->get_field_id('title'); ?>">Titel: <input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo attribute_escape($title); ?>" /></label></p>
  
  <p><label for="<?php echo $this->get_field_id('anzahl'); ?>">Anzahl: <input id="<?php echo $this->get_field_id('anzahl'); ?>" 
name="<?php echo $this->get_field_name('anzahl'); ?>" type="number" value="<?php echo attribute_escape($anzahl); ?>" /></label></p>
  
  
<?php
  }
 
  function update($new_instance, $old_instance)
  {
    $instance = $old_instance;
    $instance['title'] = $new_instance['title'];
    $instance['anzahl'] = $new_instance['anzahl'];
    return $instance;
  }
 
  function widget($args, $instance)
  {
    extract($args, EXTR_SKIP);
 
    echo $before_widget;
    
    $title = empty($instance['title']) ? ' ' : apply_filters('widget_title', $instance['title']);
    $anzahl    = empty( $instance['anzahl'] ) ? '' : esc_attr( $instance['anzahl'] );
   
    // WIDGET CODE GOES HERE
    /**********************/    
    //END WIDGET CODE
    
    echo $after_widget;
  }
 
}
add_action( 'widgets_init', create_function('', 'return register_widget("HitparadenWidget");') );?>

Das ist das Gerippe für das Widget, es werden zwei Eingabefelder erzeugt und deren Werte auf die Variablen $title und $anzahl gelegt. Mit denen kann man weiterarbeiten, hier kommt der Code aus dem vorigen Beitrag nahezu unverändert rein:

// WIDGET CODE GOES HERE
    echo "<h2>".$title."</h2>";
    echo "<h3>Die ".$anzahl." beliebtesten Rezepte</h3>"; 
    
        //Bisher gespeicherte Rezepte holen
    global $wpdb;
        $alleposts = $wpdb->get_results( "SELECT * from wp_options where option_name like 'zaehler_%'");
        
        
        $rohdaten = array();
        $i=0;
        foreach($alleposts as $einpost){
            
            //Optionswerte auslesen            
            $meineOption = get_option($einpost->option_name);
            $o_zaehler = $meineOption['zaehler'];
            $o_titel = $meineOption['titel'];
            $o_id = $meineOption['id'];
            //debug unsortierte Ausgabe
            //echo $o_titel." ".$o_zaehler."<br>";
            
            //Array befüllen
            $rohdaten[$i]['zaehler']= $o_zaehler;
            $rohdaten[$i]['titel']= $o_titel;
            $rohdaten[$i]['id']= $o_id;
            $i=$i+1;
        }
        //echo "<h2>Hitparade:</h2>";
    
        $counter = array();
        foreach ($rohdaten as $key => $row)
        {
            $counter[$key] = $row['zaehler'];
        }
        array_multisort($counter, SORT_DESC, $rohdaten);


        $hilf = array_slice($rohdaten, 0, $anzahl);
        foreach($hilf as $roh){
                
                    echo $roh['titel']." (".$roh['zaehler'].")<br>";
                    
            }

    
    //END WIDGET CODE

Ich verwende nur noch den $title für die Überschrift des Widgets und die $anzahl für den array_slice, um die Anzahl der ausgegebenen Datensätze zu begrenzen. Das wars auch schon! Nach erfolgreicher Aktivierung präsentiert sich das Widget so:

hitparadenwidget

hitparadenwidget

Die Ausgabe sieht so aus:

hitparade_ausgabe

hitparade_ausgabe

Funkt, kann man so lassen.

 

Rezeptecounter revisited: für WordPress-Puristen

Ich arbeite ja gern mit eigenen Tabellen, aber die WordPress-Puristen schreien da immer gleich Zetermordio und wollen eine WordPress-konforme Lösung sehen. Deswegen stricke ich die Logik für das Wegschreiben des Rezeptezählers nochmal um und verstaue die relevanten Daten in der wp_options. Ich habe zu jedem Datensatz drei relevante Kennzahlen: die ID des Rezeptes, den Titel und den Zählerstand – wobei man den Titel auch nachträglich über die ID dazujoinen könnte, der ist hier eigentlich redundant. Diese drei packe ich in ein Array, und mit diesem Array füttere ich die Option. Das Ganze sieht dann so aus:

function el_rezeptcounter() {
 
 if(is_single()) {
 
 //id und titel abholen
 $akt_id = get_the_id();
 $akt_titel = get_the_title();
 
 //option nachschauen
 $meineOption = get_option('zaehler_'.$akt_id.'');
 $o_zaehler = $meineOption['zaehler'];
 
 if ($o_zaehler == ''){$zahl = 0;}else{$zahl = $o_zaehler;}
 
 echo "Dieses Rezept wurde bisher ".$zahl." mal aufgerufen";
 
 if($o_zaehler == ''){
 
 //neu eintragen
 $myOptions = array(
 'zaehler' => 1,
 'id' => $akt_id,
 'titel' => html_entity_decode($akt_titel)
 );
 
 update_option('zaehler_'.$akt_id, $myOptions);
 
 }else{
 
 //Zähler updaten
 $o_zaehler = $o_zaehler +1;
 
 $myOptions = array(
 'zaehler' => $o_zaehler,
 'id' => $akt_id,
 'titel' => html_entity_decode($akt_titel)
 );
 
 update_option('zaehler_'.$akt_id, $myOptions); 
 }
 
 } //End von if( is_single)
 
}

Genau genommen könnte man sich die if-else-Konstruktion sparen und in jedem Fall einen Update mit Zähler+1 machen, da WordPress mit update_option die Option neu anlegt, falls sie noch nicht existieren sollte.

WordPress macht aus den Arrays serialisierte Strings, da sieht dann der option_value zum Beispiel so aus:

a:3:{s:7:"zaehler";i:4;s:2:"id";i:1392;s:5:"titel";s:20:"Test für Backwerk";}

Das kann man Klartext lesen:

a für Array, Länge drei{s für string: Länge7:wert „zaehler“; i für integer:Wert 4…

… und so weiter. Um jetzt zum Beispiel an den Wert des Zählers heranzukommen, verwendet man folgende Syntax:

$meineOption = get_option('[name_der_option]');
 $zaehler = $meineOption['zaehler'];

Da $meineOption hier ein Array zurückgibt, könnte man auch über die Einträge iterieren und alle ausgeben, wenn man es braucht.

Jedenfalls haben wir jetzt unsere relevanten Daten WordPress-konform in der wp_options gespeichert. Wie aber kommen wir jetzt an die Auswertung? Mit einem SQL sind die serialisierten Werte nicht zu packen, da muss eine andere Lösung her. Na ja, pack’mas.

Auswertung des Rezeptecounters aus der wp_options

Dafür holt man sich mit einem Select.. WHERE option_name like ‚zaehler_%‘ alle relevanten Einträge aus der wp_options. Die Werte kann man schon mal mit einem foreach ausgeben:

function el_opcounter_hitparade(){
 
 //Bisher gespeicherte Rezepte holen
 global $wpdb;
 $alleposts = $wpdb->get_results( "SELECT * from iii_wpoptions where option_name like 'zaehler_%'");
 $anzahl = $wpdb->num_rows;
 echo "Anzahl gefunden= ".$anzahl."<br>";
 
 
 foreach($alleposts as $einpost){
 
 //Optionswerte auslesen 
 $meineOption = get_option($einpost->option_name);
 $o_zaehler = $meineOption['zaehler'];
 $o_titel = $meineOption['titel'];
 $o_id = $meineOption['id'];
 echo $o_id." ".$o_titel." ".$o_zaehler."<br>";
 }
 
}

Damit kriegt man schon mal die Liste.

unsortiert

unsortiert

Die hätte ich jetzt aber gern nach der Anzahl der Aufrufe absteigend sortiert, aber hier geht nix mit Order by, mit SQL ist das leider nicht zu packen. Das muss mit PHP, und das ist nicht ohne, dafür muss man einen multisort bemühen. Für den lesen wir unsere Daten in ein Array ein, das machen wir so:

Einlesen der Rohdaten in ein Array

Dafür kann unser Select nochmal herhalten:

//Bisher gespeicherte Rezepte holen
 global $wpdb;
 $alleposts = $wpdb->get_results( "SELECT * from iii_wpoptions where option_name like 'zaehler_%'");
 $anzahl = $wpdb->num_rows;
 echo "Anzahl gefunden= ".$anzahl."<br>";
 
 $rohdaten = array();
 $i=0;
 foreach($alleposts as $einpost){
 
 //Optionswerte auslesen 
 $meineOption = get_option($einpost->option_name);
 $o_zaehler = $meineOption['zaehler'];
 $o_titel = $meineOption['titel'];
 $o_id = $meineOption['id'];
 //debug unsortierte Ausgabe
 //echo $o_titel." ".$o_zaehler."<br>";
 
 //Array befüllen
 $rohdaten[$i]['zaehler']= $o_zaehler;
 $rohdaten[$i]['titel']= $o_titel;
 $rohdaten[$i]['id']= $o_id;
 $i=$i+1;
 }

Jetzt wirds ein bisschen tricky. Ich hab mir folgende Lösung ergooglet:

Array als Parameter für den multisort

Unsere Daten stecken jetzt in dem Array $rohdaten. Jetzt brauchen wir aber noch ein zweites Array, um den array_multisort (s. PHP-Handbuch) damit zu bedienen, und zwar muss dieses die Spalte enthalten, nach der sortiert werden soll. Das Ganze sieht dann so aus:

$counter = array();
foreach ($rohdaten as $key => $row)
{
 $counter[$key] = $row['zaehler'];
}
array_multisort($counter, SORT_DESC, $rohdaten);

Damit werden die Rohdaten nach dem Zähler sortiert, und zwar DESC also absteigend. Jetzt können wir die ganze Chose als Hitparade ausgeben:

foreach($rohdaten as $roh){
 
 echo $roh['titel']." (".$roh['zaehler'].")<br>";
 
 }

Das sieht jetzt schon ganz gut aus.

hitparade

hitparade

Natürlich kann man auch hier anhand der Rezept-IDs einen Link zum Rezept mit dem get_permalink einbauen, aber das spare ich mir jetzt. Wenn man nur die ersten X Rezepte ausgeben möchte, den SQL um ein LIMIT X ergänzen. Achtung, Denkfehler! Das klappt natürlich nicht, da der Select die Daten nicht vorsortiert ausgeben kann. Da muss man dann nur die ersten X Zeilen des Arrays ausgeben, sonst wird das nix. Ich pack das Ding doch noch in ein Widget mit Benutzereingabe für die X auszugebenden Rezepte! Kurze Lösung: man verwendet ein array_slice:

$hilf = array_slice($rohdaten, 0, 3);
foreach($hilf as $roh){
        
            echo $roh['titel']." (".$roh['zaehler'].")<br>";
            
            }

Ich finde den Aufwand zum Auswerten der serialisierten Daten schon recht kopflastig, das geht über die eigene Tabelle wesentlich komfortabler. Na ja, kann jeder selber entscheiden, welche Lösung er einsetzen möchte.

Noch eine Anmerkung am Schluss

Ich bin gefragt worden, warum ich den Code für das Wegschreiben des Rezeptezählers nicht in einen Filter gepackt habe, sondern als Shortcode in die single.php eingefügt habe. Das hat einen guten Grund: ich habe mit komplexeren Filtern bei WordPress schon schlechte Erfahrungen gemacht, insbesondre bei eingebauten Datenbankabfragen kann es da zu kuriosen Nebenwirkungen kommen. Jedenfalls ist der Shortcode-Aufruf in der single.php eine unkomplizierte Lösung und liefert korrekte Ergebnisse, damit kann ich ganz gut leben.