Archiv der Kategorie: Allgemein

WordPress als führendes System für das Mitgliederverzeichnis: ein Anfang

Erstmal eine Hypothese

Ich habs ja sonst nicht so mit theoretischen Konstrukten, ich bin eher praktisch veranlagt. Aber der Turnverein Weiß-Blau möchte seine Mitgliederstammdaten in Zukunft mit WordPress verwalten, also an die Arbeit. Ich gehe mal davon aus, daß der Import zumindest der Adressdaten mit Custom Fields geklappt hat und unser bestehender Mitgliederstamm jetzt sauber in den WordPress-eigenen Tabellen abgelegt ist.Notfalls mit einem kostenpflichtigen Plugin, das wars uns jetzt wert.

Erstmal sehen sie: nichts.

Kurz zur Erinnerung: für jedes Mitglied gibt es einen eigenen Beitrag. Im Beitragstitel steht die Mitgliedsnummer und der Name. Im Beitragsinhalt möchten wir die Adressdaten sehen. Wieso möchten? Die haben wir doch importiert? Ja aaaber – nach dem Import sehen wir im Beitrag jetzt erst einmal – nichts. Nur wenn man im Dashboard auf „Alle Beiträge“ geht und da bei einem Beitrag „Bearbeiten“ wählt und dann ein bißchen runterscrollt, dann sieht man die Benutzerdefinierten Felder mit ihren Inhalten.

fridolin_CF

fridolin_CF

Das ist unser Editor für Bestandsdaten. Wenn sich an den Adressdaten eines Mitglieds etwas ändern sollte, suchen wir uns den entsprechenden Beitrag und gehen in den Bearbeitungsmodus, hier können wir unsere Änderungen direkt einpflegen. Kann ich damit leben, die Benutzer brauchen halt eine kurze Einweisung in den Beitragseditor und sollten auch nicht vergessen auf „Aktualisieren“ zu klicken, dann funktioniert das schon.

Und wenn es neue Mitglieder einzutragen gibt?

Natürlich könnte man einfach in WordPress einen neuen Beitrag erstellen und die Daten manuell eingeben, aber das ist dann schon ein bisschen sperriger in der Benutzerführung. Man muß nämlich die relevanten Benutzerdefinierten Felder einzeln hinzufügen, und das ist aufwendig und fehlerträchtig. Ausserdem muss man sich genau überlegen, woher man die Mitgliedsnummer nimmt, unsere so wichtige eindeutige ID. Was gibt es für Alternativen?

Man könnte die neuen Benutzer in der alten Excel-Liste eintragen, die Mitgliedsnummern fortlaufend weiterführen und einen neuen Import fahren, aber was passiert dann mit unseren Bestandsdaten? Werden die „alten“ Mitgliederdaten dann überschrieben? Was passiert mit Änderungen, die ich in den Bestandsdaten eventuell bereits gemacht habe? Wäre dann die Excel-Datei unser führendes System, und nicht WordPress? Lauter Fragen, auf die es so ad hoc keine einfachen Antworten gibt.

Noch ’ne Hypothese

Viele CSV-Import-Plugins, die ich ausprobiert habe, bieten zumindest in der Pro Edition ein Feature, mit dem man auswählen kann ob bereits bestehende Datensätze upgedated werden sollen oder nicht. Ausprobieren konnte ich das nicht, aber es scheint mir die einzig praktikable Lösung zu sein, wenn man neue Mitglieder-Datensätze nicht einzeln manuell eingeben, sondern per neuem Import hinzufügen möchte. Das würde natürlich bedeuten daß wir von unserer alten Excel-Tabelle nicht wegkommen, aber sei’s drum. Gehen wir mal davon aus, dass das funktioniert mit dem selektiven Import, und kümmern wir uns mal um das nächstliegende Problem:

Die immer noch unsichtbaren Custom Fields

Man sieht die benutzerdefinierten Felder ja nach wie vor nur im Beitragseditor, und das soll sich jetzt schnellstens ändern. Eine sehr kurze und praktische Lösung ist ein Shortcode, den wir wie gewohnt in die functions.php unseres Child-Themes setzen. Der kann zum Beispiel so aussehen:

function ms(){
    
    $aktID=get_the_id();
    $strassehsnr= get_post_meta($aktID, 'strassehausnummer', true);
    echo $strassehsnr;
        
}
add_shortcode('meinshortcode','ms');

Ich habs ein bisschen ausführlicher als unbedingt nötig hingeschrieben, aber so läßt sichs besser erklären. Die Funktion ms() macht Folgendes:

  • get_the_id() holt die WordPress-ID des aktuellen Beitrags
  • get_post_meta() holt zur aktuellen Beitrags-ID den Wert des Custom Field namens „strassehausnummer“, das „true“ am Ende bewirkt, daß eine Einzelvariable ausgegeben wird und kein Array
  • echo gibt die ganze Chose aus.

Fertig! Shortcode in einen Beitrag einsetzen, Beitrag ansehen und da steht sie, die Strasse und Hausnummer. Die anderen Custom Fields holt man sich mit genau der gleichen Mechanik, man muß halt nur den get_post_meta() mit dem korrekten Namen des Feldes füttern. Also, damit kann ich jetzt echt leben!

Verwendung im Theme

Man kann die Funktion get_post_meta() natürlich auch in einem Template nutzen, sie z.B. an geeigneter Stelle in ein Child seiner single.php einbauen, aber das ist sehr Theme-spezifisch, da muss jeder selber rumdoktern, das versuche ich zu vermeiden wo es nur geht. In der Praxis wird man sich wahrscheinlich einen benutzerdefinierten Post Type anlegen und dort die entsprechenden Ergänzungen vornehmen, aber davon laß ich hier mal die Finger, das sieht wie gesagt in jedem Theme anders aus. Mir reicht mein funktionaler Shortcode, der ist universell einsetzbar.

Ja, aber was ist jetzt mit dem Anlegen neuer Mitglieder?

Machen wir das echt im alten Excel?
Gute Frage, und ich glaube, dazu brauchts einen neuen Beitrag.

Andere Importer, zentrale Datenhaltung und Custom Fields

Noch mehr Testberichte?

Nein, da muß ich euch enttäuschen. Ich hab zwar noch eine ganze Latte anderer Import-Plugins ausprobiert, eine Auswahl davon findet ihr hier bei winningWP, aber so richtig begeistert war ich von keinem davon. Die mit den meisten Features sind nahezu beliebig kompliziert in der Bedienung und erfordern einen erheblichen Einarbeitungsaufwand, und die einfacheren Genossen können allesamt keine Benutzerdefinierten Felder (Custom Fields). Zumindestenst nicht die Free Editions, die kostenpflichtigen Pro-Ausgaben sollens dann schon können. Aber die, wie ich schonmal gesagt hatte, kommen bei mir nicht zum Einsatz – mir fehlt das nötige Kleingeld, und mir gehts auch ums Prinzip. Open Source, sie wissen schon.

Warum mir die Custom Fields so wichtig sind

Da muss ich ein bißchen ausholen. Ich (und einige Legionen anderer WordPress-Entwickler da draussen) stehe immer wieder vor der Herausforderung, bestehende Unternehmensdaten in WordPress integrieren zu müssen. Darüber habe ich mich in diesem Artikel kürzlich ausführlicher ausgelassen, das will ich jetzt nicht alles wiederholen. Bleiben wir bei einem einfachen Beispiel:

Der Turnverein Weiß-Blau mit dem Mitgliederverzeichnis

Gehen wir davon aus, daß beim Kickoff unseres WordPress-Projekts bereits Mitgliederdaten vorhanden sind, das ist schließlich der Normalfall.  Gehen wir weiter davon aus, daß diese Daten als Excel-Liste geführt werden. Auch das ist eher der Normalfall, und da kommen wir relativ simpel an unser CSV ran. Wir klemmen uns ein Import-Plugin, erzeugen uns für jeden Mitgliederdatensatz einen Beitrag in WordPress, schubsen unsere Mitgliedsnummer, den Vornamen und den Nachnamen in die Titelzeile und den Rest (Adresse, Email, Telefonnummer und sportliche Präferenzen) in den Post Content, und fertig.

Fertig? Nein, jetzt gehts erst los!

Der Kunde ist erfreut, man kann sich die Liste der Mitglieder sowohl auf der Blogseite als auch im Dashboard anschauen, man kann suchen, z.B. nach Namen oder nach Ort, man kann (im Beitragseditor) die kompletten Mitgliederdaten anschauen und auch ändern, und – halt, Stopp. Da haben wir jetzt einen ganz wichtigen Knackpunkt erwischt. Was ist zu tun, wenn sich Mitgliederdaten ändern? Jemand hat eine neue Telefonnummer oder ist umgezogen und die ganze Adresse ändert sich, jemand interessiert sich jetzt auch noch für Aerobic und möchte das eingetragen haben, oder -Gott bewahre! – es kommen nach dem CSV-Import noch neue Mitglieder dazu… was ist dann zu tun?

Die schwierige Entscheidung: was ist das führende System?

Diese Frage stellt sich bei nahezu jedem IT-Projekt, und wenn man das nicht frühzeitig entscheidet, kommt man in Teufels Küche. Ich habe da selbst in Großprojekten, an denen ganze Mannschaften von Programmierern monatelang schufteten, schon die tollsten Pleiten erlebt, nur weil nicht von Anfang an entschieden wurde, was denn nun de Facto das führende System ist. Dabei ist es im Prinzip ganz einfach. Man muß festlegen, wo die Datenpflege passiert. In der Praxis heißt das meistens, wo die Inserts von neuen Datensätzen und die Updates von bereits existierenden Bestandsdaten erfolgen. Im Regelfall wird das eine Tabelle sein, oder bei komplexeren Systemen auch mehrere verknüpfte Tabellen. Wenn man es richtig macht, implementiert man eine Logik die festlegt was zu tun ist wenn z.B. ein neues Mitglied im Turnverein hinzukommt, und sorgt programmtechnisch dafür, daß der neue Mitgliederdatensatz ordentlich angelegt wird, mit eindeutiger Mitgliedsnummer (ID) und allem Pipapo. Ebenso bei der Änderung von Daten eines bereits vorhandenen Mitglieds, da wird man im Regelfall den zu ändernden Datensatz anhand der Mitgliedsnummer identifizieren und updaten. Alles klar soweit? Und dann?

Jetzt kommts: alle anderen Systeme referenzieren auf das führende System

Sinn und Zweck der ganzen Übung ist natürlich der: man möchte Datenänderungen nur zentral an einer Stelle im System vornehmen und es nicht an -zig verschiedenen Stellen (Huhu SAP!) eintragen müssen, wenn sich irgendwo zum Beispiel eine Telefonnummer oder eine Bankverbindung ändert. Wenn die zentralen Daten an anderer Stelle in der IT-Architektur des Unternehmens gebraucht werden (Beispiele kommen gleich) greift man auf die zentrale Datenhaltung zu und saugt sich die entsprechenden Daten aktuell ab. Im Idealfall sorgt der Entwickler sogar für eine dynamische Verknüpfung, so daß bei einer Änderung in den Stammdaten die abhängigen Systeme in Echtzeit mitkriegen, daß was passiert ist.

Ganz einfache Beispiele

Der Vorstand unseres Turnvereins möchte gerne wissen, wie viele unserer Turnvereinsmitglieder sich für Aerobic interessieren. Früher ist man da in die Excel-Liste gegangen, hat einen Filter auf die Spalte „Aerobic“ gesetzt und abgezählt bei wie vielen Mitgliedern da ein „ja“ drinstand. Ein bißchen EDV zu Fuß, aber es hat funktioniert.

Oder die Pressereferentin möchte alle weiblichen Mitglieder anschreiben und nachfragen, ob Interesse an einem Mutter-Kind-Training besteht – ja echt, sowas ist der Renner in unserem Stadtteilzentrum! Wieder kommt unsere Excel-Liste zum Einsatz, wir filtern nach Geschlecht und holen uns nur die Spalten mit den Namen und Adressdaten heraus, und die Mailing-Aktion kann starten.

Sie sehen, auf was ich hinauswill? Nennt sich zentrale Datenhaltung, oder neudeutsch „Data Warehousing“. In unserem Fall, dem ganz konkreten Mitgliederverzeichnis des Turnvereins, stellt sich die zentrale Frage:

Was ist unser führendes System: WordPress oder Excel?

Wenn wir unsere Mitgliederdaten per CSV-Import schon so schön in Wordpess reinjongliert haben, möchte unser Kunde mit an Sicherheit grenzender Wahrscheinlichkeit jetzt auch weiterhin mit WordPress arbeiten. Neue Mitglieder hinzufügen, Bestandsdaten ändern, kleine Datenauswertungen (siehe oben) für den Vorstand, die Buchhaltung und sonst noch etliche Anwender fahren, das hatten wir ja gerade. Gehts, oder gehts nicht? Das kommt ganz schwer darauf an, und jetzt kommen wir endlich zu den Custom Fields. Es geht nämlich erstmal nicht.

Text ist Text, da beißt die Maus keinen Faden ab

So wie die meisten CSV-Importer (zumindest die Free Editions) funktionieren, landen unsere Mitgliederdaten erstmal gesammelt im Post Content. Das ist ein Longtext. Da kann man die Daten zwar anschauen oder  editieren, aber wenn man sich jetzt z.B. alle Münchner Adressen rausfischen will, steht man schon am Ende der Fahnenstange. Ja nee, man könnte mit einem „post_content like %München%“ vielleicht die meisten Datensätze erwischen, aber sauber ist das nicht. Und wenn man jetzt alle Datensätze herausholen will, die bei Aerobic ein „ja“ drinstehen haben, ist endgültig Schluß, das geht so nicht.

Jetzt endlich: Custom Fields, ihr Einsatz!

Um bei dem Beispiel mit den Münchnern zu bleiben: wenn man den Ort aus der Exceltabelle in ein Custom Field namens „ort“ importieren könnte, könnte man später einen sauberen Select über „ort = ‚München'“ fahren. Wir erinnern uns nochmal kurz an die Logik der Custom Fields:

Ich habe z.B. ein neues Benutzerdefiniertes Feld „Preis“ mit dem Wert 12,80 angelegt. In der Tabelle wp_postmeta landet ein neuer Eintrag mit einer laufenden meta_id:

post_meta_custom_field

post_meta_custom_field

Hier wird über die post_id der Name (meta_key) und der Wert (meta_value) des benutzerdefinierten Feldes festgehalten.

Ich hätte also für unseren Ort den meta_key „ort“ und bei jedem Datensatz (erkenntlich an der post_id) entsprechend den meta_value „München“ oder eben einen anderen Ort. Da geht im Schnellschuss mal ein Join der wp_posts auf die wp_postmeta, das könnte etwa so aussehen:

Select * fom wp_posts left join wp_postmeta on wp_posts.ID = wp_postmeta.post_id 
where  wp_postmeta.meta_key = "ort" and wp_postmeta.meta_value = "München"

Damit hätte man zumindest mal sauber alle Münchner herausgefiltert. Auch wenn ich die WordPress-Puristen hier schon maulen höre: dafür gibts doch vordefinierte Funktionen, get_post_meta() zum Beispiel! Ja, ich weiß. Aber Hier gehts ums Prinzip, nämlich der programmtechnischen Auswertbarkeit von in WordPress enthaltenen Daten.

Die schlechte Nachricht: wie sieht der Select bei mehreren Custom Fields aus?

Da wirds ein bißchen unübersichtlich. Mit der eben beschriebenen Merthode müßte man tatsächlich für jedes weitere Custom Field einen weiteren Join hinzufügen, ich hab mir da mal ein Beispiel mit vier Feldern bei stackoverflow ausgeliehen:

SELECT wp_posts.ID, wp_posts.post_title, wp_posts.whatever,
            color.meta_value        AS color,
            transmission.meta_value AS transmission,
            model.meta_value        AS model,
            brand.meta_value        AS brand
       FROM wp_posts

  LEFT JOIN wp_postmeta  AS color 
         ON wp_posts.ID = color.post_id        AND color.meta_key='color'

  LEFT JOIN wp_postmeta  AS transmission
         ON wp_posts.ID = transmission.post_id AND transmission.meta_key='transmission'

  LEFT JOIN wp_postmeta  AS model
         ON wp_posts.ID = model.post_id        AND model.meta_key='model'

  LEFT JOIN wp_postmeta  AS  brand
         ON wp_posts.ID = brand.post_id        AND brand.meta_key='brand'

      WHERE wp_posts.post_status = 'publish'
        AND wp_posts.post_type = 'car'
   ORDER BY wp_posts.post_title

Beeindruckend, nicht wahr? Aber nicht wirklich schön. Der ganze Heckmeck ist nötig, weil die Tabelle wp_postmeta eine sogenannte Pivot-Struktur (Excel-Fexe kennen das) für die Custom Fields einsetzt, und dafür gibts in MySQL keine einfachere Auswertelogik. Hier ebenfalls bei Stackoverflow ist ein interessanter Beitrag mit einem anderen Lösungsansatz, aber das führt mir jetzt wirklich zu weit, und ausserdem ist dieser Beitrag schon lang genug. Ich hoffe, dass ich einigermassen klarmachen konnte, warum ich den Datenimport in Custom Fields für unbedingt notwendig halte, und werde mich im nächsten Beitrag darum kümmern, die Custom Fields auch angezeigt zu bekommen. Wollen doch mal sehen, wie sich WordPress als führendes System so anlässt.

Import der zweite Kandidat: WP Ultimate CSV Importer

Sie müssen diesen Artikel nicht lesen….

… denn ich habe den Ultimate CSV Importer nicht zum Laufen gekriegt. Aber vielleicht hat ja jemand anderer mehr Glück, Zumindest sah das mit den Benutzerdefinierten Feldern auf den ersten Blick gut aus.

Wenigstens Custom Fields?

Auf der Plugin-Homepage des Ultimate CSV Importer steht ziemlich weit oben, daß dieses Tool auch Custom Fields importieren kann. Na, dann wollen wir mal. Unsere Ausgangsdatei bleibt dieselbe, eine Liste von 10 Adressen mit einer eindeutigen Mitgliedsnummer, im CSV Format. Nach der Installation hat man auch hier ein eigenes Menü im Dashboard, wir rufen mal gleich den Unterpunkt Import/Aktualisierung auf. Als allererstes fällt auf: die Übersetzung ist abenteuerlich! Kleiner Appetithappen gefällig?

Wenn Sie lieben WP Ultimate CSV Importer zeigen uns Sie interessieren sich mit einem 5-Sterne-Überprüfung auf Datenschutzerklärung |

Ist das nicht super? Aber lassen wir mal die Scherzchen und kümmern uns um unser CSV. Wir laden es vom Desktop hoch, belassen die Default-Option „Import each record as posts“ und schauen uns das Ganze mal an.

Der Advanced Mode

Der kommt als erstes hoch, deswegen schauen wir ihn uns auch zuerst an. Es gibt einen Mapping-Bereich, in dem die zur Verfügung stehenden WordPress-Felder aufgelistet werden. Bei jedem Feld gibt es ein Dropdown, in dem man auswählen kann, welches Feld aus der CSV-Datei in welches WordPress-Feld importiert werden soll.

ultimate_dropdown

ultimate_dropdown

Hier ist anscheinend nur eine 1:1-Zuordnung möglich. Ausserdem ist das ehrlich gesagt nicht das was ich brauche. Ich könnte hier zwar, sagen wir mal: die Mitgliedsnummer in die ID, den Nachnamen in den Post Content und den Vornamen in den Post Excerpt setzen, aber  das ist jetzt echt nicht zielführend. Mal sehen wie es weiter unten aussieht:  gut siehts aus! Hier kann man sogar Custom Fields hinzufügen, und das machen wir mal schnell, gleich mit der Zuordnung der Felder aus der CSV-Datei.

ultimate_custom_fields

ultimate_custom_fields

Das sieht doch sehr brauchbar aus, oder? Aber ach, ich hab mich zu früh gefreut.

Bug oder Feature?

Ich habe den Ultimate CSV Importer trotz mehrfacher Versuche nicht dazu bringen können, mehr als einen Datensatz mit Custom Fields zu importieren. Er bleibt bei Record 1 von 10 stehen, die Uhr läuft weiter, auch nach mehr als 10 Minuten Wartezeit ist nichts mehr passiert. Wenn man die Aktion dann abbricht, ist zwar tatsächlich 1 Beitrag mit den gewünschten benutzerdefinierten Feldern angelegt worden, das sieht dann im WordPress Editorfenster korrekt so aus:

ultimate_ein_beitrag

ultimate_ein_beitrag

Aber wo sind meine restlichen Datensätze? Nicht da, obwohl ich es bestimmt fünfmal mit verschiedenen Einstellungen probiert habe.

Na gut, probieren wir halt den Drag&Drop-Mode

Der Drag&Drop-Mode sieht dem vom WP All Import sehr ähnlich und ist auch genauso zu bedienen, das geht recht intuitiv. Aber was soll ich sagen: kein anderes Ergebnis. Der Import bleibt beim ersten Datensatz stehen. Hab ich mich halt auf die Homepage des Plugins begeben und in den FAQ und im Supportforum nachgebuddelt, hab einige Ratschläge zum Speichern der CSV-Datei mit und ohne BOM ausgetestet und noch so allerhand probiert. Aber es hat nichts geholfen, ich krieg den Import nicht zum Laufen. Nach etwa zwei Stunden Recherche hab ichs dann bleiben lassen. Falls der „Hänger“ wirklich an einem für den Ultimate nicht geeigneten CSV-File liegt, na, da kann ich nur sagen: ich habs ganz normal aus Excel raus gespeichert, und das sollte ja nun wirklich ein Standardformat sein. Wenn der Ultimate CSV Importer damit nicht klarkommt, brauche ich ein anderes Plugin.

 

Datenschubserei für Fortgeschrittene: wir nehmen CSV

 Ausgangsbasis: Fehlanzeige

Vielleicht hat man ja schon mal ganz hoffnungsvoll reingeguckt, was sich im Dashboard unter Werkzeuge/Daten importieren oder exportieren verbirgt, aber da kommen wir leider nicht  weiter. Hier kann man nur WordPress-eigene XML-Dateien erzeugen oder hochladen, mit denen sich Beiträge und Seiten aus einem Blog herausexportieren und in einen anderen Blog wieder reinladen lassen. Wir brauchen ganz was anderes, und da wollen wir uns mal umsehen, was der Plugin-Markt so hergibt. Aber zuerst einmal ein paar Sätze zum Datenformat.

Unser Format der Wahl: CSV

CSV oder „Comma separated values“ ist das wohl verbreitetste Transportmittel für tabellarische Daten jeder Art. Es enthält normalerweise eine Kopfzeile, in der die Feldnamen stehen, und dann in jeder weiteren Zeile jeweils einen Datensatz.  Die meisten Anwender kennen die Möglichkeit, Excel-Tabellen als CSV zu speichern, und solche Listen werden wir in den meisten Fällen verwenden.  Ich zeig hier mal nur ein ganz einfaches Beispiel:

id;vorname;nachname
1;Fridolin;Hackmann
2; Marius ;Bishop
3;Yvonne;Green
4; Lina ;Ward
5; Tara; Sell
6; Antonia ; David 
7; Lorena ; Peter 
8; Franz ;Pfaff
9; Maja ; Bartels 
10; Laurin ; Selle

Das sind einfach zehn Datensätze, die Feldnamen kann man in der ersten Zeile lesen: id, vorname, nachname. Diese erste Zeile wird auch „Header“ genannt und wird uns bei unseren Importbemühungen noch öfter begegnen. Sie legt fest, in welcher Reihenfolge und mit welcher Benennung die einzelnen Datensätze zu lesen sind. Ob dahinter nun eine Liste mit 10 oder 10.000 Datensätze steht kann uns völlig wurst sein, Hauptsache die Struktur stimmt.

CSV geht (fast) immer

Ich habe schon viele Stunden meiner IT-Laufbahn damit verbracht, den unterschiedlichsten Systemen CSV-Dateien zu entlocken, und die gute Nachricht ist: meistens gehts schon irgendwie. Ob das nun DATEV, SAP, Lexware oder WISO meinBüro ist, oder eine MySQL- oder Oracle- oder sonst eine Datenbank, in den meisten Fällen verbirgt sich irgendwo eine Exportfunktion für CSV, man muß oft nur ein bißchen suchen. Manchmal muß man noch ein bißchen mit den Optionen experimentieren, ein beliebtes Spiel ist dass Umlaute und Sonderzeichen (ÄÖÜ%$§…) erstmal nicht so codiert sind wie man sie braucht, aber für die alten Datenbankfexe da draussen erzähle ich hier nichts Neues. Im schlimmsten Fall muss man da mit dem Notepad++ und etlichen Suchen&Ersetzen-Läufen nachkorrigieren, aber meistens hält sich der Aufwand in Grenzen. Es ist im Normalfall gar nicht so schwierig, eine funktionale CSV-Datei aus dem vorhandenen Datenbestand zu erhalten.

Was ist mit XML?

Das lassen wir mal aussen vor. Im Otto-Normal-Büro wird man in den seltensten Fällen Bestandsdaten als XML antreffen, auch wenn heutzutage nahezu jede Datenbank auch XML-Export und Import beherrscht. Wir halten uns an die schlichten CSV-Listen, das ist für unsere Anwendungen völlig ausreichend.

Wie gehts jetzt weiter mit dem CSV?

Da läßt sich leider keine allgemeingültige Anleitung formulieren, hier muß man schon sehr spezifisch einsteigen, was denn nun mit den Daten passieren soll. Ich werde ab jetzt mit konkreten Beispielen arbeiten, damit die Kuh vom Eis kommt. Erinnern sie sich noch an den Turnverein Weiß-Blau? Da hatten wir mithilfe einer eigenen Tabellen ein Mitgliederverzeichnis erstellt. Dafür haben wir für jedes Mitglied einen eigenen Beitrag erstellt und mit der Mitglieds-ID auf die eigene Tabelle mit allen relevanten Daten verknüpft. Jetzt gehen wir mal davon aus, daß eine umfangreiche Mitgliederliste mit Adressen und allem drum und dran schon existiert. Wollen mal sehen, ob wir die in unser WordPress importiert kriegen, aber dazu gibt es einen neuen Beitrag.

Wahrscheinlich guckt wieder kein Schwein: Scheduled events

Was ist ein Scheduled Event?

Das ist im Prinzip so etwas wie ein Cron-Job, und hier höre ich alle alten Programmierer „Aha!“ rufen. Jeder kennt die Möglichkeit, in Unix/Linux zeitlich wiederkehrende Aufgaben per Cron-Job zu steuern. WordPress bietet mit den Scheduled events eine ähnliche zeitabhängige Funktionssteuerung, und das ist prinzipiell eine schicke Sache, aber es gilt ein paar Eigenheiten zu beachten. Man kann schon festlegen, zu welcher Uhrzeit eine Funktion ausgeführt werden soll, ob sie täglich oder zweimal am Tag oder wöchentlich ausgeführt werden soll und all sowas, aaaber…

Wahrscheinlich guckt wieder kein Schwein

Ein WordPress Scheduled event wird erst ausgeführt, wenn die Blogseite auch aufgerufen wird. Dann guckt WordPress nach, ob es Events gibt, deren festgelegte Uhrzeit bereits abgelaufen ist, und erst jetzt wird die geplante Aktion gegebenfalls ausgeführt. Heißt im Klartext: wenn kein Besucher auf die Webseite kommt, wird auch kein Event ausgeführt. Nein, ich veräppel euch nicht, das ist so. Schlimmstenfalls wird der Event erst genau dann ausgeführt, wenn man selber mal auf seine Webseite geht um zu gucken ob schon was passiert ist. Das ist natürlich datenverarbeitungstechnisch Bockmist, weil es dem reinen Zufall überlassen ist, wann die gewünschte Funktion nun tatsächlich ausgeführt wird.

Und, ist das so schlimm?

Das kommt darauf an. Wenn ich über einen Scheduled Event einen größeren Datenbankabgleich mit richtig vielen Datensätzen fahren will, hätte ich schon gern dass der wie geplant Nachts um 2:00 über die Bühne geht und nicht erst Morgens wenn der erste Besucher auf meiner Webseite hereinschneit. Wenn ich zu bestimmten Zeiten E-Mails mit Informationen über „Neues zum Tage“ an meine Abonennten verschicken möchte, sollen die schon zu einer bestimmten Tageszeit abgesetzt werden und nicht erst Stunden später. Das mal nur so als Beispiele.

Wir wollen mal nicht so kleinlich sein, in den meisten Fällen wird es nicht so gravierend sein, wenn eine geplante Funktion statt um 0:00 erst um 6:27 beim ersten Besucher der Webseite getriggert wird. Man muss es halt wissen, und sich genau überlegen ob es im Einzelfall etwas ausmacht, daß ein geplanter Event erst „irgendwann“ stattfinden wird, und nicht genau zur geplanten Zeit.

Wie plane ich einen Scheduled Event?

Dafür verweise ich auf diesen ausgezeichneten Artikel bei sitepoint, da steht alles in komprimierter, bestens verständlicher Form drin. Ich renne hier nur mal im Schnellgang durch.

Zuerst definieren wir die Funktion, die ausgeführt werden soll,

function mein_event(){  // Mach irgendwas.}

Wir verknüpfen die Funktion mit einem neuen Action hook:

add_action('mein_action_hook','mein_event');

Schließlich setzen wir die Parameter für den Event, dabei nutzen wir die PHP-Funktion time(), die den UNIX-Timestamp für „jetzt“ zurückgibt:

wp_schedule_event(time(), 'hourly', 'mein_action_hook');

Das alles kommt in die functions.php unseres Child-Themes, und sollte dafür sorgen, dass unsere Funktion ab sofort stündlich ausgeführt wird… jedenfalls so ungefähr, wann halt der nächste Besucher hereinschneit. Also ehrlich, mir sträuben sich da sämtliche Nackenhaare, aber anders kann es WordPress halt nicht. Da dürfen sich die Entwickler aber schon nochmal was einfallen lassen!

Workaround und Helferlein

Ja, ich habs gehört, die alten Programmierer habens alle schon eingeworfen: dann basteln wir uns halt einen Cron-Job, der unsere WordPress-Seite automatisch aufruft, sagen wir mal eine Minute nachdem der Scheduled Event fällig war. Wie das geht steht z.B. hier bei Sebastian Widmann , aber das führt mir jetzt endgültig zu weit, das kann sich jeder der möchte selber zusammenpfriemeln.

Wenn man unbedingt mit Wordpess Scheduled events arbeiten will, kommt man an einem sehr ausgefeilten und nützlichen Plugin kaum vorbei:
WP Crontrol bietet eine komfortable Benutzeroberfläche zur Steuerung von Events und Schedules und macht einem das Leben hier deutlich leichter. Ändert zwar nix an der grundlegenden Problematik von „wahrscheinlich guckt wieder kein Schwein“, aber ist zumindest sehr einleuchtend zu bedienen und hilft einem bei mehreren Scheduled Events wirklich, den Überblick zu bewahren.

Erstellen wir jetzt endlich mal ein eigenes Theme?

Nein.

Und das ist mein letztes Wort, ehrlich. Themes gibt es wie Sand am Meer, da ist echt für jeden Geschmack, Zweck und Funktionsumfang was dabei. Themes sind ein Konzept, das auch vom Enduser (im Zweifelsfall meinem Kunden) leicht begriffen wird, nach Themes kann jeder selber suchen und sich aus den vielen bunten Bildchen das heraussuchen, was ihm am besten gefällt. Und glauben sie mir: es wird ihm erst das eine Theme gefallen, und dann das Nächste und dann noch das Übernächste. Was bei einem Themewechsel auf sie zukommt, da haben sich andere Leute schon schlaue Gedanken gemacht, siehe z.B. dieser Artikel vom elbnetz. Bestenfalls passen nur ein paar Bildformate nicht mehr, schlimmstenfalls zerhackt es ihnen die ganzen Seitenlayouts und die ganzen schönen kundenspezifischen Funktionen.

Bedenken sie immer: als Entwickler kommen sie bei einem Themewechsel in Teufels Küche, wenn sie abhängig von einem bestimmten Theme programmiert haben, und der Kunde möchte jetzt aber unbedingt ein anderes.

Ich editiere Page Templates nur im äußersten Notfall

Weil ich aus langjähriger leidgeprüfter Praxis genau weiß, dass sich der Kunde früher oder später für ein anderes Theme entscheiden wird, greife ich wenn möglich gar nicht in die Page Templates ein. Die Arbeit ist bei einem Themewechsel grad für die Katz‘, und es ist auch nicht gesagt daß ihre Anpassungen in einem anderem Theme auch genauso funktionieren werden, dazu sind die Templates einfach zu uneinheitlich konstruiert.

Aber was ist, wenn man ein Child Theme verwendet?

Das selbe in Grün, ich setze hier keine modifizierten Page Templates ein, wenn es nicht unbedingt sein muss. Ich verwende (ausser bei ganz, ganz einfachen Blogs) immer ein Child Theme. In den seltenen Fällen, wo ich es nicht von Anfang an eingerichtet habe, kam immer früher oder später der Zeitpunkt wo ich doch eins gebraucht habe.

Die einzigen Dateien, an denen ich Änderungen vornehme, sind üblicherweise die functions.php (ganz wichtig für unsere Shortcodes) und seltener noch die style.css des Child Themes. Und das wird dann (verdammtnochmal!) ordentlich und ausführlich dokumentiert, damit man die Funktionalität im Falle des Falles ohne Beinbruch in ein neues Theme übertragen kann.

Ausnahmen bestätigen die Regel

Und falls doch mal eine Änderung an einem Template fällig ist: immer im Child Theme, und immer gut dokumentieren. Ich habe zum Beispiel auf der Seite des Turnvereins Weiß-Blau die Beitragsseiten als Mitgliederverzeichnis umgestaltet, da war ein Eingriff in die single.php notwendig, um die Daten aus der eigenen Tabelle mit den Mitglieder-Stammdaten anzeigen zu lassen. Aber ich wette mit mir selber, dass ich das auch mit einem Shortcode lösen könnte – da mach ich mir bei Gelegenheit mal mehr Gedanken drüber.

Auch wenn man bestimmte Teile des Themes loswerden möchte (keine Sidebar, kein Footer etc…) kommt man um eine Änderung der entsprechenden Templates kaum herum, aber auch hier gilt: immer im Child Theme, und immer gut aufpassen was man da wirklich macht. Die Themes sind ja, wie bereits gesagt, so uneinheitlich konstruiert (und so lausig dokumentiert, das muss auch mal gesagt werden!), dass sich hier kaum allgemeingültige Regeln aufstellen lassen, welche Änderung welchen Effekt bewirkt. Da ist immer viel Trial and Error dabei.

Änderungen in der style.css nur mikrochirurgisch

Natürlich kann man sich hier spielen und die Layouts für nahezu alle WordPress-Elemente nach eigenem Gusto verändern. Aber ich bin Fachinformatikerin, keine Webdesignerin, und überlasse das passende Styling denen, die’s gelernt haben, und das sind im Zweifelsfall nun mal die Theme-Designer. Ich greife mal ein, wenn z.B. die Umrandungen von Formularfeldern unsichtbar sind (beliebtes Manko), oder die Beschriftung von Buttons unleserlich ist. Aber das wars dann schon. Alles andere lasse ich wie es ist, die Leute vom Design haben ja schließlich grosse Mühe darauf verwendet, dass alles zusammen ein stimmiges Bild ergibt, und ich als Design-Laie kann hier bestenfalls verschlimmbessern.

Theme-unabhängig programmieren, das geht!

Sogar sehr schmerzlos. Mit Shortcodes und Plugins, Filtern und Action Hooks läßt sich fast alles  erreichen, was der Kunde anfordert, und hier kann man wirklich Theme-unabhängig arbeiten, wenn man ein bisschen aufpasst.

Grosse Styling-Änderungen reden wir unserem Kunden einfach aus, da suchen wir ihm lieber ein anderes Theme. Wir sind schließlich (ich sags nochmal) alte Programmierer und keine Designer.

WordPress möchte doch so gern ein CMS sein

Da fehlts zwar noch ein paar Meter, aber das gute alte WordPress nimmt ja für sich in Anspruch, ein vollwertiges CMS werden zu wollen. Und da ist die Trennung von Funktion und Design unverzichtbar! Will heissen, eigentlich sollte man die Themes wechseln können wie die frischen Socken, ohne dass an den Inhalten überhaupt Nacharbeiten anfallen.

Davon sind wir zwar im Moment noch einiges entfernt, aber mit der Multisite-Technologie gehts schon in die richtige Richtung, besonders wenn man geeignete Plugins zum Beispiel zum Clonen von ganzen Blogseiten einsetzt. Damit kommen zwar auch wieder neue Herausforderungen – wie schreibe ich ein Multisite-fähiges Plugin? – aber die Richtung stimmt wie gesagt schon. Ich beobachte diese Entwicklung mit grossem Interesse, und werde hier sicher noch den einen oder anderen Beitrag zum Thema CMS reinstellen. Aber ein eigenes Theme – no way Josè, das wirds hier bei mir nie geben.

Unser erstes Plugin: SetzeCode 2, die Funktionalität

Was wollten wir also tun? Den Wert einer Option per Plugin setzen. Dafür brauchen wir zuerstmal:

Ein kleines Formular für die Eingabe

//Begin Formular
echo ‚<form method=“post“>‘;
echo ‚Mein Code: <input type=“text“ name=“code“ /></br>‘;
echo ‚<input type=“submit“ name = „senden“ value=“Abschicken“/>‘.“<br>“;
echo „</form>“;
// End Formular

Keine Hexerei, nur ein ganz normales PHP-Formulärchen mit einem Texteingabefeld und einem submit-Button.

Was soll passieren, wenn auf „Abschicken“ gedrückt wurde?

Auch nicht weiter schwer, die Option soll mit dem Inhalt der Benutzereingabe gesetzt werden.

if (isset($_POST[’senden‘]))
{
    $akt_code = $_POST[‚code‘];
    update_option (‚mein_code‘, $akt_code);   
}

Und schon landet der vom Benutzer eingegebene Code als Options-Wertepaar (Name und Wert) in der Tabelle wp_options!

Man kann noch eine kleine Debug-Ausgabe mit dranhängen, dann sieht man auch gleich mal wie das mit dem get_option() funktioniert:

$akt_option = get_option (‚mein_code‘);
        echo „Wert aus der Datenbank &nbsp:“.$akt_option;

Alles klar? Bitteschön, unser erstes Plugin kann marschieren! Nochmal zur Kontrolle die gesamte Funktion:

function pluginAdminScreen() {
    echo „<h1>Setze Meinen Code</h1>“;
            
//Begin Formular
echo ‚<form method=“post“>‘;
echo ‚Mein Code: <input type=“text“ name=“code“ /></br>‘;
echo ‚<input type=“submit“ name = „senden“ value=“Abschicken“/>‘.“<br>“;
echo „</form>“;
// End Formular

if (isset($_POST[’senden‘]))
{

    $akt_code = $_POST[‚code‘];
    update_option (‚mein_code‘, $akt_code);
    
}
$akt_option = get_option (‚mein_code‘);
        echo „Wert aus der Datenbank &nbsp:“.$akt_option;      
}

Hochladen und Aktivieren unseres Plugins

Man verschiebt jetzt die fertige setze_code.php in unser vorher angelegtes Plugin-Unterverzeichnis. Wenn alles geklappt hat, taucht unser neues Plugin jetzt im Plugins-Menü auf und kann dort aktiviert werden. Dann erscheint am Ende des Admin-Menüs der neue Eintrag „SetzeCode“, und den rufen wir jetzt auf und probierens mal aus. Aussehen sollte das Ganze etwa so:

screenshot_setze_code

screenshot_setze_code

Der Wert aus der Datenbank wird beim ersten Aufruf leer sein, weil die Option noch nicht gesetzt wurde, aber nach dem ersten Abschicken sollte er treu und brav den zuletzt eingegebenen Code wiedergeben. Das wars!

 

 

Options: benutzerdefinierte globale Variable

Wozu globale Variable?

Manchmal braucht man Werte, die global in WordPress zur Verfügung stehen sollen. Man kann natürlich Shortcodes definieren, die nur einen bestimmten Wert zurückgeben (return „ABCD“;), aber mit dem Shortcode ist man logischerweise abhängig vom Theme, wenn man das Theme wechselt ist auch der Shortcode und der Wert futsch. WordPress bietet da alternativ eine hübsch simple Möglichkeit, eigene Werte in der Datenbank abzulegen und auch wieder herauszuholen.

Die WordPress Options

Kurzer Blick in den Codex: add_option() legt ein Wertepaar (Option Name und Wert) in der WordPress-Tabelle wp_options ab. Mit get_option(‚optionsname‘) holt man sich den Wert bei Bedarf wieder heraus. Ich zitiere nochmal kurz den Codex:

If you are trying to ensure that a given option is created, use update_option() instead, which bypasses the option name check and updates the option with the desired value whether or not it exists.

Wir nehmen also lieber die Funktion update_option(), die uns auf jeden Fall den Datenbankeintrag anlegt, falls er noch nicht existiert. Das sieht dann etwa so aus:

update_option (‚mein_code‘, ‚Evis neuer Code‘);

Das ‚mein_code‚ ist der Name der Option, diese wird durch das update_option() neu angelegt, falls sie noch nicht existiert. Der zweite Parameter setzt den Wert (value) der neuen Option, hier habe ich einfach einen String eingesetzt. Das Feld option_value ist übrigens ein longtext, da können sie bis zu 2^32 Bytes speichern, also nur keine Hemmungen!

Wie sieht das in der Datenbank aus?

Braucht uns eigentlich nicht zu kümmern, da wir ja den Wert der Option nachher mit get_option() herausholen, aber ich zeigs mal der Vollständigkeit halber:

options

options

Die neue Option kriegt eine AutoIncrement-ID, in option_name landet der angegebene Name, in option_value der gewünschte Wert der Option, und das autoload wird defaultmäßig auf yes gesetzt und braucht uns nicht weiter zu kümmern.

Wie man den Wert der Option wieder herauskriegt

Ganz simpel. Mit get_option($option_name). Der Code für die Ausgabe sieht zum Beispiel so aus:

$akt_option = get_option (‚mein_code‘);
echo „Wert von akt_option :&nbsp“.$akt_option;

Das erzeugt die Ausgabe:

Wert von akt_option :  Evis neuer Code

Eigentlich selbsterklärend.

Und wohin jetzt mit den update- und get- Anweisungen? Dafür (täterätää!) schreiben wir unser erstes Plugin, und dazu gibts natürlich einen neuen Beitrag.

Noch’n Nachschlag: Formular mit Gedächtnis (reines PHP)

Formular soll sich Eingaben merken

Kennen sie das auch: man hat ein Online-Formular ausgefüllt und auf den „Abschicken“-Button geklickt, und alle Benutzereingaben verschwinden, man hat wieder ein leeres Formular vor sich. Passiert heutzutage gottseidank nicht mehr so oft, aber wenn dann ist es oft verdammt ärgerlich, wenn man nicht das gewünschte Ergebnis erzielt hat und das Formular nochmal von vorn ausfüllen muß. Unser Freizeitsportpartner-Suchformular vom Turnverein Weiß-Blau leidet noch unter dieser Kinderkrankheit, und die wollen wir ihm abgewöhnen. Kurz zur Erinnerung, das Formular sieht so aus:

suchformular

suchformular

Selektive Vorbelegung

Jetzt möchte ich gerne, daß nach dem Klick auf „Abschicken“ die vom Benutzer gesetzten Häkchen in den Checkboxen erhalten bleiben. Bislang sah der Code für eine Checkbox so aus:

echo „<input type=’checkbox‘ name=’turnen‘ value=’ja‘>Turnen<br>“;

Den brechen wir jetzt ein bißchen auf, und wir fragen ab ob denn der Wert für die Checkbox nicht schon mal gesetzt wurde, das sieht dann so aus:

echo „<input type=’checkbox‘ name=’turnen‘ value=’ja'“;
            if ($_POST[‚turnen‘] == ‚ja‘){
            echo „checked=’checked'“;
            }
        echo „>Turnen<br>“;

Das $_POST[‚turnen‘] enthält den aktuellen Wert des Feldes, der ist leer, wenn die Checkbox vor dem Drücken des Abschicken-Buttons leer war, und er enthält ein „ja“, wenn die Checkbox angekreuzt war. Das checken wir mit dem IF ab, und mit dem „checked=’checked'“ setzen wir im ja-Fall das Häckchen.

That’s it! Das machen wir für alle anderen Checkboxen analog, und schon haben wir ein Formular mit Gedächtnis.

Dropdownfeld mit Gedächtnis

Für die, die’s ganz perfekt haben wollen, hier noch die Vorbelegung für das Dropdown-Feld:

/*Begin select*/
echo „Auswahl Fitneß: &nbsp“;
echo „<select name=’meine_auswahl‘>“;

echo „<option“;
if ($_POST[‚meine_auswahl‘] == ‚egal‘){
    echo “ selected“;
}
echo „>egal</option>“;

echo „<option“;
if ($_POST[‚meine_auswahl‘] == ‚Anfänger‘){
    echo “ selected“;
}
echo „>Anfänger</option>“;

echo „<option“;
if ($_POST[‚meine_auswahl‘] == ‚Freizeitsportler‘){
    echo “ selected“;
}
echo „>Freizeitsportler</option>“;

echo „<option“;
if ($_POST[‚meine_auswahl‘] == ‚Profi‘){
    echo “ selected“;
}
echo „>Profi</option></select><br>“;
/*End Select*/

Das können sie sich jetzt aber selber zusammenreimen, der Mechanismus ist genau so wie bei der Checkbox, nur halt für jede Option extra. Zugegeben, es ist ein bißchen Aufwand, aber in Sachen Benutzerfreundlichkeit der Mühe wert.

Nachschlag: Kategorien schöner ausgeben leicht gemacht

Kategorienliste

Erinnern sie sich, bei der Rezeptausgabe hatte ich das Problem, daß ich hinter jeder Kategorie in der Liste ein Komma hatte, auch hinter der letzten, und das war nicht so schön.

alleposts_mit_kategorien

alleposts_mit_kategorien

Jetzt bin ich gerdae über eine WordPress-Funktion gestolpert, die das wesentlich hübscher macht, und einen gleich mit den Links zu den Kategorien mit versorgt. Kurzer Blick in den Codex: get_the_category_list

Die Funktion gibt die Kategorienliste zu einer beliebigen Beitrags-ID aus, das sieht z.B. so aus:

$ausgabe =  get_the_category_list( ‚,‘,“,1270);
echo „Kategorien: &nbsp“.$ausgabe;

Ergebnis (mit funktionalen Links):

Kategorien:  Echt Bairisch,Plauderei,Single-Küche

Man kann als Seperator übrigens beliebige Strings setzen, ich nehm mal ein “ + „:

Kategorien:  Echt Bairisch + Plauderei + Single-Küche

Das sieht doch gleich viel besser aus!

Schlagwortliste

Eine ähnliche Funktion gibt es auch für die Schlagworte, die nennt sich get_the_tag_list, hier lohnt ebenfalls ein Blick in den Codex:

https://codex.wordpress.org/Function_Reference/get_the_tag_list

Diese kleinen Nützlinge kann man immer wieder mal brauchen.