| | | | |
| |||||||
| Tutorials Hier findest Du Tutorials, welche nach und nach ein fertiges Script ergeben. Sehen, lernen & verstehen! |
|
| | LinkBack | Themen-Optionen | Thema bewerten |
| | |
| Moderator Registriert seit: 11.05.2008
Beiträge: 6.069
![]() ![]() ![]() ![]() ![]() ![]() ![]() | Hallo, ich greife das Thema OR-Mapping mal wieder auf, da ich einige Verbesserungen zum vorherigen Thread: http://www.php.de/tutorials/54558-tu...l-und-oop.html (Tutorial: PHP/MySQL und OOP) vorgenommen habe. Dabei unterteile ich wieder in
Bei den Daten-Objekten geht es wieder darum, unabhängig von der Datenquelle (CSV, Datenbank, Formular, ..) Objekte zu erzeugen, mit denen in der Anwendung gearbeitet werden kann. Da wir in einigen Fällen gleich mehrere Daten-Objekte haben (bei Such-Ergebnissen, Übersichts-Seiten, ..), gehe ich kurz auf Objekte ein, die diese Daten-Objekte wiederum kapseln und als Listen zur Verfügung stellen. Danach reden wir darüber, woher die Daten-Objekte ihre Daten erhalten, wo sie gespeichert werden und wie das ganze einfach austausch- und filterbar gemacht wird. Am Ende erfolgt ein kleiner Ausblick darauf, wie man sich diese Muster generieren lassen kann. Daten-Objekte Unsere Daten-Objekte kapseln die variablen Daten unserer Anwendung, kennen ihre Datenquelle jedoch nicht. Das ist wichtig, damit man die Datenquelle wechseln kann. Man mag meinen, dass das seltenst der Fall ist, aber ein Formular und eine Datenbank sind bereits verschiedene Datenquellen und von beiden (und noch anderen) soll unser Daten-Objekt gefüllt werden können. In der prozeduralen Programmierung schreiben wir die Daten oft in einen Array: PHP-Code: PHP-Code: PHP-Code: Bleibt also noch die Frage nach den Daten und der Validierung. Es kann natürlich sein, das $user noch gar keinen Nickname zugewiesen bekommen hat. Man mag meinen, dass ein User immer einen Nickname haben muss und dieser Wert also an den Konstruktor übergeben werden sollte, aber man muss da sehr vorsichtig sein. Erinnern wir uns daran, dass die Datenquelle unbekannt ist, also könnte der User auch aus einem Formular stammen. Vergisst der Surfer hier die Eingabe des Feldes, wollen wir trotzdem ein User-Objekt haben, um damit das Formular erneut füllen zu können. Oder aber wir haben anonyme Gast-Benutzer. Eine ganz andere Frage ist es, ob der Benutzer einen Nicknamen haben muss, bevor er in unserer Datenbank verewigt wird. Da unser Daten-Objekt aber niemals selbst Kontakt zur Datenquelle aufnimmt, ist diese Prüfung sicherlich nicht Aufgabe des Daten-Objektes. Genauso wenig wie das Prüfen auf einen doppelte belegten Nicknamen. Das klären wir dann im 3. Kapitel. Da die Information, ob ein Nickname vorliegt, trotzdem wichtig ist, fügen wir der User-Klasse noch zwei weitere Methoden hinzu: PHP-Code: PHP-Code: Entsprechend reicht uns die einfache Aussage hasNickname() nicht aus. Wir wollen wissen, ob eine mögliche Eingabe valide ist. Ich erwähne hier noch kurz Konventionen zur Schreibweise und Funktionalität: set*() Methoden sind Setter-Methoden, deren Wert als Parameter übergeben wird und die selbst keinen Rückgabewert haben. get*() Methoden sind Getter-Methoden, deren Rückgabewert valide Objekteigenschaften sind. Hinzu kommen die boolschen Getter has*(), is*() und gegebenenfalls can*(). Wem aufgefallen ist, dass wir scheinbar anstandslos Eigenschaften setzen, aber plötzlich valide Eigenschaften auslesen können, hat gut aufgepasst und mag sich noch etwas gedulden. Denn was unserer User-Klasse noch fehlt und was der entscheidende Unterschied zum Array ist, ist eine Validerung und Filterung der Daten. Das Erreichen wir mit Validierungs-Filtern. Diese sagen aus, ob $nickname ein gültiger Nickname ist und ob $birthDate auch ein Datum ist, das wir akzeptieren. Ein weiteres mal erweitern wir unsere User-Klasse: PHP-Code: PHP-Code: Warum ist isValid*() nicht statisch? Was passiert beim Weglassen der isValid*() Prüfung? Wie kommt man an Informationen über die verwendeten Validierungs-Filter heran? Warum ist isValid*() nicht statisch? Eine Validierung ist nicht zwingend User-unabhängig. Daten untereinander können sich widersprechen und das möchten wir vermeiden. Wenn ich angebe vor 25 Jahren geboren worden zu sein, kann ich nicht behaupten seit 20 Jahren den Führerschein zu besitzen. Oftmals wird solch eine Querprüfung nicht benötigt, aber ein statischer Aufruf würde uns zumindest die Möglichkeit dafür nehmen. Was passiert beim Weglassen der isValid*() Prüfung? Ganz einfach: Der Programmierer hat einen Fehler gemacht und somit tritt ein, was eintreten muss: Es wird eine Exception geworfen, denn wir hatten ja vereinbart, dass nur gültige Werte mit set*() gesetzt werden dürfen. Wenn der Programmierer die Prüfung nun unterschlagen hat, stellt dies einen Fehler (eine Ausnahme) dar und die Ausführung muss an dieser Stelle unterbrochen werden. Schließlich hätten wir fast nichts gewonnen, wenn $user nun plötzlich nicht-valide Daten annehmen würde. Wie kommt man an Informationen über die verwendeten Validierungs-Filter heran? Diese Frage wird oft unterschlagen, es ist aber wichtig zu wissen, warum eine Validierung/Filterung fehlgeschlagen ist. Sei es um den Surfer, der das Formular falsch ausgefüllt hat darüber zu informieren, oder um selbst festzustellen, welches Element beim CSV-Import ungültig ist, um es möglicherweise manuell zu korrigieren oder einen Validierungs-Filter anzupassen. Kurz gesagt, wir verwenden für die Validierung/Filterung selbst wieder Objekte und diese können wir auch bei Bedarf dem Benutzer zur Verfügung stellen: PHP-Code: Wir können nämlich im Daten-Objekt Aufgaben durchführen, die wir in einem Array nicht oder nur als dessen Ergebnis unterbringen könnten. Beispielsweise die Berechnung des Alters für unseren User. Selbstverständlich können wir die Information auch in unserem Array hinterlegen, aber erstens können dadurch inkonsistente Daten entstehen: PHP-Code: PHP-Code: Bevor ich jetzt tatsächlich zur kompletten Implementierung der User-Klasse komme, muss ich Validatoren und Filter genauer erklären. Validatoren und Filter Meine ValidateFilter sind intern Arrays, die Objekte (Validator- und Filter-Objekte) zusammenfassen (siehe 2. Kapitel) und deren Implementierung ich deshalb nicht vorweg nehmen kann. Ein ValidateFilter-Objekt hat zwei essentielle Methoden: validate() und filter(). validate() auf der einen Seite validiert lediglich, dies kommt bei den isValid*() Methoden zum Zug. Wie der Name der Methode schon andeutet (is*()) liefert die Methode nur TRUE oder FALSE. Natürlich werden hierbei die Filter auch miteinbezogen, da bei einer gemischten Kombination (wie es üblich ist) ein Validate-Objekt auf die vorhergehende Filter-Operation angewiesen ist (z.B. wenn ein deutsches Eingabedatum (d.m.Y) in ein normiertes (Y-m-d) umgewandelt wurde, um letztlich die Validität des Datums überprüfen zu können). Das hat die Konsequenzen, dass ein Filter darauf angewiesen ist, dass das vorherige Validate-Objekt eine bestimmte Datengrundlage garantiert. Bleiben wir beim Datum: wenn also das deutsche Datum beim "." aufgetrennt wird und mit "-" wieder umgedreht zusammengesetzt wird, muss auch garantiert sein, dass das Datum auch wirklich im "d.m.Y"-Format vorliegt. Das hat die Konsequenz, dass bereits beim ersten FALSE eines Validators, die gesamte validate() Methode abbricht und FALSE zurückliefert. Weiterhin ist garantiert, dass die Methode keine Exceptions wirft, so dass man bei einer "fairen" Vorprüfung auch nicht damit rechnen muss, dass eine fehlerhafte Eingabe das Skript bis zum nächsten (try-)catch abbricht. filter() wiederum liefert üblicherweise kein TRUE oder FALSE, sondern liefert den gefilterten und validierten Eingabewert, durchläuft also selbst wiederum alle Validatoren und Filter. Das hat zur Folge, dass nicht valide Eingaben mit einer Exception abgebrochen werden müssen. Darum wird filter() auch nur durch die set*() Methoden aufgerufen, da bei diesen davon ausgegangen wird, dass validate() bereits erfolgreich durchlaufen wurde. Aufgrund der möglichen Umformungen innerhalb des ValidateFilter-Objektes durch filter() kann eine Eingabe deshalb auch nicht teilweise gefiltert zurückgeliefert werden, um "fast" richtige Eingaben schonmal "fast" vorzuformatieren. Denken wir nur daran, was passiert wenn man das Datum "32.12.2009" angibt, die ersten Filter für das Datum durchlaufen sind, der String ins normierte Format gebracht wurde und dann festgestellt wird, der 32.12. existiert garnicht? Sollte dann 2009-12-32 zurückgeliefert werden? Sicherlich nicht. Hier die zusammengefasste Implementierung von Anti_ValidateFilter_String_Date: PHP-Code: PHP-Code: PHP-Code: PHP-Code: PHP-Code: Zunächst wird der Konstruktor der User-Klasse (__construct()) aufgerufen, der unsere ValidateFilter erstellt. Das heißt diese werden in der geschützten User-Eigenschaft $_valideFilters abgelegt. Für unser Geburtsdatum wurde der ValidateFilter Anti_ValidateFilter_String_Date instanziert, der die PHP-internen Funktionen is_string() und trim() als Validatoren bzw. Filter verwendet und zusätzlich eine Anti_Validate_String_Date-Klasse als Validator und eine Anti_Filter_String_Date-Klasse als Filter. Beim Aufruf von $user->isValidBirthDate("2009-12-32") wird nun die validate()-Methode bzw. die interne Methode _invoke("2009-12-32", true) von Anti_ValidateFilter_String_Date aufgerufen und die Validierung kann beginnen. Hierbei wird bei _invoke() $this in foreach verwendet, denkt euch dafür, wenn ihr das Interface Iterator noch nicht kennt, einfach einen Array mit den an push() übergebenen Elementen: array("is_string", "trim", new Anti_Validate_String_Date(), new Anti_Filter_String_Date()) Unsere ersten beiden Elemente kommen im 3. if-Block zum Zug (is_callback). Dabei wird geprüft, ob der Funktionsname ein String und aufrufbar ist. Beides ist jeweils der Fall. "is_string" wurde mit dem "is_"-Test als Validator erkannt und wird auf die Eingabe losgelassen. Sie ist nicht FALSE (is_string("2009-12-32") ist ja TRUE), also landet PHP schließlich beim continue, die Validierung kann weitergehen. "trim" landet ebenfalls im 3. if-Block des foreach() und wird als Filter erkannt, da "trim" nicht mit "is_" beginnt. Daher wird trim("2009-12-32") ausgeführt und liefert den selben Wert zurück, da keine vor- oder nachgestellten Leerzeichen (bzw. Whitespaces) vorkommen. Falls doch, wären diese jetzt entfernt worden. Das Ergebnis wird in jedem Fall zurück in $value geschrieben. Nun wird auf Anti_Validate_String_Date die Methode isValid() ausgeführt. Diese, so können wir nachlesen, testet die Eingabe mit return strtotime($value) !== false; Diese liefert uns nun endlich FALSE für das ungültige Datum, _invoke(..) wird beendet und validate() liefert uns das erwartete Ergebnis FALSE zurück. Ich denke nun wird auch klar, in welche Blöcke wir gelangen, wenn das ganze nun an setBirthDate() übergeben wird. $user->setBirthDate("2009-12-32") wird aufgerufen, wir kommen wieder zum Anti_ValidateFilter_String_Date, nur diesmal zur filter() Methode, die ebenfalls _invoke(..) aufruft, nun allerdings mit dem 2. Parameter ($validate = ) FALSE. Wir durchlaufen wieder unseren Array, durchlaufen die Validierung mit "is_string" und die Filterung mit "trim" erfolgreich, bis nun die Methode check() von Anti_Validate_String_Date aufgerufen wird. Diese macht nichts als isValid() aufzurufen (wie es bereits beim ersten isValidBirthDate() direkt geschehen ist) und wirft im Fehlerfall eine spezielle Exception. Genau wie vorhergesagt hat also isValidBirthDate() funktioniert und FALSE zurückgeliefert, wir haben dies jedoch falsch programmiert und versehentlich trotzdem setBirthDate() übergeben. Das Ergebnis, eine Exception, ist korrekt und notwendig. Korrigieren wir also Eingabe und Abfrage: PHP-Code: PHP-Code: PHP-Code: Fazit So was haben wir gewonnen? Wir haben unsere unsicheren Arrays durch ein Daten-Objekte ersetzt, die uns nun Schnittstellen, eine Prüfung und Filterung, und damit valide Daten bieten. Zusätzlich können wir Berechnungen auf diesen Daten dort zentral unterbringen. Sicherlich kann & sollte nicht jeder Array in der Anwendung durch eine Klasse ersetzt werden, aber dort wo wir verlässlichen Zugriff auf unsere komplexen Daten wollen, sollten wir darüber nachdenken. Wenn ihr die komplette, lauffähige Version mal bei euch ausprobieren wollte, könnt ihr sie im Anhang für hoffentlich eine längere Zeit downloaden. Ihr findet im Anhang zusätzlich eine Auflistung der verwendeten Klassen. Einfach so einrichten, dass index.php aus dem public-Ordner auf euren DOCUEMNT_ROOT zeigt. Am einfachsten geht das wohl über einen eigenen Virtual Host. Wie ihr den einrichtet ist eine andere Geschichte: http://www.php.de/tutorials/42725-vi...r-windows.html (Virtual Hosts (vhosts) einrichten unter Windows) Wundert euch nicht, wenn der Aufbau der Klassen etwas auseinandergezogen wurde und nun Ableitung auf Ableitung folgt, aber so fand ich das aus Programmierer-Sicht am saubersten, zum Erklären aber am schwierigsten. Daher hier im Thread nur die grobe Version! Zum Schluß noch der Hinweis, dass dieser Thread nicht als Anleitung zu verstehen ist, sondern lediglich als Anregung. Ich habe selbst noch kleine Fehler gefunden, beispielsweise wollte ich hasBirthDate() ebenso wie die anderen has*() Methoden schön generisch mit return $this->isValidBirthDate($this->_birthDate) aufrufen, allerdings erlauben die momentan verwendeten Filter keine Verzweigung (getBirthDate() liefert ja ein Anti_DateTime-Objekt, was eindeutig kein String ist). Sprich $user->setBirthDate($user->getBirthDate()) funktioniert nicht, was eigentlich nicht sein sollte. Vielleicht entwickelt sich ja im Lauf der Zeit hierfür eine Lösung, allzu schwer sollte _invoke() ja nicht umzubauen sein. Danke fürs Lesen und vor allem eure Kritiken. Das zweite Kapitel (Daten-Listen) werde ich wohl in den kommenden zwei Wochen posten. Für Kapitel 3 setze ich etwas mehr Zeit an und Kapitel 4 folgt irgendwann
__________________ "Nuschel ich?" - "Was?" Geändert von Chriz (14.09.2009 um 00:44 Uhr). |
| | |
| | |
| PHP Code Flüsterer Registriert seit: 21.08.2005 Beiträge: 4682 PHP-Kenntnisse: Fortgeschritten | |
| | |||
| Moderator Registriert seit: 11.05.2008
Beiträge: 6.069
![]() ![]() ![]() ![]() ![]() ![]() ![]() | Zitat:
Zitat:
__________________ "Nuschel ich?" - "Was?" Geändert von Chriz (14.09.2009 um 16:38 Uhr). | ||
| | |
| | ||
| moderatives Dielektrikum Registriert seit: 21.05.2008
Beiträge: 34.253
PHP-Kenntnisse: Fortgeschritten ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() | Zitat:
__________________ -- One pixel is still too big. Please make it smaller. ASAP. Initiative Mittelstand. Die wichtigste Gestaltungsregel im Screendesign ist Pi mal Daumen des Arbeitgebers. -- | |
| | |
| | |
| Moderator Registriert seit: 11.05.2008
Beiträge: 6.069
![]() ![]() ![]() ![]() ![]() ![]() ![]() | Auch nicht, weil nur die Datenquelle weiß, wie sie ihre Daten haben möchte. Natürlich ist das ungünstig, um z.B. Duplikate gleich abzulehnen. Möglicherweise sollte ich die setup()-Methoden public machen und erlauben, dass die Datenquelle eigene ValidateFilter injiziert, beispielsweise als Callback, das dann auf die Datenbank losgelassen wird und auf Duplikate prüft. Ich lass mir das mal durch den Kopfgehen.
__________________ "Nuschel ich?" - "Was?" |
| | |
| | ||
| Erfahrener Benutzer Registriert seit: 01.12.2008
Beiträge: 450
PHP-Kenntnisse: Fortgeschritten ![]() ![]() | Zitat:
Du musst nur import() anders implementieren. Code: foreach daten
try
set
catch exception
übergebe an den errorhandler
Die Exceptions selbst hätten für mich nur eine sehr einfach englische Fehlerbeschreibung und würden entweder UserException oder WrongInputException angehören, der Handler würde evtl. noch mit Sprachunterstützung eine userfreundliche Fehlermeldung generieren oder weiterleiten. | |
| | |
| | |
| moderatives Dielektrikum Registriert seit: 21.05.2008
Beiträge: 34.253
PHP-Kenntnisse: Fortgeschritten ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() | Das Array in _validate(array $property) verwendest Du, falls die genannten Abhängigkeiten von Properties bei der Validierung berücksichtigt werden sollen oder wozu? Gerade dieser Punkt macht mir auch anderweitig Sorgen. Wäre es nicht doch besser, Eingabedaten doch erstmal ins Objekt aufzunehmen, egal ob valide oder nicht? Aus der Sicht einer Formulareingabe kann sowas für den User zur Geduldsprobe werden. Das ist bei Deiner Lösung ja selbst bei einzelnen invaliden Daten der Fall. Zum Formwiederbefüllen muß ich mich damit also an $_POST halten, nicht an das Objekt. Das widerum verkompliziert die Angelegenheit, wenn ich einen Formularcode gleichzeitig für Neuerfassung und Bearbeiten eines Datensatzes nutzen wollte. Ich hoffe Du verstehst, was ich meine. Das widerspricht natürlich Deinem gesamten Konzept, aber ich finde darüber muß man nachdenken.
__________________ -- One pixel is still too big. Please make it smaller. ASAP. Initiative Mittelstand. Die wichtigste Gestaltungsregel im Screendesign ist Pi mal Daumen des Arbeitgebers. -- |
| | |
|
| Themen-Optionen | |
| Thema bewerten | |
|
|
Ähnliche Themen | ||||
| Thema | Autor | Forum | Antworten | Letzter Beitrag |
| Registrierte User sollen ihre Daten ändern können | 54ch4 | PHP Tipps 2009 | 17 | 14.03.2009 14:29 |
| Mehrere Arrays unterschiedlicher Größe kombinieren | querfisch | PHP Tipps 2007 | 9 | 31.03.2007 21:34 |
| Session Frage - gleiches Formular 2 mal alle Daten behalten | NetLook | PHP Tipps 2007 | 1 | 21.11.2005 18:42 |
| PHP-GTK Tutorial | Beitragsarchiv | 9 | 02.11.2005 21:07 | |
| [Erledigt] sql daten für einen kunden auslesen/ändern im Formular | PHP Tipps 2005-2 | 3 | 12.10.2005 08:36 | |
| [Erledigt] Daten auslesen und ändern | Datenbanken | 2 | 17.09.2005 19:28 | |
| Daten eintragen und auslesen | Rettungsdackel | Datenbanken | 0 | 14.09.2005 16:29 |
| Daten in Datenbank ändern | PHP Tipps 2005 | 3 | 27.01.2005 14:40 | |
| array_push nur in begrenzter Anzahl ausführen ? | PHP Tipps 2004 | 2 | 07.09.2004 09:05 | |
| Besucher kamen über folgende Suchanfragen bei Google auf diese Seite |
| php mapping, php or mapping, or mapping php, php or mapper, php datenobjekte, or mapper php, php or-mapping, php ormapping, php o/r mapper, php datenobjekt, php ormapper, php or, objektrelationales mapping php, php mapper, ormapper php, php object mapping, php db mapping, php objekt datensätze, php objektrelationales mapping, db mapping php |