Objekte und Datenbanken, best practice

Ich bin dabei, mir Gedanken ueber die Umstellung einer groesseren
Applikation auf einen objektorientierten Ansatz zu machen. Fuer die
einfachen Dinge nimmt das Konzept langsam konkrete Zuege an, aber
fuer die Abbildung der (ueber 50) Datenbankobjekte stellen sich mir
noch ein paar grundlegende Fragen.

Im weiteren soll das rein fiktive Objekt "Kostenstelle" als Beispiel
dienen, das eine Kennung und einen Namen, sowie eine Adresse hat,
welche selbst wieder durch ein (datenbankbasiertes) Objekt erfasst
ist. Mein erster Ansatz waere da (teilweise nur skizziert):

| class Kostenstelle {
| private $dbID;
| private $kennung;
| private $name;
| private $addresse;
|
| public function __construct($dbID=NULL) {
| if (is_null($dbID)) {
| # neue kostenstelle anlegen
| $this->dbID = NULL;
|
| $this->kennung = '';
| $this->name = '';
| $this->addresse = NULL;
| }
| else {
| # bestehende kostenstelle in der datenbank
| # referenzieren
| $this->dbID = $dbID;
|
| # hier noch informationen aus der datenbank
| # laden und eintragen, z.B.:
| $this->addresse = new Addresse($dbIDAddresse);
| }
| }
|
| public function __destruct() {
| $this->save();
| }
|
| public function save() {
| # hier noch pruefungen der daten durchfuehren
| if (is_null($this->dbID)) $this->dbID = SQLinsert();
| else SQLupdate($this->dbID);
| }
|
| public function abc() { ... }
| }

Nun tauchen da schon einmal einige Fragen bei mir auf:

a) es erscheint mir unsinnig, beim Aufruf des Konstruktors gleich
alle Daten aus der Datenbank zu holen. Das mag hier zwar einfach
sein, bei komplexeren Objekten ist das sicherlich nicht der Fall.
Spaeteres nachladen waere ja auch kein Problem, bloss muesste ich
dann in jeder Methode pruefen, ob das bereits geschehen ist. Kann
man diesen Prozess (Pruefen und Nachladen) irgendwie einigermassen
elegant automatisieren?

b) ist es weise, das Abspeichern nach Aenderungen in den Destruktor
zu verlagern? Ad hoc faellt mir nichts ein, was dagegenspraeche
(vielleicht ggf. Fehlerbehandlung?), und es automatisiert diese
Arbeit.

c) im Prinzip braucht bzw. soll es fuer jede $dbID auch nur ein
Objekt geben, was im PHP-Code natuerlich erst einmal nicht
garantiert wird. Wie loest man das am elegantesten (d.h. ein "new
Kostenstelle(48)" sollte beim ersten Aufruf das zum Datensatz 48
passende Objekt erstellen, beim zweiten Aufruf hingegen einer
Referenz auf das erste Objekt)? Oder ist das schon von Grund auf ein
verwerflicher Ansatz?

d) wenn in abc() Adressdaten ausgegeben werden, wird das (analog
konstruierte) Adress-Objekt seinerseits Informationen aus der
Datenbank nachladen. Bislang gab es in solchen Faellen ein
SELECT-Statement ueber mehrere Tabellen, das sich alle benoetigten
Informationen zusammengetragen hat. Durch die Kapselung wird das
Programm zwar eleganter, dafuer sind aber (je nach Objekt durchaus
auch massiv) mehr SELECT-Statements notwendig. Wie geht man damit in
der Praxis vernuenftig um?

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich

Stefan - Genuss mit Zittern und Zagen!
(Sloganizer)
Stefan+Usenet [ So, 04 November 2007 18:35 ] [ ID #1862197 ]

Re: Objekte und Datenbanken, best practice

..oO(Stefan Froehlich)

>Ich bin dabei, mir Gedanken ueber die Umstellung einer groesseren
>Applikation auf einen objektorientierten Ansatz zu machen. Fuer die
>einfachen Dinge nimmt das Konzept langsam konkrete Zuege an, aber
>fuer die Abbildung der (ueber 50) Datenbankobjekte stellen sich mir
>noch ein paar grundlegende Fragen.
>
>[...]
>
>Nun tauchen da schon einmal einige Fragen bei mir auf:
>
>a) es erscheint mir unsinnig, beim Aufruf des Konstruktors gleich
>alle Daten aus der Datenbank zu holen. Das mag hier zwar einfach
>sein, bei komplexeren Objekten ist das sicherlich nicht der Fall.
>Spaeteres nachladen waere ja auch kein Problem, bloss muesste ich
>dann in jeder Methode pruefen, ob das bereits geschehen ist. Kann
>man diesen Prozess (Pruefen und Nachladen) irgendwie einigermassen
>elegant automatisieren?

Ich habe hier z.B. eine Klasse TProductDescriptor, welche ein Produkt
eines Shopsystems beschreibt und alle dazugehörigen Informationen
bereitstellt bzw. unter einem Hut zusammenfaßt. Die Informationen selbst
sind in der DB über zahlreiche Tabellen verstreut (Produktdetails, Kate-
gorien, Mengenrabatte, sonstige Attribute etc.) Die Descriptor-Klasse
stellt all diese Infos über entsprechende Methoden bereit:

::getCategories()
::getClasses()
::getDiscounts()
::getAttributes()

usw. Diese Methoden existieren aber nicht wirklich, sondern werden alle
intern über __call() abgefangen und bearbeitet. Dadurch kann ich an
einer zentralen Stelle darüber Buch führen, welche Informationen bereits
geladen wurden und welche erst noch aus der DB geholt werden müssen. Was
nicht gebraucht wird, wird auch nicht geladen.

Das gleiche erfolgt auch in umgekehrter Richtung - alle set*()-Gegen-
stücke zu obigen Methoden werden ebenfalls von __call() abgefangen und
protokolliert. Somit werden später dann auch nur die Infos in die DB
geschrieben, die geändert wurden.

>b) ist es weise, das Abspeichern nach Aenderungen in den Destruktor
>zu verlagern? Ad hoc faellt mir nichts ein, was dagegenspraeche
>(vielleicht ggf. Fehlerbehandlung?), und es automatisiert diese
>Arbeit.

Ich würd's nicht machen, da ich keine Kontrolle über die Destruktor-
Aufrufe habe und somit nicht genau weiß, zu welchem Zeitpunkt und in
welcher Reihenfolge sie aufgerufen werden. Wäre schon blöd, wenn der
eine Destruktor noch etwas abspeichern will, das Datenbank-Objekt aber
bereits entsorgt wurde.

Ich kann zwar mangels Kenntnissen über die internen Abläufe in der Zend-
Engine nicht beurteilen, ob dies in PHP wirklich geschehen könnte, halte
es aber prinzipiell für möglich. Mir sind daher explizite Methodenauf-
rufe bzw. irgendwie geartete Aufrufe unter meiner Kontrolle grundsätz-
lich lieber.

>c) im Prinzip braucht bzw. soll es fuer jede $dbID auch nur ein
>Objekt geben, was im PHP-Code natuerlich erst einmal nicht
>garantiert wird. Wie loest man das am elegantesten (d.h. ein "new
>Kostenstelle(48)" sollte beim ersten Aufruf das zum Datensatz 48
>passende Objekt erstellen, beim zweiten Aufruf hingegen einer
>Referenz auf das erste Objekt)? Oder ist das schon von Grund auf ein
>verwerflicher Ansatz?

Wenn für eine ID zu jedem Zeitpunkt nur eine einzige Instanz existieren
soll, dann käme das Singleton-Pattern vielleicht ganz gelegen (Details
dazu gibts im Archiv dieser Gruppe oder bei Google zuhauf). Statt

$dbID = new Kostenstelle(48);

wäre der Aufruf dann z.B.

$dbID = Kostenstelle::getInstance(48);

>d) wenn in abc() Adressdaten ausgegeben werden, wird das (analog
>konstruierte) Adress-Objekt seinerseits Informationen aus der
>Datenbank nachladen. Bislang gab es in solchen Faellen ein
>SELECT-Statement ueber mehrere Tabellen, das sich alle benoetigten
>Informationen zusammengetragen hat. Durch die Kapselung wird das
>Programm zwar eleganter, dafuer sind aber (je nach Objekt durchaus
>auch massiv) mehr SELECT-Statements notwendig. Wie geht man damit in
>der Praxis vernuenftig um?

Siehe oben. Eventuell versuchen, bestimmte SELECT-Anweisungen erst bei
Bedarf abzusenden. Bei mehrfachem Absenden der gleichen Query können
auch Prepared Statements der Performance zuträglich sein. Ansonsten
bleibt nur testen, ob und wo die wirklichen Flaschenhälse sind.

Micha
Michael Fesser [ So, 04 November 2007 19:08 ] [ ID #1862198 ]

Re: Objekte und Datenbanken, best practice

Stefan Froehlich schrieb:
> Ich bin dabei, mir Gedanken ueber die Umstellung einer groesseren
> Applikation auf einen objektorientierten Ansatz zu machen. Fuer die
> einfachen Dinge nimmt das Konzept langsam konkrete Zuege an, aber
> fuer die Abbildung der (ueber 50) Datenbankobjekte stellen sich mir
> noch ein paar grundlegende Fragen.

Meine grundlegende Frage dabei wäre: was habe ich davon? Spart es mir
Zeit, macht es meinen Code besser wartbar, besser wiederverwendbar,
sicherer, stabiler, schneller? Sehe ich nirgendwo.

--
Mein Zeugs:
http://www.hadanite-marasek.de/classes.php
http://www.objektivsuche.de/
Ansonsten:
http://www.php-faq.de/q/q-newsgroup-wie-helfen.html
Hadanite Marasek [ Mo, 05 November 2007 01:33 ] [ ID #1862969 ]

Re: Objekte und Datenbanken, best practice

On Mon, 05 Nov 2007 01:33:36 +0100 Hadanite Marasek wrote:
> > Ich bin dabei, mir Gedanken ueber die Umstellung einer groesseren
> > Applikation auf einen objektorientierten Ansatz zu machen. [...]

> Meine grundlegende Frage dabei wäre: was habe ich davon? [...]

Das wollte ich der Gruppe ersparen :-)

> Spart es mir Zeit, macht es meinen Code besser wartbar, besser
> wiederverwendbar, sicherer, stabiler, schneller?

Einfaches Beispiel: der Code verwendet ein ausgefeiltes
Template-System, um HTML, PDF, CSV, XML, Excel... zu erzeugen.
Bislang bekommen die Templates jeweils spezifisch zugeschnittene,
assoziative Arrays mit den moeglicherweise fuer sie interessanten
Werten uebergeben. Das funktioniert zwar perfekt, ist aber
unuebersichtlich und in der Masse praktisch nicht mehr wartbar: die
Dokumentation, was wo unter welchem Namen verfuegbar ist, hinkt
konsequent hinter der Realitaet her.

Mit einer Auswahl an ca. 50 Objekten brauche ich bloss noch einmal die
Methoden dokumentieren, sowie je Template eine Liste der erhaeltlichen
Objekte - und habe damit restlos geklaert, auf welche Funktionalitaeten
zugegriffen werden kann.

Hauptnutzen soll also "besser wartbar" sein, daneben auch "besser
wiederverwendbar". Stabilitaet kommt vielleicht als Randeffekt mit,
Sicherheit eher weniger. _Schneller_ wird dadurch gar nichts, aber damit
kann ich (hoffentlich - siehe die Bedenken bezgl. hoeherer Zahl der
Abfragen) leben.

> Sehe ich nirgendwo.

Woher kennst Du eigentlich meinen Code? :-)

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich

Stefan - die höchste Ueberraschung des Verruchten!
(Sloganizer)
Stefan+Usenet [ Mo, 05 November 2007 08:32 ] [ ID #1862970 ]

Re: Objekte und Datenbanken, best practice

> Einfaches Beispiel: der Code verwendet ein ausgefeiltes
> Template-System, um HTML, PDF, CSV, XML, Excel... zu erzeugen.
> Bislang bekommen die Templates jeweils spezifisch zugeschnittene,
> assoziative Arrays mit den moeglicherweise fuer sie interessanten
> Werten uebergeben. Das funktioniert zwar perfekt, ist aber
> unuebersichtlich und in der Masse praktisch nicht mehr wartbar: die
> Dokumentation, was wo unter welchem Namen verfuegbar ist, hinkt
> konsequent hinter der Realitaet her.

Hmmm....was erzeugst Du denn da? Also ich habe ein System mit 104
Tabellen und nichts dergleichen, weil es in den meisten Fällen nur
Eingabeformular/Ausgabeliste ist. Die Ausgabeliste ist dann eine Abfrage
+ generische Klasse, die es darstellt.

Wenn Du natürlich lauter sehr spezifische Ausgaben hast, ist die Sache
anders gelagert. Ich habe das bei einer Zeiterfassung; dort habe ich die
Situation, dass ich Ausgaben brauche, deren Struktur nicht mehr 1:1 von
SQL geliefert wird. Ich gehe dann hin und programmiere eine abstrakte
"Zusammenstellungsklasse", die eine konkrete Ausgabe hat. Generell bin
ich jemand, der eher weniger mit Templates arbeitet. Zumindest enthalten
meine Templates nur noch Platzhalter und keinerlei Logik.

> Woher kennst Du eigentlich meinen Code? :-)

Wäre mal interessant. Ich bräuchte noch mehr Hintergrundinformationen
zur Problematik.


--
Mein Zeugs:
http://www.hadanite-marasek.de/classes.php
http://www.objektivsuche.de/
Ansonsten:
http://www.php-faq.de/q/q-newsgroup-wie-helfen.html
Hadanite Marasek [ Mo, 05 November 2007 09:11 ] [ ID #1862971 ]

Re: Objekte und Datenbanken, best practice

Stefan Froehlich schrieb:
> Ich bin dabei, mir Gedanken ueber die Umstellung einer groesseren
> Applikation auf einen objektorientierten Ansatz zu machen. Fuer die
> einfachen Dinge nimmt das Konzept langsam konkrete Zuege an, aber
> fuer die Abbildung der (ueber 50) Datenbankobjekte stellen sich mir
> noch ein paar grundlegende Fragen.

Ich habe eine Basisklasse, welche sich um die Datenhaltung (siehe unten,
assotiatives Array) und die komplette Kommunikation mit der Datenbank
kümmert. Die einzelnen Datenklasse stellen dann jeweils eine Tabelle
dar. Der Quellcode jeder dieser Klasse bleibt dadurch sehr schlank.

> Im weiteren soll das rein fiktive Objekt "Kostenstelle" als Beispiel
> dienen, das eine Kennung und einen Namen, sowie eine Adresse hat,
> welche selbst wieder durch ein (datenbankbasiertes) Objekt erfasst
> ist. Mein erster Ansatz waere da (teilweise nur skizziert):
>
> | class Kostenstelle {
> | private $dbID;
> | private $kennung;
> | private $name;
> | private $addresse;
....

Ich würde ein assotiatives Array wählen. Hat den Vorteil dass eine
Methode "set($key, $value)" und "get($key)" machen kannst und eine
Basisklasse für alle Tabellenklasse, welche das implementiert.

> a) es erscheint mir unsinnig, beim Aufruf des Konstruktors gleich
> alle Daten aus der Datenbank zu holen. Das mag hier zwar einfach
> sein, bei komplexeren Objekten ist das sicherlich nicht der Fall.
> Spaeteres nachladen waere ja auch kein Problem, bloss muesste ich
> dann in jeder Methode pruefen, ob das bereits geschehen ist. Kann
> man diesen Prozess (Pruefen und Nachladen) irgendwie einigermassen
> elegant automatisieren?

Ich würde es nicht im Konstruktor laden, vor allem weil das dann ein
"select *" wäre. Meine Tabellenklasse besitzen eine Methode "load()",
welcher ich eine beliebige Anzahl an Spalten mitgeben kann zum laden.
Hat den Vorteil dass ich die Daten dann lade, wenn ich diese brauche und
nur die, die ich brauche.

> c) im Prinzip braucht bzw. soll es fuer jede $dbID auch nur ein
> Objekt geben, was im PHP-Code natuerlich erst einmal nicht
> garantiert wird. Wie loest man das am elegantesten (d.h. ein "new
> Kostenstelle(48)" sollte beim ersten Aufruf das zum Datensatz 48
> passende Objekt erstellen, beim zweiten Aufruf hingegen einer
> Referenz auf das erste Objekt)? Oder ist das schon von Grund auf ein
> verwerflicher Ansatz?

Singleton wäre hier der Ansatz. Allerdings würde ich nicht jede
Datenklasse zu einem Singleton machen, sondern eine zentrale Klasse, die
alles verwaltet.

Aufruf dann über z.B.: getData($class, ...);

Man sollte der Methode natürlich beliebig viele Parameter mitgeben
können, da es auch zusammengesetzte Primary Keys geben kann.

> d) wenn in abc() Adressdaten ausgegeben werden, wird das (analog
> konstruierte) Adress-Objekt seinerseits Informationen aus der
> Datenbank nachladen. Bislang gab es in solchen Faellen ein
> SELECT-Statement ueber mehrere Tabellen, das sich alle benoetigten
> Informationen zusammengetragen hat. Durch die Kapselung wird das
> Programm zwar eleganter, dafuer sind aber (je nach Objekt durchaus
> auch massiv) mehr SELECT-Statements notwendig. Wie geht man damit in
> der Praxis vernuenftig um?

Warum mehr Select Statements? Du brauchst ja nicht mehr Daten, sondern
tust das ganze nur kapseln. Zur Ausgabe würde ich diese Datenobjekte
entweder über die getter auslesen und selbst erzeugen oder (wenn eine
Ausgabe mehrfach benötigt werden kann) extra Viewer Klassen erstellen.

--
Mit freundlichen Grüßen,
Christoph Herrmann

http://dragonprojects.de/
Christoph Herrmann [ Mo, 05 November 2007 09:32 ] [ ID #1862972 ]

Re: Objekte und Datenbanken, best practice

Stefan Froehlich schrieb:
> Ich bin dabei, mir Gedanken ueber die Umstellung einer groesseren
> Applikation auf einen objektorientierten Ansatz zu machen. Fuer die
> einfachen Dinge nimmt das Konzept langsam konkrete Zuege an, aber
> fuer die Abbildung der (ueber 50) Datenbankobjekte stellen sich mir
> noch ein paar grundlegende Fragen.


Alle DB-Klassen sind Subklassen von dbobject und bieten folgende
Schnittstelle: Vielleicht kannst du Dir ein paar Anregungen holen:

// Konstruktion

__construct Die Konstruktion erlaubt folgende Parameter:
() Leerer Konstruktor. Initialisierung bei
setSpalte().
(int) der Datensatz mit dem Angegebenen PK wird
aus der DB geladen.
(instanceof self) Copy-Construktor.
(array) objekt wird mittels getFromSql
Initialisiert.
(string) Das objekt wird mit diesem Where-teil
aus der DB initialisiert.
(array,int) objekt wird mittels getFromSqlOffset
Initialisiert.
(func_num_args == getSQLCOUNTER) beschreibt
direkt das Datenarray.

// Tabeleninfos:
getTABELLENKUERZEL Ein Kürzel für die Tabelle
getSQLPK Der Name des Primärschlüssels
getSQLID Der Name des internen-Schlüssels, bei normalen
Tabellen=Primärschlüssels
getSQLTABELLE Der Name der Tabelle in der DB
getSQLGETTERM Ein Term für das Recordset im Format:
kürzel.spalte AS spalte, ...
getSQLGET Ein kompletter SQL-Befehl um die einen Recordset
auszulesen (Ohne WHERE-Statement)
getSQLSET Ein Term für einen SET teil im UPDATE format:
spalte=?, ...
getSQLSETPREP Ein Term für alle Spalten im format: spalte, ...
getSQLAUSSCHLUSS Eine Where-klausel um Gelöschte Datensätze
auszuschließen.
getSQLANGEZEIGTE Eine Where-klausel um Gelöschte und versteckte
Datensätze auszuschließen
getSQLCOUNTER Die Anzahl der Spalten. Nützlich für Kombinierte
Selects, um den Offset zu berechnen
getDATATYPES Ein String mit den Datentypen Reihenfolge wie in
SQLSETPREP
getSQLTABLESH ein String im format "tabelle kürzel"
getID gibt die ID zurück (entweder DB-ID oder Temp-Id)
getDBID gibt die Datenbank ID zurück. 0 wenn der
Datensatz noch nicht in der DB ist

// Objektzustände
isChanged Liefert True, wenn sich der Datensatz geändert
hat
isWasteable Tabelle hat ein Papierkorbattribut
isTree Tabelle ist ein NestetSet
getSpalte gibt den einzelnen Wert zurück

//DB- zugriff
singleRead($id) Liest den Datensatz mit der ID aus der Tabelle
singleWrite Schreibt den Datensatz in die Tabelle. (benutzt
Optimistic-Locking)
write(&$connection) Schreibt das Objekt in die DB über die
angegebene PDO-Connection.

//Datenmanipulation
setID($id) Setzt die temp_id
setSpalte($wert) Setzt den Wert
getFromSql(&$result) Holt die Daten von einem flachen
Array (RecordSet)
getFromSqlOffset(&$result,$offset) Holt die Daten von einem flachen
Array ab einer bestimten Position
setFromArray() Das Objekt wird aus einem
Assoziativen Array befüllt wird
nichts übergeben dann wird post
benutzt. Es kann ein präfix
benutzt werden
setPrefix(String &$prefix) Setzt den Präfix für setFromArray()
getPrefix Gibt den Präfix für setFromArray()
zurück



>
> Im weiteren soll das rein fiktive Objekt "Kostenstelle" als Beispiel
> dienen, das eine Kennung und einen Namen, sowie eine Adresse hat,
> welche selbst wieder durch ein (datenbankbasiertes) Objekt erfasst
> ist. Mein erster Ansatz waere da (teilweise nur skizziert):

....

> | public function __destruct() {
> | $this->save();
> | }

Das halte ich für nicht gut. Gespeichert wird nur wenn das auch
ausdrücklich verlangt wird. So kannst du dir krass ins Bein schießen.

Ausserdem sollten deine Objekte Transaktionen berücksichtigen. Mann will
mehrere Objekte mit einer Connection schreiben.

> Nun tauchen da schon einmal einige Fragen bei mir auf:
>
> a) es erscheint mir unsinnig, beim Aufruf des Konstruktors gleich
> alle Daten aus der Datenbank zu holen. Das mag hier zwar einfach
> sein, bei komplexeren Objekten ist das sicherlich nicht der Fall.
> Spaeteres nachladen waere ja auch kein Problem, bloss muesste ich
> dann in jeder Methode pruefen, ob das bereits geschehen ist. Kann
> man diesen Prozess (Pruefen und Nachladen) irgendwie einigermassen
> elegant automatisieren?

Au kannst ruhig die ganze Zeile aus der Datenbank holen. Sollange da
keine Extrem großen Blobs, Texte oder Bilder drin sind.

Nachladen würde viel mehr Overhead erzeugen. Wirklich Kritische spalten
blende ich mit einem NULL-Wert im SQL term aus:

Select s.foo as foo, s.bar as bar, null where s.pk=1

Mach ich z.B. bei Passwörtern so.


> b) ist es weise, das Abspeichern nach Aenderungen in den Destruktor
> zu verlagern? Ad hoc faellt mir nichts ein, was dagegenspraeche
> (vielleicht ggf. Fehlerbehandlung?), und es automatisiert diese
> Arbeit.

Nein. Der Zeitpunkt wann Destruktor aufgerufen wird ist undefiniert.
Mann will auch Transaktionen benutzen. Dazu müssen die Schreib-Methoden
aller zu schreibenden Objekte gebündelt werden.

> c) im Prinzip braucht bzw. soll es fuer jede $dbID auch nur ein
> Objekt geben, was im PHP-Code natuerlich erst einmal nicht
> garantiert wird. Wie loest man das am elegantesten (d.h. ein "new
> Kostenstelle(48)" sollte beim ersten Aufruf das zum Datensatz 48
> passende Objekt erstellen, beim zweiten Aufruf hingegen einer
> Referenz auf das erste Objekt)? Oder ist das schon von Grund auf ein
> verwerflicher Ansatz?

Jein. Ein Cache für die schon geladenen Instanzen steckt aber besser
nicht im ORM sondern eine Schicht höher. Steck Objekte die öfter
vorkommen kännen einfach in ein Array.

> d) wenn in abc() Adressdaten ausgegeben werden, wird das (analog
> konstruierte) Adress-Objekt seinerseits Informationen aus der
> Datenbank nachladen. Bislang gab es in solchen Faellen ein
> SELECT-Statement ueber mehrere Tabellen, das sich alle benoetigten
> Informationen zusammengetragen hat. Durch die Kapselung wird das
> Programm zwar eleganter, dafuer sind aber (je nach Objekt durchaus
> auch massiv) mehr SELECT-Statements notwendig. Wie geht man damit in
> der Praxis vernuenftig um?

Ich habe dazu getFromSqlOffset. Damit kannst du mehrere Objekte aus
einem Record lesen. Beispiel:


$SQL ="SELECT " ;
$SQL.=xobj::getSQLGETTERM();
$SQL.=aobj::getSQLGETTERM();
$SQL.=" WHERE a.id=x.id" ....

// ergibt:
//SELECT x.x_id, x.foo, x.bar, x.blub, a.sepp, a.karl FROM x,a WHERE ...

$ergebnissarray=db::getRecord($SQL);
x=new xobj()
a=new aobj()
x.getFromSql($ergebnissarray)
a.getFromSql($ergebnissarray,x.getSQLCOUNTER());

//Einfacher noch mit den Construktoren:

$ergebnissarray=db::getRecord($SQL);
x=new xobj($ergebnissarray)
a=new aobj($ergebnissarray,x.getSQLCOUNTER())
Harald Stowasser [ Mo, 05 November 2007 09:38 ] [ ID #1862973 ]

Re: Objekte und Datenbanken, best practice

On Mon, 05 Nov 2007 09:38:10 +0100 Harald Stowasser wrote:
> Alle DB-Klassen sind Subklassen von dbobject und bieten folgende
> Schnittstelle: Vielleicht kannst du Dir ein paar Anregungen holen:

Die eine oder andere sicherlich. Die Argumente beim Konstruktor
variabel zu halten, ist eine nette Idee (eine Moeglichkeit, in PHP
mit Typehinting von vornherein unterschiedliche Konstruktoren
anbieten zu koennen, waere natuerlich noch schoener).

Weniger gut gefaellt mir persoenlich die Zerstueckelung von
SQL-Befehlen. Da hat sich in der Vergangenheit (im Sinn von
Wartbarkeit) sehr bewaehrt, die komplett beisammen zu lassen,
notfalls auch ein wenig auf Kosten von Effizienz.

> > | public function __destruct() {
> > | $this->save();
> > | }

> Das halte ich für nicht gut. Gespeichert wird nur wenn das auch
> ausdrücklich verlangt wird. So kannst du dir krass ins Bein
> schießen.

> Ausserdem sollten deine Objekte Transaktionen berücksichtigen.
> Mann will mehrere Objekte mit einer Connection schreiben.

Die Idee, die ich kurz hatte war, dass (im Hauptspeicher) ohnehin
nur in Objekte geschrieben wird, wenn das auch (in der Datenbank)
gespeichert werden soll - ist ja schliesslich eine Webapplikation,
wo nach dem Submit und Pruefen der Eingangsdaten nichts mehr kommt,
was den Schreibvorgang noch geplant aufhalten koennen sollte.

Aber die Transaktionen stehen diesem Ansatz ohnehin entgegen, damit
erledigt sich das also.

> > a) es erscheint mir unsinnig, beim Aufruf des Konstruktors gleich
> > alle Daten aus der Datenbank zu holen. Das mag hier zwar einfach
> > sein, bei komplexeren Objekten ist das sicherlich nicht der Fall.
> > Spaeteres nachladen waere ja auch kein Problem, bloss muesste ich
> > dann in jeder Methode pruefen, ob das bereits geschehen ist. Kann
> > man diesen Prozess (Pruefen und Nachladen) irgendwie einigermassen
> > elegant automatisieren?

> Au kannst ruhig die ganze Zeile aus der Datenbank holen. Sollange
> da keine Extrem großen Blobs, Texte oder Bilder drin sind.
> Nachladen würde viel mehr Overhead erzeugen.

Einige Bilder gibt es, aber die werden in getrenntem Code verwaltet.
Aber: es gibt viele Verkettungen. Beim Beispiel Kostenstelle war es
eine Adresse (die bei der Initialisierung der Kostenstelle dann
ihrerseits nachgeladen wuerde), in Wahrheit ist es nicht bloss eine,
sondern eine ganze Liste davon. Jede Adresse ist wiederum mit
Personen verknuepft, diese mit Elementen der
Organisationshierarchie, und so weiter.

Das Nachladen soll mir nicht das _eine_ aktuelle Objekt
beschleunigen, sondern primaer die zahlreichen anderen , die damit
verknuepft sind (und auf die nur in wenigen Funktionen dann auch
tatsaechlich zugegriffen wird).

> > ein "new Kostenstelle(48)" sollte beim ersten Aufruf das zum
> > Datensatz 48 passende Objekt erstellen, beim zweiten Aufruf
> > hingegen einer Referenz auf das erste Objekt)? Oder ist das
> > schon von Grund auf ein verwerflicher Ansatz?

> Jein. Ein Cache für die schon geladenen Instanzen steckt aber
> besser nicht im ORM sondern eine Schicht höher. Steck Objekte die
> öfter vorkommen kännen einfach in ein Array.

Ok, ein Array (in seiner Funktion als Hash) kam mir dafuer als sehr
einfache Loesung auch schon in den Sinn, ich wollte mir bloss noch
weitere Meinungen dazu einholen.

> > Durch die Kapselung wird das Programm zwar eleganter, dafuer
> > sind aber (je nach Objekt durchaus auch massiv) mehr
> > SELECT-Statements notwendig. Wie geht man damit in der Praxis
> > vernuenftig um?

> Ich habe dazu getFromSqlOffset. Damit kannst du mehrere Objekte
> aus einem Record lesen. Beispiel:

> $SQL ="SELECT " ;
> $SQL.=xobj::getSQLGETTERM();
> $SQL.=aobj::getSQLGETTERM();
> $SQL.=" WHERE a.id=x.id" ....

> // ergibt:
> //SELECT x.x_id, x.foo, x.bar, x.blub, a.sepp, a.karl FROM x,a WHERE ...

Gefaellt mir stilistisch nicht, dazu zeichnen sich in meinem Kopf
auch noch Probleme mit einigen komplizierteren JOINs und Spalten
gleichen Namens ab, auf die ich aber noch nicht mit dem Finger
zeigen kann.

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich

Wilde Maeuse nimmt die Welt: Stefan!
(Sloganizer)
Stefan+Usenet [ Do, 08 November 2007 12:37 ] [ ID #1866018 ]

Re: Objekte und Datenbanken, best practice

On Mon, 05 Nov 2007 09:32:26 +0100 Christoph Herrmann wrote:
> > | class Kostenstelle {
> > | private $dbID;
> > | private $kennung;
> > | private $name;
> > | private $addresse;
> ...

> Ich würde ein assotiatives Array wählen. Hat den Vorteil dass eine
> Methode "set($key, $value)" und "get($key)" machen kannst und eine
> Basisklasse für alle Tabellenklasse, welche das implementiert.

Stimmt natuerlich, dafuer fallen Inkonsistenzen bzw. auch simple
Tippfehler nicht so schnell auf, als wenn man versucht, eine nicht
existente Property zu verwenden. Dazu kaeme IMHO der aesthetisch
negative Aspekt, dass auf unmittelbar in der Tabelle abgebildete
Eigenschaften via get($key) zugegriffen wird, auf davon abgeleitete
Eigenschaften aber ueber Methoden.

> > ein "new Kostenstelle(48)" sollte beim ersten Aufruf das zum
> > Datensatz 48 passende Objekt erstellen, beim zweiten Aufruf
> > hingegen einer Referenz auf das erste Objekt)? Oder ist das
> > schon von Grund auf ein verwerflicher Ansatz?

> Singleton wäre hier der Ansatz. Allerdings würde ich nicht jede
> Datenklasse zu einem Singleton machen, sondern eine zentrale
> Klasse, die alles verwaltet.

Zentrale Ansaetze wecken immer ein gewisses Gruseln bei mir. Mir ist
nur noch nicht ganz klar, wie ich den Singleton (von dem es ja per
Definition nur eine Instanz gibt) und die Objekte fuer die einzelnen
Tabellen (von denen es jeweils mehrere geben kann) elegant unter einen
Hut bekomme.

> Man sollte der Methode natürlich beliebig viele Parameter mitgeben
> können, da es auch zusammengesetzte Primary Keys geben kann.

Tabellen, die ich als Objekt abbilden moechte, haben samt und
sonders einen int(11) als primary key. n:m-Verknuepfungen und
1:n-Relationen ohne eigene "Identitaet" existieren im
Datenbankmodell natuerlich auch, werden aber im Programm nicht
als eigenstaendige Objekte auftauchen.

> > Durch die Kapselung wird das Programm zwar eleganter, dafuer
> > sind aber (je nach Objekt durchaus auch massiv) mehr
> > SELECT-Statements notwendig. Wie geht man damit in der Praxis
> > vernuenftig um?

> Warum mehr Select Statements? Du brauchst ja nicht mehr Daten,
> sondern tust das ganze nur kapseln.

Naja, wenn ich auf die Adresse der Kostenstelle zugreife, loese ich
dort in irgendeiner Methode ein SELECT aus. Bis dato wurden diese
Felder (so sie im Programm gebraucht wurden) gleich zu Beginn mit
einem JOIN in die Abfrage eingebunden.

> Zur Ausgabe würde ich diese Datenobjekte entweder über die getter
> auslesen

Na, die getter brauchen ihre Informationen aber auch irgendwoher...

> oder (wenn eine Ausgabe mehrfach benötigt werden kann) extra
> Viewer Klassen erstellen.

Das ist wahrscheinlich der Knackpunkt: wirklich intensives Nachladen
tritt vor allem bei Reporting auf, weniger beim Bearbeiten oder beim
Anzeigen einzelner Objekte. Und dort kann ich dann in der Tat mit
tabellenuebergreifenden Views arbeiten und die Objekte beiseite
lassen.

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich

Geht nicht!? Aber, aber - es gibt doch Stefan!
(Sloganizer)
Stefan+Usenet [ Do, 08 November 2007 12:39 ] [ ID #1866019 ]

Re: Objekte und Datenbanken, best practice

On Mon, 05 Nov 2007 09:11:51 +0100 Hadanite Marasek wrote:
> > der Code verwendet ein ausgefeiltes Template-System, um HTML, PDF,
> > CSV, XML, Excel... zu erzeugen. Bislang bekommen die Templates
> > jeweils spezifisch zugeschnittene, assoziative Arrays mit den
> > moeglicherweise fuer sie interessanten Werten uebergeben. [...]

> Hmmm....was erzeugst Du denn da?

Alles moegliche. Natuerlich viele Eingabeformulare und Ansichten bzw.
Ausgabelisten. Beim HTML kommen noch Status- und Fehlermeldungen, einige
wenige Popups und Sicherheitsabfragen dazu. Die anderen Formate sind
meist kundenspezifisch fuer den notwendigen Datenaustausch
zugeschnitten. Gerade dort waere eine einfachere, quasi applikationsweit
genormte Handhabung der verfuegbaren Daten sehr wuenschenswert. Objekte
duerften diesen Gedanken stark foerdern.

> [...] habe ich die Situation, dass ich Ausgaben brauche, deren
> Struktur nicht mehr 1:1 von SQL geliefert wird. Ich gehe dann hin und
> programmiere eine abstrakte "Zusammenstellungsklasse", die eine
> konkrete Ausgabe hat. Generell bin ich jemand, der eher weniger mit
> Templates arbeitet.

Templates werden halt dann interessant, wenn Du ein Programm fuer Kunden
mit teilweise sehr unterschiedlichen Anforderungen bezgl. Optik und
Schnittstellen, aber sehr aehnlichen Anforderungen bezgl. Betriebslogik
hast.

> Zumindest enthalten meine Templates nur noch Platzhalter und keinerlei
> Logik.

So fing das einmal an, ja. Im Lauf der Zeit tauchten dann zunehmend
Dinge auf wie "Statuscode und Rohdatenwerte an das Template uebergeben -
jeder kann sich daraus flexibel die von ihm bevorzugte Darstellung
basteln", wo vorher direkt die auszugebenden Daten aus den Rohdaten
errechnet und (teilweise bereits fertig formatiert) uebergeben wurden.

Oder nimm Timestamps: sinnvoll erscheint, einen UNIX-Timestamp an ein
Template zu uebergeben. Dieses macht damit dann, was es will, aber es
wird ihn, von pathologischen Faellen, niemals 1:1 ausgeben. In Smarty
kann das dann so einfach wie

| Die Frist endet am {$date|date_format:$_date}.

....sein, aber zum Beispiel auch - fuer die gleiche Variable $date -
etwas komplizierter:

| {if $date < $_now}Die Zeit ist abgelaufen.
| {elseif today($date)}Es wird schön langsam knapp.
| {else}Kein Grund zur Eile.
| {/if}

Und schon ist man dabei, die Naht zwischen Logik und Layout fuer sich
selbst immer weiter zu verschieben :-)

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich

Für kleine Schussel: Stefan - wenn der kleine Hunger plagt!
(Sloganizer)
Stefan+Usenet [ Do, 08 November 2007 12:47 ] [ ID #1866020 ]

Re: Objekte und Datenbanken, best practice

On Sun, 04 Nov 2007 19:08:17 +0100 Michael Fesser wrote:
> >a) es erscheint mir unsinnig, beim Aufruf des Konstruktors gleich
> >alle Daten aus der Datenbank zu holen. [...]

> Ich habe hier z.B. eine Klasse TProductDescriptor, welche ein
> Produkt eines Shopsystems beschreibt und alle dazugehörigen
> Informationen bereitstellt bzw. unter einem Hut zusammenfaßt. Die
> Informationen selbst sind in der DB über zahlreiche Tabellen
> verstreut (Produktdetails, Kategorien, Mengenrabatte, sonstige
> Attribute etc.) Die Descriptor-Klasse stellt all diese Infos über
> entsprechende Methoden bereit:
>
> ::getCategories()
> ::getClasses()
> ::getDiscounts()
> ::getAttributes()
>
> usw. Diese Methoden existieren aber nicht wirklich, sondern werden
> alle intern über __call() abgefangen und bearbeitet. Dadurch kann
> ich an einer zentralen Stelle darüber Buch führen, welche
> Informationen bereits geladen wurden und welche erst noch aus der
> DB geholt werden müssen. Was nicht gebraucht wird, wird auch nicht
> geladen.

Von der Idee gefaellt mir der Ansatz erst einmal ausgesprochen gut.
Allerdings fuehrt er zu den Klassen gleich noch eine weitere
Abstraktionsebene ein.

Wie gut ist denn das, was sich bei Dir hinter __call() verbirgt,
noch les- und wartbar? Du musst in dieser einen Funktion ja
letztlich die Logik fuer mehrere Methoden mehrerer Klassen
zumindestens einmal in den Grundzuegen abwickeln. (Hast Du irgendwo
ein konkretes Beispiel dafuer?)

Interessant waere halt, auch alle _existierenden_ Methoden vor ihrem
Aufruf abfangen zu koennen...

> Das gleiche erfolgt auch in umgekehrter Richtung - alle
> set*()-Gegenstücke zu obigen Methoden werden ebenfalls von
> __call() abgefangen und protokolliert. Somit werden später dann
> auch nur die Infos in die DB geschrieben, die geändert wurden.

Das kommt mir - wenigstens ein bisschen - so vor, als wuerde ich um
meinen bestehenden, prozeduralen Code, eine einzelne, grosse
Wrapper-Klasse herumlegen...

> >b) ist es weise, das Abspeichern nach Aenderungen in den
> >Destruktor zu verlagern?

> Ich würd's nicht machen,

Ok, davon bin ich in der Zwischenzeit ohnehin schon wieder
abgekommen.

> >d.h. ein "new Kostenstelle(48)" sollte beim ersten Aufruf das zum
> >Datensatz 48 passende Objekt erstellen, beim zweiten Aufruf
> >hingegen einer Referenz auf das erste Objekt)?

> Wenn für eine ID zu jedem Zeitpunkt nur eine einzige Instanz
> existieren soll, dann käme das Singleton-Pattern vielleicht ganz
> gelegen (Details dazu gibts im Archiv dieser Gruppe oder bei
> Google zuhauf). [...]
>
> $dbID = Kostenstelle::getInstance(48);

Fuer mich war ein Singleton immer etwas, von dem es nur eine Instanz
geben kann - was hier gerade nicht der Fall ist. Aber ja, genau so
ein Konstrukt wird es nun wohl werden. Die folgende Konstruktion:

| class Kostenstelle {
| private __construct() { }
|
| public static function getInstance() {
| return $this;
| }
| }

....sollte das dann leisten, oder?

> >Durch die Kapselung wird das Programm zwar eleganter, dafuer sind
> >aber (je nach Objekt durchaus auch massiv) mehr SELECT-Statements
> >notwendig. Wie geht man damit in der Praxis vernuenftig um?

> Eventuell versuchen, bestimmte SELECT-Anweisungen erst bei Bedarf
> abzusenden. Bei mehrfachem Absenden der gleichen Query können auch
> Prepared Statements der Performance zuträglich sein. Ansonsten
> bleibt nur testen, ob und wo die wirklichen Flaschenhälse sind.

Vermutlich gar nicht dort, wo mit einzelnen Objekten hantiert wird,
da hast Du recht. Fuer grosse Abfragen muss man ja nicht zwingend
mit dem Objektmodell arbeiten (zumal dann meist ohnehin einfach eine
Liste aus dem Ergebnis erstellt wird), sondern kann die Abfragen
auch direkt absetzen.

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich

Listig und doch eigenhändig?! Stefan - für romantische Stunden.
(Sloganizer)
Stefan+Usenet [ Do, 08 November 2007 12:51 ] [ ID #1866021 ]

Re: Objekte und Datenbanken, best practice

Stefan Froehlich schrieb:
> On Mon, 05 Nov 2007 09:38:10 +0100 Harald Stowasser wrote:
>> Alle DB-Klassen sind Subklassen von dbobject und bieten folgende
>> Schnittstelle: Vielleicht kannst du Dir ein paar Anregungen holen:
>
> Die eine oder andere sicherlich. Die Argumente beim Konstruktor
> variabel zu halten, ist eine nette Idee (eine Moeglichkeit, in PHP
> mit Typehinting von vornherein unterschiedliche Konstruktoren
> anbieten zu koennen, waere natuerlich noch schoener).

Jo, leider kann PHP keine Überladung von Funktionen.

> Weniger gut gefaellt mir persoenlich die Zerstueckelung von
> SQL-Befehlen. Da hat sich in der Vergangenheit (im Sinn von
> Wartbarkeit) sehr bewaehrt, die komplett beisammen zu lassen,
> notfalls auch ein wenig auf Kosten von Effizienz.

Dann benutze am besten gleich Stored Procedures. Ich hab mein Modell
sehr auf Performance und Flexibilität getrimmt. Das geht natürlich ein
wenig zu Lasten des 'Programmierkomforts'. :-)

Übrigens werden ja 90% der Fälle sowieso durch den 'PK-Konstruktor'
erledigt. Die SQL-Zerstückelung ist nur bei Komplizierteren Vorgängen
notwendig.
Und da achte ich auch darauf das möglichst kein SQL in den Controller
rutscht, sondern ich erstelle eine Komposition aus den beteiligen
Modell-Klassen. Die Benötigten Joins werden innerhalb dieser
Kompositionen gekapselt.

> Einige Bilder gibt es, aber die werden in getrenntem Code verwaltet.

Bilder sollten besser nicht in eine Datenbank!

> Aber: es gibt viele Verkettungen. Beim Beispiel Kostenstelle war es
> eine Adresse (die bei der Initialisierung der Kostenstelle dann
> ihrerseits nachgeladen wuerde), in Wahrheit ist es nicht bloss eine,
> sondern eine ganze Liste davon. Jede Adresse ist wiederum mit
> Personen verknuepft, diese mit Elementen der
> Organisationshierarchie, und so weiter.

Dafür dann die Kompositionen. Ich habe sogar ein eigenes Array-Objekt
das Temporäre-Handles benutzt. So können bereits in der Datenbank
stehende Objekte und neu Erstellte in einem Array behandelt werden.

> Das Nachladen soll mir nicht das _eine_ aktuelle Objekt
> beschleunigen, sondern primaer die zahlreichen anderen , die damit
> verknuepft sind (und auf die nur in wenigen Funktionen dann auch
> tatsaechlich zugegriffen wird).

Jo die kannst du innerhalb der Komposition auch gleichzeitig dazu
joinen: Beispiel aus einer meiner Kompositionen, Die eine M:N Beziehung
auflösst:

public function read( $map_type_id, $conn ) {
// Das Array für die patterns:
$this->patterns = new DbArray( "Map_pattern" );
// Prepared-Statement schon erzeugt?
if ( !isset( $conn->prep_MP ) ) {
$sql = $this->getSQLGET( ) . ' WHERE map_type_id=?';
$conn->prep_MP = $conn->prepare( $sql );
$conn->prep_MP->setFetchMode( PDO::FETCH_NUM );
}
// Den Parameter schreiben:
$conn->prep_MP->bindParam( 1, $map_type_id, PDO::PARAM_INT );
if ( !$conn->prep_MP->execute( ) ) {
throw new EDbObject( 10009, $map_type_id );
}
$result = $conn->prep_MP->fetchAll( );
}
$this->getFromSql( &$result[0] );
if ( isset( $result[0][Map_type::SQLCOUNTER] ) ) {
foreach ( $result as $value ) {
$this->readPat( &$value );
}
}

Das Treibende-Objekt steht hier zwar mehrmals in der Ergebnissmenge.
Trotzdem ist das noch viel schneller als mehrere Abfragen hintereinander
abzufeuern.

> Ok, ein Array (in seiner Funktion als Hash) kam mir dafuer als sehr
> einfache Loesung auch schon in den Sinn, ich wollte mir bloss noch
> weitere Meinungen dazu einholen.

Jo eine Pauschallösung innerhalb des ORM ist nicht gut. Da PHP sowieso
sehr viel Speicher braucht, und du so Niemals die Objekte zerstören
dürftest.
Bringen tut das außerdem eh nicht viel, da ein PHP-Prozess nur eine
Relativ kurze Lebensdauer hat. Und ein Hit im Speicher-Cache dadurch
eher unwahrscheinlich wird.

>> // ergibt:
>> //SELECT x.x_id, x.foo, x.bar, x.blub, a.sepp, a.karl FROM x,a WHERE ...
>
> Gefaellt mir stilistisch nicht, dazu zeichnen sich in meinem Kopf
> auch noch Probleme mit einigen komplizierteren JOINs und Spalten
> gleichen Namens ab, auf die ich aber noch nicht mit dem Finger
> zeigen kann.

Spalten mit gleichen namen gibt es nicht, da ich Aliase benutze. Die
Aliase sind durch den Prozess der das ORM erstellt eindeutig. Das oben
gezeigte SQL ist nur eine Vereinfachung in der Praxis sieht das dann
etwa so aus:

SELECT x_mp.pattern, x_mt.map_type, x_mpt.text, x_l.permalink ,m.rootid,
x_mcm.publish_date, x_mcm.publish_time ,
mpi.map_piece_id AS map_piece_id, mpi.changed AS changed,
mpi.width AS width, mpi.height AS height,
mpi.content_type AS content_type, mpi.path AS path,
mpi.protocol AS protocol, mpi.image_typ AS image_typ
FROM dev_orm.map m
LEFT JOIN dev_orm.map_piece_image mpi USING( map_piece_id )
LEFT JOIN dev_orm.map_piece_text x_mpt USING( map_piece_id )
LEFT JOIN dev_orm.web_permalink x_l ON x_l.foreign_key = m.map_id
AND foreign_table = 'map'
LEFT JOIN dev_orm.map_pattern x_mp USING (map_pattern_id)
LEFT JOIN dev_orm.map_type x_mt USING (map_type_id)
INNER JOIN myids x_mcm ON x_mcm.map_id = m.rootid
WHERE m.wastebasket='Nein' AND m.state='Angezeigt'
GROUP BY m.map_id
ORDER BY x_mcm.sort DESC, m.rootid, m.le
Harald Stowasser [ Do, 08 November 2007 14:22 ] [ ID #1866026 ]

Re: Objekte und Datenbanken, best practice

Stefan Froehlich schrieb:
>> usw. Diese Methoden existieren aber nicht wirklich, sondern werden
>> alle intern über __call() abgefangen und bearbeitet. Dadurch kann
>> ich an einer zentralen Stelle darüber Buch führen, welche
>> Informationen bereits geladen wurden und welche erst noch aus der
>> DB geholt werden müssen. Was nicht gebraucht wird, wird auch nicht
>> geladen.
>
> Von der Idee gefaellt mir der Ansatz erst einmal ausgesprochen gut.
> Allerdings fuehrt er zu den Klassen gleich noch eine weitere
> Abstraktionsebene ein.

Ich halte __call für Böse!

1: Es verhindert unterschiedliche Zugriffsprivilegien (Private, Public,
Protected).
2: Die Schnittstelle ist nicht definiert.
3: Reflection wird damit kaput gemacht.

>> Das gleiche erfolgt auch in umgekehrter Richtung - alle
>> set*()-Gegenstücke zu obigen Methoden werden ebenfalls von
>> __call() abgefangen und protokolliert. Somit werden später dann
>> auch nur die Infos in die DB geschrieben, die geändert wurden.

Das wird sowieso im setter gemacht ($this->ObChanged):

// Datenmodell:
public function getLon() {return this->voidGetter(self::posLon);}
public function setLon($lon) {$this->numSetter(self::posLon,$lon);}

// Superklasse:
protected function numSetter( $datapos, $wert ) {
if ( is_null( $wert ) || $wert == '' ) {
$wert = "0";
}
if ( !is_numeric( $wert ) ) {
throw new EDbObject( 10017 );
}
$this->voidSetter( $datapos, $wert );
}

protected function voidSetter( $datapos, $wert ) {
if ( !$this->daten ) {
$this->initData( $this->getSQLCOUNTER( ) );
}
if ( $this->daten[$datapos] != $wert ) {
$this->ObChanged = true;
$this->daten[$datapos] = $wert;
}
}

protected function voidGetter( $datapos ) {
$ret = $this->daten[$datapos];
return( $ret );
}

> Vermutlich gar nicht dort, wo mit einzelnen Objekten hantiert wird,
> da hast Du recht. Fuer grosse Abfragen muss man ja nicht zwingend
> mit dem Objektmodell arbeiten (zumal dann meist ohnehin einfach eine
> Liste aus dem Ergebnis erstellt wird), sondern kann die Abfragen
> auch direkt absetzen.

Diese Option ist immer gegeben ;-)
Harald Stowasser [ Do, 08 November 2007 14:39 ] [ ID #1866027 ]

Re: Objekte und Datenbanken, best practice

Stefan Froehlich schrieb:
> On Sun, 04 Nov 2007 19:08:17 +0100 Michael Fesser wrote:
>> Wenn für eine ID zu jedem Zeitpunkt nur eine einzige Instanz
>> existieren soll, dann käme das Singleton-Pattern vielleicht ganz
>> gelegen (Details dazu gibts im Archiv dieser Gruppe oder bei
>> Google zuhauf). [...]
>>
>> $dbID = Kostenstelle::getInstance(48);
>
> Fuer mich war ein Singleton immer etwas, von dem es nur eine Instanz
> geben kann - was hier gerade nicht der Fall ist. Aber ja, genau so
> ein Konstrukt wird es nun wohl werden. Die folgende Konstruktion:
>
> | class Kostenstelle {
> | private __construct() { }
> |
> | public static function getInstance() {
> | return $this;
> | }
> | }
>
> ...sollte das dann leisten, oder?

Nein, lies dir mal
http://www.professionelle-softwareentwicklung-mit-php5.de/er ste_auflage/design-patterns.creational-patterns.singleton.ht ml
durch, dass sollte Klarheit verschaffen :-)

Frohes Schaffen,
Christoph
Christoph Bersch [ Do, 08 November 2007 14:52 ] [ ID #1866028 ]

Re: Objekte und Datenbanken, best practice

Stefan Froehlich schrieb:
> Stimmt natuerlich, dafuer fallen Inkonsistenzen bzw. auch simple
> Tippfehler nicht so schnell auf, als wenn man versucht, eine nicht
> existente Property zu verwenden. Dazu kaeme IMHO der aesthetisch

Klar fallen die auf, zumindest wenn man E_NOTICE Meldungen angeschaltet
hat, was man natürlich machen sollte in der Entwicklung. Alternativ
kannst du auch in der get Methode erstmal per isset Abfragen, ob der Key
vorhanden ist und ansonsten eine Exception werfen. Steht ja jedem offen.


> negative Aspekt, dass auf unmittelbar in der Tabelle abgebildete
> Eigenschaften via get($key) zugegriffen wird, auf davon abgeleitete
> Eigenschaften aber ueber Methoden.

Kapier ich nicht was du willst. Auf alle Daten des Datenobjektes wird
per "get" zugegriffen. was sind denn abgeleitete Eigenschaften in dem
Bezug?

> Zentrale Ansaetze wecken immer ein gewisses Gruseln bei mir. Mir ist
> nur noch nicht ganz klar, wie ich den Singleton (von dem es ja per
> Definition nur eine Instanz gibt) und die Objekte fuer die einzelnen
> Tabellen (von denen es jeweils mehrere geben kann) elegant unter einen
> Hut bekomme.

Zentral hat den Vorteil, dass du den Zugriff an einer Stelle hast, die
Implementierung des Singleton auch nur einmal machen musst und die
einzelnen Datenklassen unabhängig davon auch (in anderen Projekten
vielleicht) ganz normal benutzen kannst ohne den Cache (nicht jeder will
den Arbeitsspeicher verballern).

Wenn der Begriff Singleton für dich hier irreführend ist, dann nimm
Multiton (gibts das eigentlich? ^^). Für mich heißt Singleton einfach
nur, dass ich die Anzahl der Instanzen kontrollieren kann (wird auch bei
Connection Pooling ja gemacht, wie nennt man es da denn?) und nicht
unbedingt dass es nur eine Instanz geben darf. Sorry falls ich da den
Begriff missbrauche.

Aber die Lösung ist ganz einfach, statt eine statische Variable mit der
Instanz der Klasse nimmt man ein Array mit den Instanzen aller möglichen
Objekte die es geben kann. Hier als Beispiel:

class CentralDataobject
{
private static $cache = array(); //<-- Geht das? Hab ich noch nie
gemacht... Ansonsten auf NULL setzen und in der statischen Methode zu
einem Array machen falls es NULL ist.

public static getDataobject($dataobject, $primarykey)
{
if(isset($cache[$dataobject][$primarykey]) == false)
{
$cache[$dataobject][$primarykey] = new $dataobject($primarykey);
}
return $cache[$dataobject][$primarykey];
}
}

Ich denke das Beispiel ist selbstsprechend, ansonsten einfach Bescheid
sagen, dann Kommentier ich es noch etwas. :)

> Tabellen, die ich als Objekt abbilden moechte, haben samt und
> sonders einen int(11) als primary key. n:m-Verknuepfungen und
> 1:n-Relationen ohne eigene "Identitaet" existieren im
> Datenbankmodell natuerlich auch, werden aber im Programm nicht
> als eigenstaendige Objekte auftauchen.

Dann sollte der obere Lösungsansatz für dich völlig ausreichend sein.

> Das ist wahrscheinlich der Knackpunkt: wirklich intensives Nachladen
> tritt vor allem bei Reporting auf, weniger beim Bearbeiten oder beim
> Anzeigen einzelner Objekte. Und dort kann ich dann in der Tat mit
> tabellenuebergreifenden Views arbeiten und die Objekte beiseite
> lassen.

Sobald es bei mir mehr als ein Datensatz wird oder über mehr als eine
Tabelle geht funktionieren meine Tabellenklassen nicht mehr. Ich sehe
diese nicht als Ersatz allen SQL Statements an, sondern als Hilfe für
die Verarbeitung einzelner Datensätze/Objekte an. Ich kann auf
einfachste Weise einen Großteil an Datenbankzugriffe abdecken und
einzelne komplexere Angelegenheiten werden dann direkt per Statement
abgedeckt.

--
Mit freundlichen Grüßen,
Christoph Herrmann

http://dragonprojects.de/
Christoph Herrmann [ Do, 08 November 2007 16:55 ] [ ID #1866037 ]

Re: Objekte und Datenbanken, best practice

Harald Stowasser schrieb:
> Bilder sollten besser nicht in eine Datenbank!

hast dafür auch einen Grund oder ist das nur eine Behauptung von dir
persönlich? Ersteres würde ich begrüßen, weil ich keinen Nachteil kenne
(außer die vielleicht aufwendigere Verarbeitung) aber den Vorteil, dass
die Daten dann alle in der Datenbank sind (Sicherung einheitlich) und je
nach System auch weniger Platz beanspruchen.

> Jo eine Pauschallösung innerhalb des ORM ist nicht gut. Da PHP sowieso
> sehr viel Speicher braucht, und du so Niemals die Objekte zerstören
> dürftest.

Je nachdem was einem wichtiger ist, Performance oder Speicher. Nur für
wen Performance elementar wichtig ist, sollte vielleicht über andere
Sprachen nachdenken, die hier etwas effizienter sind. Also ist ein Cache
an der Stelle eher nicht sinnvoll um eine Handvoll SQL Statements
weniger zu haben. Kommt aber drauf an wie viele Objekte gecacht werden
müssten im Verhältnis zur Menge der SQL Statements.

> Bringen tut das außerdem eh nicht viel, da ein PHP-Prozess nur eine
> Relativ kurze Lebensdauer hat. Und ein Hit im Speicher-Cache dadurch
> eher unwahrscheinlich wird.

Hit = 2 verschiedene Objekte mit gleichem Hash? Klar ist das
unwahrscheinlich, aber dadurch eine richtige tickende Zeitbombe die
jederzeit undefinierbaren Schaden anrichten kann. Also würde ich davon
doch eher abraten. Tabelle + Primary Keys beschreiben eindeutig einen
Datensatz und damit sollte er auch im Cache identifiziert werden.

--
Mit freundlichen Grüßen,
Christoph Herrmann

http://dragonprojects.de/
Christoph Herrmann [ Do, 08 November 2007 17:06 ] [ ID #1866039 ]

Re: Objekte und Datenbanken, best practice

Christoph Herrmann schrieb:
> Harald Stowasser schrieb:
>> Bilder sollten besser nicht in eine Datenbank!
>
> hast dafür auch einen Grund oder ist das nur eine Behauptung von dir
> persönlich?

Der Artikel ist zwar schon etwas älter, das Grundproblem bleibt jedoch
erhalten.
http://www.php-faq.de/q/q-db-blob.html

>> Bringen tut das außerdem eh nicht viel, da ein PHP-Prozess nur eine
>> Relativ kurze Lebensdauer hat. Und ein Hit im Speicher-Cache
>> dadurch eher unwahrscheinlich wird.
>
> Hit = 2 verschiedene Objekte mit gleichem Hash?

Nein. Mit Hit meinte ich, das ein Objekt auch wirklich schon im Speicher
vorhanden ist, und dann auch gefunden wird. Kollisionen sollte es bei
den PHP-Assoziativen-Hashlist-Arrays sowieso nicht geben.

Da in PHP Speicher nur von Request bis Response vorhanden ist, ist ein
einfacher Cache meist unsinnig. Da die meisten Datensätze während eines
Requests eh nur einmal geladen werden.

Natürlich kann man in Dateien Serialisieren, Memcache benutzen, oder
nach GLOBALS schreiben. Aber da handelt man sich wiederum andere
Probleme auf.

Das sind die Gründe warum IMHO ein cache nicht in das ORM sondern in den
Controller(MVC) oder ggf. in eine Komposition gehören.
Harald Stowasser [ Fr, 09 November 2007 09:13 ] [ ID #1866921 ]

Re: Objekte und Datenbanken, best practice

Harald Stowasser schrieb:

> Der Artikel ist zwar schon etwas älter, das Grundproblem bleibt jedoch
> erhalten.
> http://www.php-faq.de/q/q-db-blob.html

Der Artikel enthält zumindest einen Satz, der sachlich falsch ist:

,----------
| Leider ist es speziell bei MySQL so, dass keinerlei Mechanismen
| vorhanden sind, die die referentielle Integrität der Datenbank
| sicherstellen, sodass diese Sicherheit nicht wirklich gegeben ist.
`----------

Solche Mechanismen sind sehr wohl vorhanden, werden von mir auch genutzt
und funktionieren sogar mit MySQL 4.1 problemlos.

Gruß. Claus
Claus Reibenstein [ Fr, 09 November 2007 09:51 ] [ ID #1866924 ]

Re: Objekte und Datenbanken, best practice

Christoph Herrmann schrieb:
> Harald Stowasser schrieb:
>> Bilder sollten besser nicht in eine Datenbank!

> hast dafür auch einen Grund oder ist das nur eine Behauptung von dir
> persönlich? Ersteres würde ich begrüßen, weil ich keinen Nachteil kenne
> (außer die vielleicht aufwendigere Verarbeitung) aber den Vorteil, dass
> die Daten dann alle in der Datenbank sind (Sicherung einheitlich) und je
> nach System auch weniger Platz beanspruchen.

Oracle z.B. sieht das völlig anders. Dort gibt es sogar einen eigenen
Datentyp (ORDImage) für Bilder und jede Menge Funktionen zum Bearbeiten
von BLOBs.

<http://download-uk.oracle.com/docs/cd/B19306_01/appdev.102/b14302/ch_intr.htm#i610845>
<http://download-uk.oracle.com/docs/cd/B19306_01/appdev.102/b14297/ch_imgref.htm#i1059859>

Ich würde nicht so pauschal sagen, dass Bilder nicht in die Datenbank
gehören. Es gibt durchaus Szenarien in denen es sinnvoll ist, da sich
dadurch wie du schon sagst einige Vorteile ergeben.
dafox [ Fr, 09 November 2007 19:47 ] [ ID #1866970 ]

Re: Objekte und Datenbanken, best practice

On Thu, 08 Nov 2007 14:39:32 +0100 Harald Stowasser wrote:
> >> Diese Methoden existieren aber nicht wirklich, sondern werden
> >> alle intern über __call() abgefangen und bearbeitet. [...]

> > Von der Idee gefaellt mir der Ansatz erst einmal ausgesprochen gut.
> > Allerdings fuehrt er zu den Klassen gleich noch eine weitere
> > Abstraktionsebene ein.

> Ich halte __call für Böse!

> 1: Es verhindert unterschiedliche Zugriffsprivilegien (Private,
> Public, Protected).
> 2: Die Schnittstelle ist nicht definiert.
> 3: Reflection wird damit kaput gemacht.

Besonders Punkt 2 halte ich auf Dauer fuer problematisch. Mit den
zerstoerten Privilegien koennte ich noch eher umgehen, denke ich.

Und als Frage: will ich mich naeher mit Reflection auseinandersetzen?

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich

Studieren mit Stefan - manierlich werden mit Vorsicht.
(Sloganizer)
Stefan+Usenet [ So, 11 November 2007 09:11 ] [ ID #1868002 ]

Re: Objekte und Datenbanken, best practice

On Thu, 08 Nov 2007 14:52:22 +0100 Christoph Bersch wrote:
> >> $dbID = Kostenstelle::getInstance(48);

> > | class Kostenstelle {
> > | private __construct() { }
> > |
> > | public static function getInstance() {
> > | return $this;
> > | }
> > | }
> >
> > ...sollte das dann leisten, oder?

> Nein, lies dir mal
> http://www.professionelle-softwareentwicklung-mit-php5.de/er ste_auflage/design-patterns.creational-patterns.singleton.ht ml
> durch, dass sollte Klarheit verschaffen :-)

Na, weit weg war ich ja eh nicht... die Verwechslung von $this und
self zeigt allerdings recht deutlich, dass ich PHP5 bislang nur im
Trockentraining schreibe. Trotzdem danke fuer den Link, die Texte
sind sehr praezise und informativ.

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich

Stefan. Für deflorierte Gummis, bis es weh tut!
(Sloganizer)
Stefan+Usenet [ So, 11 November 2007 09:13 ] [ ID #1868003 ]

Re: Objekte und Datenbanken, best practice

On Thu, 08 Nov 2007 16:55:22 +0100 Christoph Herrmann wrote:
> > dafuer fallen Inkonsistenzen bzw. auch simple Tippfehler nicht
> > so schnell auf, als wenn man versucht, eine nicht existente
> > Property zu verwenden. Dazu kaeme IMHO der aesthetisch

> Klar fallen die auf, zumindest wenn man E_NOTICE Meldungen
> angeschaltet hat, was man natürlich machen sollte in der
> Entwicklung.

Erreichst Du 100% code coverage beim Entwickeln? In meiner Phantasie
nehme ich mir das immer wieder vor, in der Praxis dagegen...
besonders beim Aendern grosser Abschnitte bestehenden Codes ist das
ein Problem, und hier _wird_ viel bestehender Code radikal geaendert
werden.

> Alternativ kannst du auch in der get Methode erstmal per isset
> Abfragen, ob der Key vorhanden ist und ansonsten eine Exception
> werfen. Steht ja jedem offen.

Ok, das waere ein Weg (braucht dann aber wieder die Liste der
Properties in den Klassen, womit der fuer mich groesste Vorteil des
Arrays, die Bequemlichkeit, dahin ist - Kontrolle und Bequemlichkeit
schliessen sich naturgemaess aus).

> > dass auf unmittelbar in der Tabelle abgebildete Eigenschaften
> > via get($key) zugegriffen wird, auf davon abgeleitete
> > Eigenschaften aber ueber Methoden.

> Kapier ich nicht was du willst. Auf alle Daten des Datenobjektes
> wird per "get" zugegriffen. was sind denn abgeleitete
> Eigenschaften in dem Bezug?

Beispielsweise der Name der Benutzerklasse eines Benutzers - der
steht nicht mehr im Objekt User, sondern im Objekt UserLevel,
welches dort als Referenz gespeichert ist. Folglich braucht es fuer
die Klasse User, so man direkten Zugriff auf diesen Namen haben
moechte, eine Methode userLevel(). Es ist dann zu unterscheiden
zwischen:

| $user->name;
| $user->homedir;
| $user->useLevel();

Da verwende ich lieber gleich fuer alles Methoden.

> Wenn der Begriff Singleton für dich hier irreführend ist, dann
> nimm Multiton (gibts das eigentlich? ^^). Für mich heißt Singleton
> einfach nur, dass ich die Anzahl der Instanzen kontrollieren kann

Der Begriff Singleton war mir ohnehin neu, da ich meine Erfahrungen
mit OO vor 10 Jahren in C++ gesammelt habe. Irgendwie sind die
Moeglichkeiten bei PHP doch ganz andere (sowohl zum besseren, als
auch zum schlechteren), trotz grosser Aehnlichkeit auf den ersten
Blick.

> Sorry falls ich da den Begriff missbrauche.

Da er nicht von mir ist, weiss ich nicht, ob er missbraucht ist.
Singleton klingt halt nach _einer_ Instanz und wird auch auf
diversen Webseiten so erklaert. Aber die Erweiterung auf n
kontrollierte Instanzen ist programmiertechnisch nicht kompliziert.

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich

Stefan, so einfältig wie das Gewissen.
(Sloganizer)
Stefan+Usenet [ So, 11 November 2007 09:20 ] [ ID #1868004 ]

Re: Objekte und Datenbanken, best practice

On Thu, 08 Nov 2007 14:22:53 +0100 Harald Stowasser wrote:
> > Die Argumente beim Konstruktor variabel zu halten, ist eine nette
> > Idee (eine Moeglichkeit, in PHP mit Typehinting von vornherein
> > unterschiedliche Konstruktoren anbieten zu koennen, waere natuerlich
> > noch schoener).

> Jo, leider kann PHP keine Überladung von Funktionen.

Oder gar Operatoren...

Ich habe mir nun u.a. eine Klasse Date gestrickt, mit der ich
Zeitstempel in allen von mir benoetigten Formaten ein- und ausgeben
kann, konvertieren etc. Andere Klassen erkennen den Objekttyp, holen
sich das fuer sie richtige Format von selbst. Tolle Sache.

Nur: der _Vergleich_ zweier solcher Date-Objekte ist echt bescheuert.
Wie einfach waeren die Operatoren <, ==, > hier, aber nein. Statt dessen
darf ich entweder beide Objekte nach integer konvertieren:

| $date1->unixtimestamp() < $date2->unixtimestamp()

oder seltsame Methoden aufrufen:

| $date1.ealier($date2)
| $date2.later($date1)

Beides traegt fuer mich nicht dazu bei, die Lesbarkeit zu erhoehen.

> > Weniger gut gefaellt mir persoenlich die Zerstueckelung von
> > SQL-Befehlen. [...]

> Dann benutze am besten gleich Stored Procedures.

Steht im Hinterkopf als fester Programmpunkt. Einige 100 Teilprogramme
auf OO umzustellen, wird aber erst einmal ein wenig dauern... (und dann
quaele ich die MySQL-Gruppe damit, nicht diese hier).

> > Einige Bilder gibt es, aber die werden in getrenntem Code verwaltet.

> Bilder sollten besser nicht in eine Datenbank!

*seufz*

Hatte ich in einer frueheren Version. Ja, die Performance war aus dem
Filesystem deutlich besser. Die Handhabung des Gesamtpakets hat aber arg
gelitten. Momentan kann ich, wenn etwas falsch laeuft, mir einen Dump
der Datenbank schicken lassen, einspielen und damit 1:1 simulieren, was
sich am Produktivsystem tut. Dito fuer Snapshots bei Umstellungen, vor
kritischen Aktionen, etc. Sobald Daten ausgelagert sind, wird das alles
viel komplizierter.

Die anderen Anregungen wurden einmal dankend aufgenommen, naehere Fragen
tauchen fruehestens dann auf, wenn ich versuche, meine Ideen auch in die
Praxis umzusetzen... :-)

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich

Ein entschlossenes Team! Stefan, damit die Gefühle nicht leiden!
(Sloganizer)
Stefan+Usenet [ So, 11 November 2007 09:34 ] [ ID #1868005 ]

Re: Objekte und Datenbanken, best practice

Stefan Froehlich schrieb:

> On Thu, 08 Nov 2007 14:22:53 +0100 Harald Stowasser wrote:
>
>> Jo, leider kann PHP keine Überladung von Funktionen.
>
> Oder gar Operatoren...

Gibt es überhaupt eine OO-Sprache außer C++, die Operatoren überladen
kann? Java kann es schon mal nicht.

Gruß. Claus
Claus Reibenstein [ So, 11 November 2007 11:55 ] [ ID #1868011 ]

Re: Objekte und Datenbanken, best practice

Stefan Froehlich schrieb:
> On Thu, 08 Nov 2007 14:22:53 +0100 Harald Stowasser wrote:
>>> Die Argumente beim Konstruktor variabel zu halten, ist eine nette
>>> Idee (eine Moeglichkeit, in PHP mit Typehinting von vornherein
>>> unterschiedliche Konstruktoren anbieten zu koennen, waere natuerlich
>>> noch schoener).

>> Jo, leider kann PHP keine Überladung von Funktionen.

Das ist ja auch bei einer so schwach typisierten Sprache ziemlich
sinnlos. Du kannst das Überladen von Funktionen mit PHP simulieren,
allerdings wird das ziemlich unübersichtlich und sehr schwer zu
dokumentieren. Hier mal ein Beispiel:

Die Klasse "Socket" soll ein Client-Socket repräsentieren, welches mit
folgenden Konstruktoren erzeugt werden kann:

1. $s = new Socket();
2. $s = new Socket($host, $port);
3. $s = new Socket(new SocketAddress($host, $port));

class Socket {
public function __construct() {
$host = null;
$port = -1;

if(func_num_args() == 1) {
$addr = func_get_arg(0);

if($addr instanceof SocketAddress) {
$host = $addr->getHost();
$port = $addr->getPort();
}
}
elseif(func_num_args() == 2) {
$host = func_get_arg(0);
$port = func_get_arg(1);
}

if($host != null && $port > -1) {
$this->connect($host, $port);
}
}
}

Schön ist es nicht, aber es funktioniert genauso, als ob der Konstruktor
überladen wäre. Man könnte natürlich auch von $host und $port noch den
Typ abfragen, aber das würde zu Problemen führen, wenn der Port z.B. als
Request-Parameter importiert wurde.

> Ich habe mir nun u.a. eine Klasse Date gestrickt, mit der ich
> Zeitstempel in allen von mir benoetigten Formaten ein- und ausgeben
> kann, konvertieren etc. Andere Klassen erkennen den Objekttyp, holen
> sich das fuer sie richtige Format von selbst. Tolle Sache.
>
> Nur: der _Vergleich_ zweier solcher Date-Objekte ist echt bescheuert.
> Wie einfach waeren die Operatoren <, ==, > hier, aber nein. Statt dessen
> darf ich entweder beide Objekte nach integer konvertieren:

Was ich etwas komisch finde ist, dass es mit relativ primitiven Objekten
genau so funktioniert, wie du es dir vorstellst. Ich verstehe nur nicht
warum das funktioniert:

class PrimitiveDate {
private $timestamp;

public function __construct($timestamp) {
$this->timestamp = $timestamp;
}
}

$a = new PrimitiveDate(time());
$b = new PrimitiveDate(time() - 86400);

var_dump($a > $b); // false
var_dump($b < $a); // true
var_dump($a == $b); // false

> | $date1->unixtimestamp() < $date2->unixtimestamp()

> oder seltsame Methoden aufrufen:

> | $date1.ealier($date2)
> | $date2.later($date1)

Das ist syntaktisch korrekt und semantisch falsch :), PHP != Java.

Ich implementiere hier für vergleichbare Objekte wie bei Java ein
Interface "Comparable" mit der Methode compare($o), die dann eben wie
strcmp() -1, 0 oder 1 zurückgibt.

Für usort() etc. gibt es dann eine Funktion cmp($a, $b):

function cmp($a, $b) {
...

if($a instanceof Comparable && $b instanceof Comparable) {
return $a->compare($b);
}

...
}

Bei meinem Date-Objekt habe ich dann genau wie du die seltsamen Methoden
eingebaut: before(mcp_util_Date $date) und after(mcp_util_Date $date)
und halt die compare() Methode.

> Beides traegt fuer mich nicht dazu bei, die Lesbarkeit zu erhoehen.

ACK, jedoch ist m.E. schon klar, was an dieser Stelle im Code passiert.
Zumal du die Methoden ja dokumentierst.

Gibt es oder gab es nicht eine PECL-Erweiterung zum überladen von
Operatoren? Ulf hatte mal sowas erwähnt.

>>> Einige Bilder gibt es, aber die werden in getrenntem Code verwaltet.
>> Bilder sollten besser nicht in eine Datenbank!

-v

> Hatte ich in einer frueheren Version. Ja, die Performance war aus dem
> Filesystem deutlich besser. Die Handhabung des Gesamtpakets hat aber arg
> gelitten. Momentan kann ich, wenn etwas falsch laeuft, mir einen Dump
> der Datenbank schicken lassen, einspielen und damit 1:1 simulieren, was
> sich am Produktivsystem tut. Dito fuer Snapshots bei Umstellungen, vor
> kritischen Aktionen, etc. Sobald Daten ausgelagert sind, wird das alles
> viel komplizierter.

ACK. Es hat mehr Vorteile die Daten nicht zu zerstückeln, sondern eben
auch die Bilder in der DB zu halten. Um die Performance zu verbessern
kannst du ja einen Cachemechanismus verwenden.

Den FAQ-Artikel zu dem Thema kannst du IMNSHO getrost vergessen.
dafox [ So, 11 November 2007 13:18 ] [ ID #1868014 ]

Re: Objekte und Datenbanken, best practice

Stefan Froehlich schrieb:
> On Thu, 08 Nov 2007 14:39:32 +0100 Harald Stowasser wrote:
>
> Und als Frage: will ich mich naeher mit Reflection auseinandersetzen?

IMHO Ja.

1. Reflection ist auch für einen Compiler/Code-Optimierer vorhersehbar.
2. Es ist der OO-Weg auf nicht näher spezifizierte Klassen zuzugreifen.
3. Generische Programmierung könnte man auch mit eval oder
Klammer-{}-wursteleien machen. Aber: "Eval is evil"
Harald Stowasser [ Mo, 12 November 2007 08:48 ] [ ID #1868666 ]

Re: Objekte und Datenbanken, best practice

Stefan Froehlich schrieb:
> Erreichst Du 100% code coverage beim Entwickeln? In meiner Phantasie
> nehme ich mir das immer wieder vor, in der Praxis dagegen...
> besonders beim Aendern grosser Abschnitte bestehenden Codes ist das
> ein Problem, und hier _wird_ viel bestehender Code radikal geaendert
> werden.

Wenn du damit meinst, ob ich meinen Code fehlerfrei halten kann und
keine Notice Meldungen bekomme, dann natürlich. Wer will schon
fehlerhaften Code haben, das ist doch das erste was man behebt bevor
etwas neues kommt.

> Ok, das waere ein Weg (braucht dann aber wieder die Liste der
> Properties in den Klassen, womit der fuer mich groesste Vorteil des
> Arrays, die Bequemlichkeit, dahin ist - Kontrolle und Bequemlichkeit
> schliessen sich naturgemaess aus).

Für mich ist es schön, weil das Array und den Zugriff darauf in einer
Basisklasse definiert ist und ich in einer abgeleiteten Klasse für die
Tabelle nur noch definieren muss, welche Spalten erlaubt sind.

> Beispielsweise der Name der Benutzerklasse eines Benutzers - der
> steht nicht mehr im Objekt User, sondern im Objekt UserLevel,
> welches dort als Referenz gespeichert ist. Folglich braucht es fuer
> die Klasse User, so man direkten Zugriff auf diesen Namen haben
> moechte, eine Methode userLevel(). Es ist dann zu unterscheiden
> zwischen:
>
> | $user->name;
> | $user->homedir;
> | $user->useLevel();
>
> Da verwende ich lieber gleich fuer alles Methoden.

$user->get("name");
$user->get("homedir");
$user->useLevel();

da hätte ich auch für alles Methoden. Es gibt ein Unterschied zwischen
einer normalen "get()" Methode und der interceptor Methode "__get()".
Letzteres kann ich nicht mehr empfehlen, hab ich mir schwere Bugs rein
gehauen damit.

> Da er nicht von mir ist, weiss ich nicht, ob er missbraucht ist.
> Singleton klingt halt nach _einer_ Instanz und wird auch auf
> diversen Webseiten so erklaert. Aber die Erweiterung auf n
> kontrollierte Instanzen ist programmiertechnisch nicht kompliziert.

Deswegen wird es gerne gemacht, vor allem bei Dingen wie Connection
Pooling (was in PHP wohl nicht benötigt wird, aber in Java oder in
Desktop Programmen sehr wohl).

--
Mit freundlichen Grüßen,
Christoph Herrmann

http://dragonprojects.de/
Christoph Herrmann [ Mo, 12 November 2007 09:02 ] [ ID #1868667 ]

Re: Objekte und Datenbanken, best practice

Thomas Hamacher schrieb:
> Christoph Herrmann schrieb:
>> Harald Stowasser schrieb:
>>> Bilder sollten besser nicht in eine Datenbank!
>
> Oracle z.B. sieht das völlig anders. Dort gibt es sogar einen eigenen
> Datentyp (ORDImage) für Bilder und jede Menge Funktionen zum Bearbeiten
> von BLOBs.

Nur kann Apache bislang nicht direkt auf Oracle zugreifen, und einen
Stream direkt ausliefern. Deshalb bleibt immer noch unperformante Umweg
über einen "hol mir mal jenes Bild aus der DB"-Thread.

Dieser Datentyp ist bei Client-Server-Strukturen Super! Nur wenn Oracle
als Backend hinter einem Webserver läuft, wirst du kaum an die
Geschwindigkeit eines direkten Dateizugriffs rankommen.

Extremfall währen z.B. Videos. Willst du die auch in die DB stecken wenn
sie über den Apachen ausgeliefert werden müssen?

> <http://download-uk.oracle.com/docs/cd/B19306_01/appdev.102/b14302/ch_intr.htm#i610845>
> <http://download-uk.oracle.com/docs/cd/B19306_01/appdev.102/b14297/ch_imgref.htm#i1059859>
>
> Ich würde nicht so pauschal sagen, dass Bilder nicht in die Datenbank
> gehören. Es gibt durchaus Szenarien in denen es sinnvoll ist, da sich
> dadurch wie du schon sagst einige Vorteile ergeben.

Pauschalisierte Aussagen sind immer schlecht :-)

Ich erinnere mich an einen Fall da hatte der Programmierer auf der
Datenbank mehr Speicher zur Verfügung als auf seinen Root-Server.

Da kann man sowas schon machen. Dafür bekam er dann halt Probleme beim
Backup. :-)
Harald Stowasser [ Mo, 12 November 2007 09:15 ] [ ID #1868668 ]

Re: Objekte und Datenbanken, best practice

Claus Reibenstein schrieb:
> Stefan Froehlich schrieb:
>
>> On Thu, 08 Nov 2007 14:22:53 +0100 Harald Stowasser wrote:
>>
>>> Jo, leider kann PHP keine Überladung von Funktionen.
>> Oder gar Operatoren...
>
> Gibt es überhaupt eine OO-Sprache außer C++, die Operatoren überladen
> kann? Java kann es schon mal nicht.

Python, Ruby und Visual Basic fallen mir da spontan ein.
Harald Stowasser [ Mo, 12 November 2007 09:18 ] [ ID #1868669 ]

Re: Objekte und Datenbanken, best practice

Stefan Froehlich schrieb:

> Ich habe mir nun u.a. eine Klasse Date gestrickt, mit der ich
> Zeitstempel in allen von mir benoetigten Formaten ein- und ausgeben
> kann, konvertieren etc. Andere Klassen erkennen den Objekttyp, holen
> sich das fuer sie richtige Format von selbst. Tolle Sache.

Denk mal über folgenden Weg nach:
Die Ausgabe des Datums wird über ein Strategie-Pattern gemacht. So
kannst du dein Programm erweitern, ohne die Date-Klasse anzufassen. Die
Modifier können außerdem auch an anderen Stellen, die gar nix mit der
Date-Klasse zu tun haben benutzt werden.
Vergleiche oÄ kommen in die Date-Klasse und benutzen dort auch nur den
Standard-Typ.

Kleines Beispiel:

class Date{
protected $date;
public getDate(Modifier $mod=null){
if ($mod){
return $mod->modify($this->date);
}
return $this->date;
}
public compare(Date $otherDate){
return $this->date == $otherDate->date;
}
...
}

>
> Nur: der _Vergleich_ zweier solcher Date-Objekte ist echt bescheuert.
> Wie einfach waeren die Operatoren <, ==, > hier, aber nein. Statt dessen
> darf ich entweder beide Objekte nach integer konvertieren:
>
> | $date1->unixtimestamp() < $date2->unixtimestamp()

Schlecht.

> oder seltsame Methoden aufrufen:

Besser :-)

> Beides traegt fuer mich nicht dazu bei, die Lesbarkeit zu erhoehen.

ACK. Wobei die seltsamen Methoden noch der bessere Kompromiss sind :-)



Gruß
Harald.
Harald Stowasser [ Mo, 12 November 2007 09:33 ] [ ID #1868670 ]

Re: Objekte und Datenbanken, best practice

On Sun, 11 Nov 2007 13:18:14 +0100 Thomas Hamacher wrote:
> > Nur: der _Vergleich_ zweier solcher Date-Objekte ist echt
> > bescheuert. Wie einfach waeren die Operatoren <, ==, > hier, aber
> > nein. Statt dessen darf ich entweder beide Objekte nach integer
> > konvertieren:

> Was ich etwas komisch finde ist, dass es mit relativ primitiven
> Objekten genau so funktioniert, wie du es dir vorstellst.

Jessas.

Danke fuer den Hinweis, das habe ich (weil ja nicht sein kann, was nicht
sein darf) gar nicht erst versucht. Meine Datumsklasse verwendet nur den
timestamp als Klassenvariable, ergo klappt das bei mir auch. Heureka!

> Ich verstehe nur nicht warum das funktioniert:

Ein paar Experimente zeigen bei mir recht schnell ein Verhalten, das
analog zum Abschnitt "Comparing objects" funktioniert:

| When using the comparison operator (==), object variables are compared
| in a simple manner, namely: Two object instances are equal if they have
| the same attributes and values, and are instances of the same class.

PHP geht offenbar die einzelnen Attribute der Reihe nach durch und
vergleicht sie. Sind alle gleich, trifft == zu, gibt es Unterschiede,
so entscheidet der erste Unterschied, ob < oder > der Fall ist.

Ah. Und sieht man nicht bei "Comparing objects" nach, sondern bei "Comparison
operators", dann findet man

| Built-in classes can define its own comparison, different classes are
| uncomparable, same class - compare properties the same way as arrays
| (PHP 4), PHP 5 has its own explanation

Die "own explanation" ist wiederum die Seite "Comparing objects", die
auf > und < nicht naeher eingeht. Vermutlich wird hier immer noch das
Verhalten von PHP4 als Fallback verwendet - fragt sich nur, ob man sich
darauf als Programmierer verlassen kann oder soll. Schoen waere es schon.

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich

Stefan - deiner fleucht fauler oder auch innovativer.
(Sloganizer)
Stefan+Usenet [ Mo, 12 November 2007 10:10 ] [ ID #1868672 ]

Re: Objekte und Datenbanken, best practice

Harald Stowasser schrieb:
> Thomas Hamacher schrieb:
>> Christoph Herrmann schrieb:
>>> Harald Stowasser schrieb:
>>>> Bilder sollten besser nicht in eine Datenbank!
>> Oracle z.B. sieht das völlig anders. Dort gibt es sogar einen eigenen
>> Datentyp (ORDImage) für Bilder und jede Menge Funktionen zum Bearbeiten
>> von BLOBs.

> Nur kann Apache bislang nicht direkt auf Oracle zugreifen, und einen
> Stream direkt ausliefern. Deshalb bleibt immer noch unperformante Umweg
> über einen "hol mir mal jenes Bild aus der DB"-Thread.

Also mit "mod_plsql" kannst du direkt vom Webserver auf die Datenbank
zugreifen. Das nützt jetzt zwar in einer PHP-Anwendung nichts, aber
grundsätzlich geht es.

Mit PHP würde ich (gerade bei Bildern) sowieso nicht einen weiteren
Thread starten (Request auf image.php?id=x), sondern das Bild direkt
beim Laden der Datensätze im Dateisystem des Webservers cachen.

<?php
$result = mysql_query('...');
$records = array();

while($record = mysql_fetch_assoc()) {
// Bild erzeugen (Fehlerhandling etc fehlt)
file_set_contents('./images/' . $record['id'] . '.jpg',
$record['image']);

// Pfad zum Bild speichern
$records[] = array(
'id' => $record['id'],
'text' => $record['text'],
'image' => './images/' . $record['id'] . '.jpg'
);
}
?>

....

<?php foreach($records as $record): ?>
<p>
<img src="<?php echo $record['image'] ?>" align="left">
<?php echo $record['text'] ?>
</p>
<?php endforeach ?>

> Dieser Datentyp ist bei Client-Server-Strukturen Super! Nur wenn Oracle
> als Backend hinter einem Webserver läuft, wirst du kaum an die
> Geschwindigkeit eines direkten Dateizugriffs rankommen.

So schnell wie ein Direktzugriff wird es nicht sein, aber bisher ist das
ausreichend performant und es ist der empfohlene Weg von Oracle. Es geht
ja hier auch weniger darum, so performant wie möglich zu werden, sondern
vielmehr darum, Inkonsistenzen zu vermeiden, Backups zu vereinfachen,
Replikation zu ermöglichen, etc.

> Extremfall währen z.B. Videos. Willst du die auch in die DB stecken wenn
> sie über den Apachen ausgeliefert werden müssen?

Wenn es im konkreten Fall Sinn macht, dann will ich das durchaus. Das
kann ich so pauschal nicht beantworten. Oracle kennt dafür ORDVideo :)

>> Ich würde nicht so pauschal sagen, dass Bilder nicht in die Datenbank
>> gehören. Es gibt durchaus Szenarien in denen es sinnvoll ist, da sich
>> dadurch wie du schon sagst einige Vorteile ergeben.

> Pauschalisierte Aussagen sind immer schlecht :-)

ACK.

> Ich erinnere mich an einen Fall da hatte der Programmierer auf der
> Datenbank mehr Speicher zur Verfügung als auf seinen Root-Server.

Hier haben die meisten Datenbank-Server auch mehr Speicher und CPU als
die Webserver (größtes System mit 32GB RAM). Was will der Web-Server
auch mit so viel Speicher?

> Da kann man sowas schon machen. Dafür bekam er dann halt Probleme beim
> Backup. :-)

Wieso das? Die Backups sind dann doch viel einfacher zu handhaben, als
wenn ich meine Daten an mehreren Stellen suchen muss. Die Datenmenge
ändert sich ja nicht.
dafox [ Mo, 12 November 2007 11:27 ] [ ID #1868673 ]

Re: Objekte und Datenbanken, best practice

Harald Stowasser schrieb:
> Claus Reibenstein schrieb:
>> Stefan Froehlich schrieb:
>>> On Thu, 08 Nov 2007 14:22:53 +0100 Harald Stowasser wrote:

>>>> Jo, leider kann PHP keine Überladung von Funktionen.
>>> Oder gar Operatoren...

>> Gibt es überhaupt eine OO-Sprache außer C++, die Operatoren überladen
>> kann? Java kann es schon mal nicht.

> Python, Ruby und Visual Basic fallen mir da spontan ein.

Ada, C-Sharp, D, Delphi, Eiffel, F-Sharp, Perl, Smalltalk ...
dafox [ Mo, 12 November 2007 11:44 ] [ ID #1868674 ]

Re: Objekte und Datenbanken, best practice

Stefan Froehlich schrieb:
> On Sun, 11 Nov 2007 13:18:14 +0100 Thomas Hamacher wrote:
>>> Nur: der _Vergleich_ zweier solcher Date-Objekte ist echt
>>> bescheuert. Wie einfach waeren die Operatoren <, ==, > hier, aber
>>> nein. Statt dessen darf ich entweder beide Objekte nach integer
>>> konvertieren:

>> Was ich etwas komisch finde ist, dass es mit relativ primitiven
>> Objekten genau so funktioniert, wie du es dir vorstellst.

> Danke fuer den Hinweis, das habe ich (weil ja nicht sein kann, was nicht
> sein darf) gar nicht erst versucht. Meine Datumsklasse verwendet nur den
> timestamp als Klassenvariable, ergo klappt das bei mir auch. Heureka!

Gern geschehen. Aber trotzdem hätte ich meine Bedenken das so zu verwenden.

>> Ich verstehe nur nicht warum das funktioniert:

> PHP geht offenbar die einzelnen Attribute der Reihe nach durch und
> vergleicht sie. Sind alle gleich, trifft == zu, gibt es Unterschiede,
> so entscheidet der erste Unterschied, ob < oder > der Fall ist.

Ja, danke dafür. Das ist nur scheinbar (für PHP5) nicht dokumentiert.

> [...]

> Die "own explanation" ist wiederum die Seite "Comparing objects", die
> auf > und < nicht naeher eingeht. Vermutlich wird hier immer noch das
> Verhalten von PHP4 als Fallback verwendet - fragt sich nur, ob man sich
> darauf als Programmierer verlassen kann oder soll. Schoen waere es schon.

Also selbst wenn man sich drauf verlassen kann, willst du das wirklich
nutzen?

Wenn du irgendwann mal die Klasse erweitern oder ableiten willst und ein
Property ergänzt, dann kracht es überall da, wo du dich darauf verlassen
hast, dass "Date" genau ein Property hat oder das das zweite Property
bei allen Date-Objekten gleich ist. Und das schlimme daran ist, dass man
die daraus resultierenden Fehler kaum findet.

Auf der anderen Seite ist $a < $b schneller zu tippen als $a->before($b)
und würde an der Stelle auch Sinn machen. Ob es den Code besser lesbar
macht mag ich zu bezweifeln, weil jeder PHP-Entwickler an dieser Stelle
wahrscheinlich erstmal stutzt :)
dafox [ Mo, 12 November 2007 18:55 ] [ ID #1868677 ]

Re: Objekte und Datenbanken, best practice

> Alles moegliche. Natuerlich viele Eingabeformulare und Ansichten bzw.
> Ausgabelisten. Beim HTML kommen noch Status- und Fehlermeldungen, einige
> wenige Popups und Sicherheitsabfragen dazu. Die anderen Formate sind
> meist kundenspezifisch fuer den notwendigen Datenaustausch
> zugeschnitten. Gerade dort waere eine einfachere, quasi applikationsweit
> genormte Handhabung der verfuegbaren Daten sehr wuenschenswert. Objekte
> duerften diesen Gedanken stark foerdern.

Also ich habe eine Formularklasse, die mit generischen Models arbeitet.
Wie erwähnt, ist die spezifische Gestaltung schwierig, aber es ist
schnell & sicher. Bestens geeignet für Backends und Applikationen, die
man primär "haben", und nicht "schön haben" möchte.

> Templates werden halt dann interessant, wenn Du ein Programm fuer Kunden
> mit teilweise sehr unterschiedlichen Anforderungen bezgl. Optik und
> Schnittstellen, aber sehr aehnlichen Anforderungen bezgl. Betriebslogik
> hast.

Ist mir klar.

>> Zumindest enthalten meine Templates nur noch Platzhalter und keinerlei
>> Logik.

> | Die Frist endet am {$date|date_format:$_date}.
>
> ...sein, aber zum Beispiel auch - fuer die gleiche Variable $date -
> etwas komplizierter:
>
> | {if $date < $_now}Die Zeit ist abgelaufen.
> | {elseif today($date)}Es wird schön langsam knapp.
> | {else}Kein Grund zur Eile.
> | {/if}
>
> Und schon ist man dabei, die Naht zwischen Logik und Layout fuer sich
> selbst immer weiter zu verschieben :-)

Ich halte das vom jetzigen Standpunkt her für Schwa...ähm, für nicht so
sinnvoll. Ich verstehe den Ansatz & den Grund, aber letzlich führt das
doch nur dazu, dass ich X Ansatzpunkte habe, wo Logik sein kann, und
mithin Fehler. Und das ganze noch weitaus hässlicher als PHP selber,
wenn ich mir das obige Beispiel anschaue (da kann ich auch gleich mit
<?php ?> arbeiten).

--
Mein Zeugs:
http://www.hadanite-marasek.de/classes.php
http://www.objektivsuche.de/
Ansonsten:
http://www.php-faq.de/q/q-newsgroup-wie-helfen.html
Hadanite Marasek [ Mo, 12 November 2007 19:08 ] [ ID #1868678 ]

Re: Objekte und Datenbanken, best practice

On Mon, 12 Nov 2007 18:55:48 +0100 Thomas Hamacher wrote:
> > Vermutlich wird hier immer noch das Verhalten von PHP4 als Fallback
> > verwendet - fragt sich nur, ob man sich darauf als Programmierer
> > verlassen kann oder soll. Schoen waere es schon.

> Also selbst wenn man sich drauf verlassen kann, willst du das wirklich
> nutzen?

Ja, durchaus.

> Wenn du irgendwann mal die Klasse erweitern oder ableiten willst und
> ein Property ergänzt, dann kracht es überall [...]

Wenn ich so etwas mache, dann nur bei relativ klar definierten Klassen -
ansonsten sind die Vergleichsoperatoren ja ohnehin sinnlos. Eine gewisse
Restgefahr bleibt, aber beim Datum halte ich sie fuer ueberschaubar (was
koennte da noch dazukommen, das den Timestamp - der an erster Stelle
stehen bleibt - seiner Funktion als Sortierkriterium beraubt?).

> Auf der anderen Seite ist $a < $b schneller zu tippen als
> $a->before($b) und würde an der Stelle auch Sinn machen. Ob es den
> Code besser lesbar macht mag ich zu bezweifeln

Bei mir ist es genau umgekehrt: ich tippe so schnell, dass die paar
Zeichen nicht entscheidend sind. Aber ich _erfasse_ den Code einfach
besser, wenn er sich halbwegs algebraisch verhaelt. Und da ich auch in
Zukunft wohl der Hauptbetroffene sein werde, der damit leben muss... :)

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich

Stefan - Für Internetler unverzichtbar: Pfeifen damit es zupft!
(Sloganizer)
Stefan+Usenet [ Mo, 12 November 2007 19:24 ] [ ID #1868679 ]

Re: Objekte und Datenbanken, best practice

Stefan Froehlich schrieb:
> On Mon, 12 Nov 2007 18:55:48 +0100 Thomas Hamacher wrote:
>>> Vermutlich wird hier immer noch das Verhalten von PHP4 als Fallback
>>> verwendet - fragt sich nur, ob man sich darauf als Programmierer
>>> verlassen kann oder soll. Schoen waere es schon.

>> Wenn du irgendwann mal die Klasse erweitern oder ableiten willst und
>> ein Property ergänzt, dann kracht es überall [...]

> Wenn ich so etwas mache, dann nur bei relativ klar definierten Klassen -
> ansonsten sind die Vergleichsoperatoren ja ohnehin sinnlos. Eine gewisse
> Restgefahr bleibt, aber beim Datum halte ich sie fuer ueberschaubar (was
> koennte da noch dazukommen, das den Timestamp - der an erster Stelle
> stehen bleibt - seiner Funktion als Sortierkriterium beraubt?).

Dazu kommen könnte theoretisch jede Menge (Zeitzone, Kalender, Format,
etc), aber es wird wohl kaum Einfluss auf die Sortierreihenfolge haben,
stimmt schon. Dann fällt aber == raus.

>> Auf der anderen Seite ist $a < $b schneller zu tippen als
>> $a->before($b) und würde an der Stelle auch Sinn machen. Ob es den
>> Code besser lesbar macht mag ich zu bezweifeln

> Bei mir ist es genau umgekehrt: ich tippe so schnell, dass die paar
> Zeichen nicht entscheidend sind.

Angeber :p
dafox [ Mo, 12 November 2007 19:40 ] [ ID #1868681 ]

Re: Objekte und Datenbanken, best practice

Stefan Froehlich schrieb:
> Wenn ich so etwas mache, dann nur bei relativ klar definierten Klassen -
> ansonsten sind die Vergleichsoperatoren ja ohnehin sinnlos. Eine gewisse
> Restgefahr bleibt, aber beim Datum halte ich sie fuer ueberschaubar (was
> koennte da noch dazukommen, das den Timestamp - der an erster Stelle
> stehen bleibt - seiner Funktion als Sortierkriterium beraubt?).

Es bleibt so lange überschaubar, wie niemand anderes auf die Idee kommt
eine Membervariable anzulegen und nicht daran denkt, dass er auf diese
spezielle Begebenheit aufpassen muss. Verwendbar ja, aber dann doch
bitte gut Dokumentieren und darauf hinweisen.

> Bei mir ist es genau umgekehrt: ich tippe so schnell, dass die paar
> Zeichen nicht entscheidend sind. Aber ich _erfasse_ den Code einfach
> besser, wenn er sich halbwegs algebraisch verhaelt. Und da ich auch in
> Zukunft wohl der Hauptbetroffene sein werde, der damit leben muss... :)

Echt? Also ich finde Quellcode wesentlich einfacher zu lesen wenn ich
die API Dokumentation der Methoden angezeigt bekomme durch die IDE und
lesen kann, was eine Methode macht bzw. es allein über den Namen sehe.

Ich persönliche sehe keinen Vorteil darin, aber das dürfte wohl
Geschmackssache sein.

--
Mit freundlichen Grüßen,
Christoph Herrmann

http://dragonprojects.de/
Christoph Herrmann [ Mo, 12 November 2007 19:46 ] [ ID #1868682 ]

Re: Objekte und Datenbanken, best practice

Hadanite Marasek schrieb:
>>> Zumindest enthalten meine Templates nur noch Platzhalter und keinerlei
>>> Logik.

>> | Die Frist endet am {$date|date_format:$_date}.

>> Und schon ist man dabei, die Naht zwischen Logik und Layout fuer sich
>> selbst immer weiter zu verschieben :-)

> Ich halte das vom jetzigen Standpunkt her für Schwa...ähm, für nicht so
> sinnvoll. Ich verstehe den Ansatz & den Grund, aber letzlich führt das
> doch nur dazu, dass ich X Ansatzpunkte habe, wo Logik sein kann, und
> mithin Fehler.

Ich trenne hier Anwendungs- und Präsentationslogik. Es geht mir dabei
weniger darum, ob die Logik nur an einer Stelle ist oder nicht, sondern
eher das sie da ist, wo sie hingehört :)

Im Template ist ein

<?php foreach($articles as $article): ?>
<h3><?php echo $article->title ?></h3>
<p><?php echo $article->body ?></p>
<?php endforeach ?>

oder

<c:foreach from=${articles} item="article">
<h3>${article.title}</h3>
<p>${article.body}</p>
</c:foreach>

besser aufgehoben (und wird sogar von den meisten Editoren entsprechend
hervorgehoben), als wenn ich in meinem Anwendungscode etwas wie

foreach($articles as $article) {
$loop = $template->addLoop('articles');
$loop->article = $article;
}

echo $template->fetch();

stehen hab und dann im Template

{articles}
<h3>{article.title}</h3>
<p>{article.body}</p>
{/articles}

Wenn jetzt die Artikel nebeneinander in zwei Spalten dargestellt werden
sollen, kannst du wieder den Code an beiden Stellen anfassen. Mit PHP
oder Smarty wird das im Template erledigt.

> Und das ganze noch weitaus hässlicher als PHP selber,

Klar, das ist ja auch Smarty. Das wird noch besser, wenn du im Template
auf Objekte zugreifen willst und "Modifier" (wie in Stefans Beispiel
date_format) verwendest:

{$user->getBirthdate()|date_format:"%d.%m.%y"}

> wenn ich mir das obige Beispiel anschaue (da kann ich auch gleich mit
> <?php ?> arbeiten).

Jo, warum auch nicht? Smarty macht allerdings mehr, als bloss eine
häßliche Syntax bereitstellen, die dann 1:1 in äquivalenten PHP-Code
übersetzt wird.
dafox [ Mo, 12 November 2007 20:04 ] [ ID #1868683 ]

Re: Objekte und Datenbanken, best practice

On Mon, 12 Nov 2007 08:48:58 +0100 Harald Stowasser wrote:
> > Und als Frage: will ich mich naeher mit Reflection auseinandersetzen?

> IMHO Ja.

> 1. Reflection ist auch für einen Compiler/Code-Optimierer vorhersehbar.
> 2. Es ist der OO-Weg auf nicht näher spezifizierte Klassen zuzugreifen.

Die Frage war noch allgemeiner zu verstehen. Was Reflection kann, ist
mir (so halbwegs) klar. Weniger klar ist mir bislang, was ich damit
eigentlich _tun_ moechte.

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich

Stefan - Liebe, die nimmermehr beerbt.
(Sloganizer)
Stefan+Usenet [ Di, 13 November 2007 12:06 ] [ ID #1869623 ]
PHP » de.comp.lang.php.misc » Objekte und Datenbanken, best practice

Vorheriges Thema: Vor- und Nachteile von "__autoload"?
Nächstes Thema: Fehlermeldung bei in_array()