Resultset-Listen in PHP-IT-Templates

Im Rahmen der Programmierung von Datenbank-Abfragen mit PHP ist man immer wieder mit der Frage konfrontiert, wie man die Ergebnisse in listenartiger Form darstellen soll. Hierbei können u.a. folgende Schwierigkeiten auftreten:

a) Die Länge oder Größe des Inhalts einzelner Felder aus der Datenbank ist zunächst unbekannt. Die einzelnen Listenelemente müssen daher in der Höhe variabel sein.

b) Innerhalb eines Listenelementes hat man oftmals komplexe Unterformatierungen vorzunehmen. So ist das Listen-Aufzählungszeichen darzustellen. Hinzu kommen evtl. Feldbezeichnungen, die vom eigentlichen Feldinhalt abgesetzt werden sollen.

c) Der einzelne Satz der Datenbankabfrage beinhaltet i.d.R. mehrere Felder und entsprechende Inhalte, die pro Satz untereinander aufgelistet werden sollen. Um ein komplettes Resultset einer Datenbank-Abfrage darzustellen, braucht man also mindestens eine zweifach geschachtelte Liste. Damit erschwert sich die Formatierung.

d) Die Darstellung der (geschachtelten) Listen erweist sich bzgl. der Formatierung oft als browserabhängig. Dies gilt im besonderen für die Behandlung von Abständen der Aufzählungszeichen zum linken Rand der Liste wie zum Inhalt des Listenpunktes. Hinzu kommen weitere Standardabstände, die ggf. mühsam korrigiert werden müssen.

Will man die Darstellung einer Datenbankabfrage also etwa nach dem Schema

>

Inhalt des Satzes Nr. 11

*

Feldbezeichner 1
Feldinhalt, Feldinhalt, Feldinhalt
Feldinhalt, Feldinhalt, Feldinhalt
Feldinhalt, Feldinhalt, Feldinhalt
Feldinhalt, Feldinhalt, Feldinhalt
Feldinhalt, Feldinhalt, Feldinhalt
Feldinhalt, Feldinhalt, Feldinhalt
………., ……. , …..
………., ……. , …..

 

*

Feldbezeichner 2
Feldinhalt, Feldinhalt, Feldinhalt
Feldinhalt, Feldinhalt, Feldinhalt
Feldinhalt, Feldinhalt, Feldinhalt
………., ……. , …..
………., ……. , …..

 

>

Inhalt des Satzes Nr. 12

*

Feldbezeichner 1
Feldinhalt, Feldinhalt, Feldinhalt
Feldinhalt, Feldinhalt, Feldinhalt
………., ……. , …..
………., ……. , …..

 

formatieren, so erweist sich der simple Einsatz von “<UL><LI></LI></UL>” oft als unzureichend und – nicht zuletzt wegen der Browserabhängigkeit der Listenformatierung – auch als schwierig.

In vielen Fällen sind neben dem oben dargestellten Elementen pro Satz des Resultsets noch weitere schmuckvolle Bereiche darzustellen: etwa eine Leiste mit Buttons zum Auslösen von Pflegeoperationen für den Datenbank-Satzes oder zur Anzeige seines Status.

Gestern bin ich denn auch bei einem Kunden gefragt worden, ob wir zur Darstellung von solchen Resultsetlisten eigentlich Tabellen verwenden würden. Bzw.: Wie wir diese Probleme generell im Zusammenspiel mit serverseitigem PHP lösen würden.

Hierzu gibt es mehrere Antworten:

  1. Wir verwenden zur Formatierung des HTML-Outputs grundsätzlich Templates – zumeist PEAR IT- oder ITX-Templates. (Smarty-Templates sind natürlich eine genauso gute Alternative). Hierdurch trennen wir die Datenbankinteraktion und evtl. Datensatzmanipulationen im PHP-Skript weitgehend von der Darstellung des Resultsets im Browser ab. (Das erleichtert im nachhinein auch den Schwenk von klassischer PHP-Interaktion mit HTML-Generierung zu moderneren Vorgehensweisen auf der Basis von Ajax-Technologien.)

    Alle nachfolgenden Hinweise zur Formatierung sind somit reine HTML- bzw. CSS-Hinweise für das Template.

  2. Bei der Formatierung des listenartigen Outputs im IT-Template setzen wir in der Regel keine Tabellen mehr ein, da wir deren Handhabung inzwischen eher als unflexibel empfinden. Vielmehr verwenden wir sehr wohl Listentags (<UL>, <LI>).
  3. Für die Listen schalten wir i.d.R. allerdings die Formatierung für linksseitige MARGINs und PADDINGs ab. Per CSS setzen wir solche “MARGINs” und “PADDINGs” auf 0, sowohl im <UL>-Tag wie auch im <LI>-Tag. Auch den “list-style-type” setzen wir i.d.R. auf “none”. Hiermit erzielen wir später eine Kompatibilität der Listendarstellung für praktisch alle gängigen Browser (s.u.).
  4. Grundsätzliche Breiteneinstellungen der Liste nehmen wir im <UL>-Tag und wg. des MS IE auch im <LI>-Tag vor. (Das UL-Element verhält sich grundsätzlich wie ein Block. Nur sieht das das der MS IE nicht so.) Die Höhenvorgabe für die gesamte Liste und auch die einzelnen Listenelemente lassen wir dagegen offen.
  5. Die Abstände zwischen den Listen-Elementen regeln “margin-bottom”-Anweisungen für die <LI>-Tags. Hintergrundsfarben für den gesamten Listenbereich regelt wir manchmal über ein DIV, das die Liste umfasst. Von der “normalen” Listenformatierung bleibt also bislang nur eine Breitenvorgabe und ein vorgegebener Abstand zwischen den Listenelementen übrig.
  6. Die Formatierung innerhalb eines Listenelements gestalten wir grundsätzlich mit DIVs. In das <LI>-Tag der Hauptliste binden wir deshalb zunächst ein Container-DIV (“Satz-Container”) vorgegebener Breite ein. Die Höhe dieses Containers bleibt offen.
  7. Innerhalb des “Satz-Container”-DIVs (in einem <LI>-Tag der Liste für die Sätze) positionieren wir nun ein weiteres DIV (“Header-Container”), das zusätzliche DIVs für den den satzspezifischen
    Header und das Aufzählungszeichen für den Satz aufnimmt. Die Positionierung dieser inneren DIVs im “Header-Container” erfolgt über float-Anweisungen; relative Abstände werden über “margin”-Anweisungen festgelegt. Wichtig ist hier noch, dass ein nachfolgendes Dummy-Element (z.B. ein <p>-Tag) das Floaten im Container wieder aufhebt. Breite und Höhe des Header-Containers können in der Regel fest vorgegeben werden. Zusätzlich dient ggf. eine “margin-bottom”-Anweisung zur Festlegung des minimalen Abstands zu weiteren nachfolgenden DIV-Elementen eines Satzes.

  8. Unterhalb des Header-Containers des Satzes positionieren wir nun eine weiteres DIV (“Container Feldliste”). Der Container “Feldliste” wird relativ (!) zum Header-Container positioniert (ggf. ist ein zusätzl. margin-top festzulegen). Seine Breite ist beliebig vorgebbar, seine Höhe bleibt offen.

    Damit die Positionierung auch richtig erfolgen kann, sollte man diesem DIV (Container Feldliste) zur Sicherheit noch eine Style-Anweisung “clear:both;” mitgegeben. Damit werden die vorhergehenden float-Anweisungen für die Header-Elemente aufgehoben, wenn man dies dort vergessen hat.

  9. Verschachtelte zweite Liste: In den “Container Feldliste” positionieren wir nun erneut eine <UL>-Liste, die dann der Darstellung der Feldelemente eines Satzes aus dem Resultset dient. Auch für diese Liste schalten wir evtl. intrinsische linksseitige margin- und padding-Vorgaben der Browser explizit ab und setzen den “list-style-type:none”. Zudem regeln wir den vertikalen Abstand der Listenelemente durch “margin-bottom”-Vorgaben.
  10. Pro Listenelement der feldbezogenen Liste erstellen wir nun einen weiteren DIV-Container (“Container Einzelfeld”). Auch seine Breite und Margins geben wir vor. Die Höhe bleibt dagegen offen.
  11. In den Container “Einzelfeld” werden nun weitere separate DIVs für ein evtl. Aufzählungszeichen, den Feldnamen und den Feldinhalt gesetzt. Die relative Positionierung der inneren DIVs zueinander erreichen wir erneut per CSS-float-Anweisungen. Die relative Positionierung der inneren DIVs zueinander regeln zusätzliche “margin”-Anweisungen. Auch hier heben wir die float-Anweisung wieder über ein abschliessendes Dummy-Element auf. Keines (!) der inneren DIVs wird also im Container über “position:absolute” positioniert. Es werden nur float- und margin-Anweisungen verwendet.
  12. Die Breite der einzelnen inneren DIVs für den Feldbereich geben wir per CSS vor. Die Höhe des DIVs zur Aufnahme des Feldinhalts lassen wir dagegen im Falle von Textfeldern offen, da die Länge und Größe des Feldinhalts dann ja vorab nicht bekannt ist.
  13. Um das Container-DIV “Einzelfeld” (wegen einer evtl. Rahmung oder Hintergrundsfarbe) an die vorab unbekannte Höhe der inneren DIVs – im besonderen des DIVs für den Feldinhalt anzupassen, ist folgender Schritt wichtig:

    Zusätzlich zu den inneren “gefloateten” DIVS bringen wir ein weiteres relativ positioniertes Dummy-Tag (z.B. ein P-Tag) an, das die “style”-Anweisung “clear:both;” enthält. Ohne diese Anweisung ist die Höhe des Container-DIVs “Einzelfeld” nur auf eine Minimum beschränkt und die gefloateten inneren DIVs ragen über den Minimalbereich des Containers weit hinaus. Das Dummy-Tag ermöglicht als reguläres Inhaltselement des Containers die Höhenbestimmung für das Elternelement, indem es die Float-Anweisungen aufhebt.

Durch diese zweifach verschachtelte Liste und die erwähnten Maßnahmen erreicht man eine sehr schöne und im nachhinein per CSS sehr einfach anpassbare Listendarstellung der Ergebnisse von Datenbankabfragen. Da die eigentliche Formatierung jedes Listenelements inkl. Aufzählungszeichen innerhalb
eines DIV-Containers erfolgt, wird man bzgl. der Listendarstellung der Sätze und der inneren Liste der Felder praktisch browser-unabhängig.

Natürlich lagert man die CSS-Anweisungen für die Listen und die inkludierten DIVs zur besseren Pflegbarkeit in eine separate Datei aus.

PHP5 – dynamische Variable – superglobale Arrays

Gestern bin ich an einem kleinen aber interessanten Problem in der PHP-Programmierung hängen geblieben. Manchmal gibt es Situationen, in denen man mit einer dynamisch erzeugten Variablen – also einer Variablen, deren Bezeichnung erst zur Laufzeit festgelegt wird – eine Aufgabe schnell in den Griff bekommt. In meinem Fall versuchte ich, in einem Objekt eine dynamische Variable zu verwenden, die sich situationsbedingt entweder auf das _GET oder das _POST – Array beziehen sollte. Dies misslang zunächst – das Thema ist daher vielleicht auch für andere interessant. Es geht also um die Kombination von dynamischen Variablen mit superglobalen Arrays. Der Reihe nach:

Voraussetzung – Erzeugung von dynamischen Variablen

Einfache dynamische Variable
erzeugt man über folgenden Mechanismus

$dyn = “anracon”;
$$dyn = “anracona”;
echo ” \$dyn hat den Wert: ” . $dyn . “<br>”;
echo ” \$anracon hat den Wert: ” . $anracon . ” ( = identisch mit \$\$dyn : ” . $$dyn . ” ) <br>”;

Dynamische Array-Variable
sind etwas anders zu behandeln. Man beachte die geschweiften Klammern:

$dyna = “ay_anracon”;
${$dyna}[0] = “anracona”;
echo “\$dyna hat den Wert: ” . $dyna . “<br>”;
echo “\$ay_anracon[0] hat den Wert: ” . $ay_anracon[0] . ” ( = identisch mit \${\$dyna}[0] : ” . ${$dyna}[0] . ” ) <br>”;

(Das Ganze geht natürlich auch mit assoziativen Arrays).

Hinweis: Man könnte bei der Erzeugung dynamischer Variable zur Not auch mit der “eval”-Funktion arbeiten – das ist aber wirklich umständlich, total unsicher und wir lassen das der Übersichtlichkeit halber einfach weg.

Dynamische Bezugnahme auf “Superglobale Arrays”

Man übergibt einem Programm die GET-Variable “snr” – Bsp.: get_parameter.php5?snr=100

Enthält das Programm auf oberster Ebene (also nicht in einer Subfunktion oder einer Methode eines Objekts) folgenden Code:

$b = true;
$ind = ‘snr’;
echo ” get_parameter = ” . $_GET[$ind] . “<br>”;
if($b) { $sgv = “_GET”; }
else { $sgv = “_POST”; }
echo ” get_parameter = ” . ${$sgv}[$ind] .”<br>” ;

so erhält man zwei identische Outputzeilen mit dem Inhalt “getparameter = 100”. Die Bezugnahme der dynamischen Array-Variablen ${$sgv} auf das “_GET”-Array funktioniert also wie erwartet.

Bezugnahme auf “Superglobale Arrays” in Objektmethoden

Hat man im Programm eine Klasse und ein zugehöriges Objekt definiert, so kann man mit einer Objektmethode direkt auf das GET-Array zugreifen, da es superglobal ist und sein Geltungsbereich daher nicht mit dem “global”-Statement spezifiziert werden muss. Folgendes funktioniert also innerhalb des Programms “get_parameter.php5”:

class GetParam
{
var $gp;
function __construct()
{
$this->gp = $_GET[‘snr’];
}
}
$Ob = new GetParam;
echo ” get_parameter = ” . $Ob->gp . “<br>”;

Dynamische Bezugnahme auf “Superglobale Arrays” in Objektmethoden

Folgendes geht jedoch nicht – trotz superglobaler Gültigkeit des “_GET”-Arrays:

class GetPar
{
var $gp;
function __construct()
{
$b = true; // Nur als Beispiel
if($b) { $sgv = “_GET”; }
else { $sgv = “_POST”; }
$this->gp = ${$sgv}[‘snr’];
}
}
$Obj = new GetPar;
echo ” get_parameter = ” . $Obj->gp . “<br>”;

Denkt man etwas darüber nach, so kommt man auf folgende Vermutung: Woher soll der Interpreter wissen, dass er eine globale Variable vorsehen muss? Er wird sich schon aus Sicherheitsgründen vielmehr auf einen lokalen Gültigkeitsbereich der dynamischen Variablen zurückziehen. Dann sollte aber Folgendes helfen:

class GetP
{
var $gp;
function __construct()
{
$b = true; // Nur als Beispiel
if($b) { $sgv = “_GET”; }
else { $sgv = “_POST”; }
//
global ${$sgv};
//
$this->gp = ${$sgv}[‘snr’];
}
}
$GP = new GetP;
echo ” get_parameter = ” . $GP->gp . “<br>”;

Tatsächlich erhält man nun den gewünschten Output. Das “global”-Statement ist zwar wirklich nicht schön. Aber bei einer Bezugnahme auf ein superglobales Array kann man diese stilistische Unsauberkeit vielleicht verzeihen.

Abschließende Anmerkung

Ziel dieses Beitrags war es, darauf hinzuweisen, dass man innerhalb von Objektmethoden mit Hilfe dynamischer Variablen nicht ohne weiteres auf superglobale Arrays zugreifen kann. Scheut man das erforderliche “global” – Statement, so muss man seien Lösungen von vornherein anders konzipieren – was in der Regel sicher möglich ist.

(In meinem Fall wollte ich zu Prüfzwecken aus dem Objekt heraus einen direkten (!) Zugriff auf die Arrays ohne Zwischenvariablen gewährleisten und trotzdem die Wahl zwischen _GET und _POST haben.)

FPDF und PHP – erste Erfahrungen

Ich habe vor kurzem die Aufgabe erhalten, mit PHP PDF-Dokumente zu generieren, die auf den Einträgen in einer Datenbank basieren. Möglichst kostengünstig. Der Griff zur PFDlib war wegen der relativ hohen Kosten für kommerzielle Zwecke also nicht möglich. Zudem behagte mir die PDFlib auch nicht, da sie als Modul im Webserver (Apache) integriert werden muss. Das wäre bei dem Web-Service-Provider, der die Domaine meines Kunden hostet, dann eher ein Problem geworden.

In meiner Not habe ich dann zur Opensource Bibliothek “FPDF” (http://www.fpdf.de/) gegriffen. Die dortigen (objektorientierten) Bibliotheken benötigen nur PHP und werden nicht als Apache-Module geladen. Ich muss nach ein paar Tagen produktiver Arbeit feststellen, dass ich mit der Bibliothek sehr positive Erfahrungen gemacht habe:

1) Die Installation ist sehr einfach.
2) Die Doku ist knapp bemessen, aber verständlich und letztlich ausreichend. Ein Blick in die Online-Tutorials mit den dortigen Beispielen ist sehr hilfreich.
3) Die Einbindung und Erweiterung der zu nutzenden Bibliotheks-Objekte in eigene PHP-Objekte und Methoden ist auf bequeme Art und Weise möglich.
4) Die Methodenaufrufe zur Gestaltung des PDF-Dokumentes sind zwar begrenzt, dafür aber sehr einfach zu verstehen und anzuwenden.
5) Footer und Header-Methoden sind einfach für die eigenen Zwecke zu überschreiben.
6) Tabellarische Inhalte lassen sich sehr bequem erstellen.
7) Die Geschwindigkeit bei der PDF-Erzeugung ist zumindest für einfache Zwecke ausreichend.
8) Es wird nicht zuviel versprochen – die angebotenen Methoden funktionieren.

Etwas umständlich ist die Einbindung von Truetype-Schriften und die zugehörige Erzeugung der metrischen Informationsdateien. Hier dem User etwas kompaktere Tools anzubieten, wäre nicht schlecht.

Aber alles in allem: eine empfehlenswerte Bibliothek für Leute, die PDF-Dokumente mit PHP erzeugen wollen und nicht zur PDFlib greifen können.