cayaphas: PHP Skriptkontrolle durch zentrale Kontrolldatei

Hallo miteinander,

ich wühle mich seit Stunden durchs Forum zum Thema Affenformular, REQUEST-Parameter, Sessions, OOP, etc. finde aber nicht genau das was ich suche und versuch es jetzt einfach mal direkt.

Es geht darum eine elegante Art zu finden ein PHP Skript zu "steuern".

Ich habe z.B. eine Datenbank mit irgendwelchen Datensätzen. Ein Interface hierfür soll (natürlich) anbieten: - einfügen, ändern, löschen eines Datensatzes.
Mein Ansatz bisher war:
Steuerung des "Interfaces" in einer zentralen PHP Datei (control.php) mittels Auswertung eines GET-parameter "action".

Am Anfang der control.php geschieht so etwas wie:

$action = isset($_REQUEST['action']) ? $_REQUEST['action'] : 'list';

switch ($action){

// liste alle entries auf
  case 'list':
    include 'db_list.php';
    break;

// gebe (leeres) formular aus
  case 'add_form':
    include 'db_form.php';
    break;

// trage formulardaten in DB ein
  case 'add':
    /* hier füge die übergebenen $REQUEST Daten in die DB */
    header(Location: $_SERVER['PHP_SELF']?action=list);
    break;

// gebe (durch $_REQUEST['change_id'] ausgewählte Daten (aus DB)) in Formular aus
  case 'change_form':

include 'db_form.php';
    break;

// update daten in DB
  case 'change':
    /* hier füge die übergebenen $REQUEST Daten in die DB */
    header(Location: $_SERVER['PHP_SELF']?action=list);
    break;

// gebe delete bestätigungsseite seite aus (durch $_REQUEST['delete_id'] ausgewählte Daten (aus DB))
  case 'delete_form':
    include 'db_delete.php';
    break;

// delete bestimmten datensat
  case delete
    /* hier füge die übergebenen $REQUEST Daten in die DB */
    header(Location: $_SERVER['PHP_SELF']?action=list);
    break;

.....

}

Der Einfachheit halber benutze ich für Add und Change das gleiche Formular, bei dem ich anhand des isset($_REQUEST['change_form']) entscheide welche action bzw. welche submit buttons angezeigt werden.

(Der genannte Code soll nur den Ablauf und groben Aufbau zeigen.)

Ich erzeuge die Variable $_REQUEST['action'] entweder im Forular als action-attribut
(z.B. <form action=control.php?action=change&change_id=1234 method="post"> oder in der Liste per normalen, dementsprechenden generierten Link z.B. <a href="control.php?action=delete_form&delete_id=4321>Delete entry 4321</a>

Nun aber zu meinem eigentlichen Problem wo ich dann die Übersicht verliere.
Bei einem serverseitigem Formularcheck z.B. checke ich die Daten, halte sie im Falle eines Fehlers in einer Session, und mache einen header-redirect auf die Kontrolldatei mit entsprechenden requestparametern, gebe eine fehlermeldung aus und gebe das formular mit den vorherigen (session restore) werten wieder aus (und lösche dann die entsprechenden session daten). Dazu muss ich aber noch andere Daten halten, wie z.B. ob ich gerade einen Eintrag hinzufügen will oder editieren etc. ... da habe ich bis jetzt meistens mit hidden fields gearbeitet und dann im formular recht oft abgefragt in welchem status ich mich befinde ... usw.
Das alles funktioniert natürlich aber ich verliere halt mittlerweile die übersicht wo ich welche daten halte (session, get-parameter, hidden field) ... deswegen meine frage:

Ist es sinnvoll so eine zentrale steuerungsdatei zu benutzen (oder gibt es eine elegantere Lösung)?? (ich habe keine einfachere und übersichtlichere Lösung im Kopf)

Wäre es sinnvoller den gesamten Status in der Session zu halten, und zu versuchen solche sachen wie hidden fields und unterschiedliche actions im formular zu vermeiden, da ich am anfang der steuerungs datei einfach immer schaue welche buttons ich gedrückt habe und dementsprechend meine switch-variable zu setzen (was dann halt nichtmehr automatisch aus dem $GET-parameter extrahiert wird)...
Ich bin mir nicht sicher ob es da zu Problemen kommt wenn der user z.B. den Browser-back button benutzt und dadurch die entsprechenden session status daten nicht mehr mit dem angezeigten formular übereinstimmen ...

Ich weiss das ist alles recht vage und grob erklärt ... Es geht mir persönlich eher darum wie man eine PHP-Applikation elegant aufbauen sollte und was pro-kontra für sessions oder $_Request parameter als programm status-"Halter" sprechen. (Ich habs bis jetzt ja irgendwie mit beidem gemacht und dass verwirrt mich immer mehr) ..

Ich hoffe ich habe mich klar genug ausgedrückt

gruss
caya

  1. echo $begrüßung;

    Es geht darum eine elegante Art zu finden ein PHP Skript zu "steuern".
    Mein Ansatz bisher war:
    Steuerung des "Interfaces" in einer zentralen PHP Datei (control.php) mittels Auswertung eines GET-parameter "action".

    Damit baust du einen Routing-Mechanismus nach, der eigentlich Aufgabe des Webservers ist. Andererseits ist es aus der PHP-Programmierer-Sicht einfacher, nur eine Datei zu pflegen, die die gleichbleibenden Teile enthält und die wechselnden weiterdelegiert, als viele Dateien, die immer wieder die gleichbleibenden Teile mittels des gleichen Codes einbinden. Dieses doppelte Routing - der Webserver routet in jedem Fall, und dann kommt noch deins dazu - ist vielleicht nicht weiter tragisch, doch auf stark belasteten Seiten kann das ein Einsparpotenzial darstellen. Aber betrachten wir dieses Thema erstmal nicht weiter ...

    Der Einfachheit halber benutze ich für Add und Change das gleiche Formular, bei dem ich anhand des isset($_REQUEST['change_form']) entscheide welche action bzw. welche submit buttons angezeigt werden.
    (z.B. <form action=control.php?action=change&change_id=1234 method="post"> oder in der Liste per normalen, dementsprechenden generierten Link z.B. <a href="control.php?action=delete_form&delete_id=4321>Delete entry 4321</a>

    Du verwendest hier Steuerungsdaten (action) und Nutzdaten (id und beim Eintragen der Formdaten auch noch andere Datenbankfelder) gemischt. Was machst du, wenn du ein Datenbankfeld namens action bedienen musst? Einige Webserver und PHP bieten die Möglichkeit, PATHINFO zu nutzen. Man hängt an das Script weitere Parameter an, die wie zum Pfad zugehörig aussehen: http://example.com/pfad/zum/script.php/pathinfo1/pathinfo2?querystring. ("script.php" kann man noch durch mod_rewrite wegbekommen oder so umschreiben, dass das .php nicht mehr auftaucht.) Diese Pathinfo-Teile findet man unter $_SERVER['PATH_INFO'] wieder. Nun hat man die Steuerungsdaten von den Nutzdaten separiert. Aber das nur nebenbei. (Außerdem sind relative Links zu beachten, da der Browser nun möglicherweise falsch nach Bildern, CSS usw. sucht. Am besten mit absoluten Pfaden einbinden.)

    Nun aber zu meinem eigentlichen Problem wo ich dann die Übersicht verliere.
    Bei einem serverseitigem Formularcheck z.B. checke ich die Daten, halte sie im Falle eines Fehlers in einer Session, und mache einen header-redirect auf die Kontrolldatei mit entsprechenden requestparametern, gebe eine fehlermeldung aus und gebe das formular mit den vorherigen (session restore) werten wieder aus (und lösche dann die entsprechenden session daten). Dazu muss ich aber noch andere Daten halten, wie z.B. ob ich gerade einen Eintrag hinzufügen will oder editieren etc. ... da habe ich bis jetzt meistens mit hidden fields gearbeitet und dann im formular recht oft abgefragt in welchem status ich mich befinde ... usw.

    Wenn ich das richtig verstanden habe, sendest du die Daten zwischen mehreren Scripten hin und her und her und hin und zwischdurch in eine Session. Lass doch die Action solange auf Add stehen, bis der Vorgang komplett erledigt ist. Du rufst Add auf, um das Leerformular anzuzeigen. Du rufst Add auf, wenn der Browser die ersten Eingabedaten sendet. Du rufst Add auf, wenn der Browser die korrigierten Eingabedaten sendet. Und nach dem Eintragen der geprüften Daten leitest du auf List weiter. Fertig.
    Add selbst prüft, ob es plausible Daten hat, trägt sie ein und leitet zu List weiter, anderenfalls zeigt es das Formular mit den nicht richtigen Daten an. Change macht eigentlich das gleiche, nur dass es bereits ein ausgefülltes ID-Feld hat und mit Daten aus der DB beginnt.

    Add und Change sollen hier nur Aktionen sein, ohne dass dabei festgelegt ist, wie sie aufgerufen werden. Ob sie ein eigenes Script sind oder von einem von PHP ausgeführten Verteilmechanismus angesprochen werden, ist prinzipiell egal.

    Ist es sinnvoll so eine zentrale steuerungsdatei zu benutzen (oder gibt es eine elegantere Lösung)?? (ich habe keine einfachere und übersichtlichere Lösung im Kopf)

    Einfacher ist das Prinzip "ein Script - eine Aufgabe". Aber das hat, wie ich oben schon ausführte, den Nachteil des mehrfachen gemeinsamen Codes - zumindest ein include zum Einladen dieses Teils braucht es in jedem Aufgabenscript. Den Nachteil kann man sich wegkaufen durch eine zentrale Steuerung. Für einfache Fälle kann sie aus einem switch-Statement bestehen, oder sie kann aus einem ausgewachsenen Routing-Dispatch-Mechanismus bestehen, wie ihn der MVC-Teil (dort speziell der durch das C repräsentierte Teil) des Zend Frameworks anbietet. Siehe Zend_Controller.

    [...] da ich am anfang der steuerungs datei einfach immer schaue welche buttons ich gedrückt habe [...]

    Ich sehe nur Bedarf für einen Submit-Button. Der gehört zum Add-/Change-Formular. Alles andere kann man mit einfachen Links aus der Auflistung erreichen: action=foo;id=42 oder script.php/foo?id=42 (auch die ID könnte man in PathInfo unterbringen).

    Ich weiss das ist alles recht vage und grob erklärt ... Es geht mir persönlich eher darum wie man eine PHP-Applikation elegant aufbauen sollte und was pro-kontra für sessions oder $_Request parameter als programm status-"Halter" sprechen. (Ich habs bis jetzt ja irgendwie mit beidem gemacht und dass verwirrt mich immer mehr) ..

    Du brauchst die Session nur als Krücke, weil du den Client noch einen Roundtrip in Form eines Location-Headers machen lässt. Vermeide dies und du benötigst die Session nicht mehr (dafür). Beachte immer, dass HTTP ein zustandsloses Protokoll ist. Versuche die Anwendung so zu gestalten, dass du keine Statusinformation irgendwo ablegen musst. Arbeite den Request ab, so wie er kommt und gut ist. Wenn die Daten dafür nicht stimmen, schick sie direkt zur Korrektur zurück.

    echo "$verabschiedung $name";

    1. wow .. erstmal danke für die unglaublich ausführliche antwort .. ich hätte nicht gedacht, dass man es wirklich versteht (so wie ich es

      geschrieben hab)

      Damit baust du einen Routing-Mechanismus nach, der eigentlich Aufgabe des Webservers ist. Andererseits ist es aus der PHP-Programmierer-Sicht einfacher, nur eine Datei zu pflegen, die die gleichbleibenden Teile enthält und die wechselnden weiterdelegiert, als viele Dateien, die immer wieder die gleichbleibenden Teile mittels des gleichen Codes einbinden.

      Ich verstehe nicht ganz den "Routing-Mechanismus" .. Meinst du als Routing das weiterleiten mittels header()?
      (Muss ich nicht automatisch weiterleiten, wenn ich z.B. die Daten eingetragen hab und dann wieder zur Ausgabe will ? oder wenn ich die Daten checke und dann wieder auf das selbe Script weiterleite?

      Du verwendest hier Steuerungsdaten (action) und Nutzdaten (id und beim Eintragen der Formdaten auch noch andere Datenbankfelder) gemischt.

      Was machst du, wenn du ein Datenbankfeld namens action bedienen musst? Einige Webserver und PHP bieten die Möglichkeit, PATHINFO zu nutzen. Man hängt an das Script weitere Parameter an, die wie zum Pfad zugehörig aussehen:

      http://example.com/pfad/zum/script.php/pathinfo1/pathinfo2?querystring. ("script.php" kann man noch durch mod_rewrite wegbekommen oder so umschreiben, dass das .php nicht mehr auftaucht.) Diese Pathinfo-Teile findet man unter $_SERVER['PATH_INFO'] wieder. Nun hat man die Steuerungsdaten von den Nutzdaten separiert. Aber das nur nebenbei. (Außerdem sind relative Links zu beachten, da der Browser nun möglicherweise falsch nach Bildern, CSS usw. sucht. Am besten mit absoluten Pfaden einbinden.)

      Wenn ich das richtig verstanden habe, sendest du die Daten zwischen mehreren Scripten hin und her und her und hin und zwischdurch in eine

      Session. Lass doch die Action solange auf Add stehen, bis der Vorgang komplett erledigt ist. Du rufst Add auf, um das Leerformular anzuzeigen.

      Du rufst Add auf, wenn der Browser die ersten Eingabedaten sendet. Du rufst Add auf, wenn der Browser die korrigierten Eingabedaten sendet. Und nach dem Eintragen der geprüften Daten leitest du auf List weiter. Fertig.

      Add selbst prüft, ob es plausible Daten hat, trägt sie ein und leitet zu List weiter, anderenfalls zeigt es das Formular mit den nicht richtigen Daten an. Change macht eigentlich das gleiche, nur dass es bereits ein ausgefülltes ID-Feld hat und mit Daten aus der DB beginnt.

      ok, das hört sich natürlich sinnvoll an .. jetzt habe ich aber das Problem, dass ich z.B. um in ein bestimmtes Feld Daten einzutragen ein anderes Such-Formular aufrufen muss ... von dem ich, nach einer Suche und der Auswahl, wieder zurück in mein Add-Formular muss mit den vorher eingetragenen Daten... Dabei muss ich ja die vorherigen Daten halten und da habe ich eigentlich nur die Session als Möglichkeit gesehen. (ok, abgesehen von unglaublich vielen hidden-fields)

      Add und Change sollen hier nur Aktionen sein, ohne dass dabei festgelegt ist, wie sie aufgerufen werden. Ob sie ein eigenes Script sind oder von einem von PHP ausgeführten Verteilmechanismus angesprochen werden, ist prinzipiell egal.

      (wenn du hier von einem "eigenen script" redest, bedeutet das z.B. ein include der entsprechenden Skriptdatei, oder?, ein Verteilmechanismus würde ja sowieso benötigt werden um die entsprechende Skriptadatei herauszufinden, oder?

      [...] da ich am anfang der steuerungs datei einfach immer schaue welche buttons ich gedrückt habe [...]

      Ich sehe nur Bedarf für einen Submit-Button. Der gehört zum Add-/Change-Formular. Alles andere kann man mit einfachen Links aus der Auflistung erreichen: action=foo;id=42 oder script.php/foo?id=42 (auch die ID könnte man in PathInfo unterbringen).

      Stimmt natürlich, wobei noch solche Sachen wie einen Back-Button natürlich ganz hilfreich sein können.

      Du brauchst die Session nur als Krücke, weil du den Client noch einen Roundtrip in Form eines Location-Headers machen lässt. Vermeide dies und du benötigst die Session nicht mehr (dafür).

      Das kommt eigentlich nur vor wenn ich noch in unterfromulare gehen muss, und die alten Daten halten muss.

      Es ist für mich nur schwierig ein Projekt jeweils konzeptionell anzufangen, und da komm ich einfach immer wieder auf die action festlegung durch request-parameter und die verteilung durch ein switch innerhalb einer zentralen datei die jeweils weiterleitet. Ist irgendwie recht übersichtlich und einfach etwas hinzuzufügen.
      Habe ich dich richtig verstanden:
      Durch direkte Scripteinbindunge würde ich jeweils bei auflistungen mit links und entsprechenden scriptdatien aufrufen, bzw. bei formularen das entsprechende Skript als action eintragen. Eine zentrale Datei würde lediglich den Einstieg bzw. die Ausgabe nach Erfüllung bestimmter Aufgaben bedeuten .. alles andere geht durch direkten scriptaufruf.

      Auf jeden Fall Vielen Dank für die ausführliche Antwort.
      gruss
      caya

      1. echo $begrüßung;

        Damit baust du einen Routing-Mechanismus nach, der eigentlich Aufgabe des Webservers ist. Andererseits ist es aus der PHP-Programmierer-Sicht einfacher, nur eine Datei zu pflegen, die die gleichbleibenden Teile enthält und die wechselnden weiterdelegiert, als viele Dateien, die immer wieder die gleichbleibenden Teile mittels des gleichen Codes einbinden.

        Ich verstehe nicht ganz den "Routing-Mechanismus" .. Meinst du als Routing das weiterleiten mittels header()?

        Nein, damit meine ich den Mechanismus, der anhand einer übergebenen URL entscheidet, welche Ressource ausgeliefert wird. Der Webserver kann diese Ressource in einer bestimmten Datei finden, oder ein Script aufrufen, das anhand der Parameter mal Dies und mal Jenes zurückgibt.

        (Muss ich nicht automatisch weiterleiten, wenn ich z.B. die Daten eingetragen hab und dann wieder zur Ausgabe will ? oder wenn ich die Daten checke und dann wieder auf das selbe Script weiterleite?

        Es ist sicher empfehlenswert, nach Entgegennahme und Eintragen der Daten ein Redirekt auf die Anzeigefunktion zu veranlassen. Man könnte aber auch einfach so als Antwort auf die erfolgreiche Eintragung die Daten die Ausgabe der Anzeigefunktion zum Client senden. Checken und Eintragen macht ein Script in Personalunion. Wenn erfolgreich geprüft wurde, trägt es die Daten ein, ansonsten liefert es das Formular plus Daten zum Korrigieren aus. Die Formular-Action verweist dabei immer wieder auf dieses/dasselbe Script.

        ok, das hört sich natürlich sinnvoll an .. jetzt habe ich aber das Problem, dass ich z.B. um in ein bestimmtes Feld Daten einzutragen ein anderes Such-Formular aufrufen muss ... von dem ich, nach einer Suche und der Auswahl, wieder zurück in mein Add-Formular muss mit den vorher eingetragenen Daten... Dabei muss ich ja die vorherigen Daten halten und da habe ich eigentlich nur die Session als Möglichkeit gesehen. (ok, abgesehen von unglaublich vielen hidden-fields)

        Du hast also ein Formular, machst beim Ausfüllen mal einen Schritt seitwärts und wieder zurück. Wenn sich das nicht so umschreiben lässt, dass der Schritt seitwärts zuerst und vor dem eigentlichen Ausfüllen ausgeführt werden kann, dann sehe ich auch die Session als sinnvollen Ablageort an.

        Add und Change sollen hier nur Aktionen sein, ohne dass dabei festgelegt ist, wie sie aufgerufen werden. Ob sie ein eigenes Script sind oder von einem von PHP ausgeführten Verteilmechanismus angesprochen werden, ist prinzipiell egal.

        (wenn du hier von einem "eigenen script" redest, bedeutet das z.B. ein include der entsprechenden Skriptdatei, oder?

        Damit meine ich, dass die Verteilung außerhalb PHPs stattfindet und der Webserver beispielsweise entweder add.php oder list.php statt allInOne.php?action=add (oder action=list) aufruft.

        ein Verteilmechanismus würde ja sowieso benötigt werden um die entsprechende Skriptadatei herauszufinden, oder?

        Der Verteilmechanismus ist einer der beiden erwähnten Routing-Mechanismen.

        Du brauchst die Session nur als Krücke, weil du den Client noch einen Roundtrip in Form eines Location-Headers machen lässt. Vermeide dies und du benötigst die Session nicht mehr (dafür).

        Das kommt eigentlich nur vor wenn ich noch in unterfromulare gehen muss, und die alten Daten halten muss.

        Es ist für mich nur schwierig ein Projekt jeweils konzeptionell anzufangen, und da komm ich einfach immer wieder auf die action festlegung durch request-parameter und die verteilung durch ein switch innerhalb einer zentralen datei die jeweils weiterleitet. Ist irgendwie recht übersichtlich und einfach etwas hinzuzufügen.

        Wenn du eine Weiterleitung mittels Redirect meinst, dann empfinde ich das als einen unnötigen zusätzlichen Roundtrip. Da kannst du ja gleich auf das Ziel-Script leiten. Es sei denn, du meinst eine Weiterleitung zu einem (inkludierten) Script-Teil.

        Durch direkte Scripteinbindunge würde ich jeweils bei auflistungen mit links und entsprechenden scriptdatien aufrufen, bzw. bei formularen das entsprechende Skript als action eintragen. Eine zentrale Datei würde lediglich den Einstieg bzw. die Ausgabe nach Erfüllung bestimmter Aufgaben bedeuten .. alles andere geht durch direkten scriptaufruf.

        Ich würde mich generell (also für jedes Projekt einzeln) für eins von beiden entscheiden. Entweder jede Aktion in eine eigenes Script legen oder einen zentralen Einstieg mit Verteilung.

        Du kannst ja mal zum Sammeln von Erfahrung mit den beiden Vorgehensweisen jeweils ein kleines Beispielprojekt anlegen, mit vielleicht einer Handvoll Aktionen (echo 'Aktion 1'; usw.). Diese aber so anlegen wie bei einem großen Projekt. Also, wenn du pro Aktion eine zu inkludierende Datei nähmst, machst du das hier auch so. Dann denkt die ein paar Änderungen aus, wie Stil-Änderung (Hintergrund soll nun grün statt blau sein) oder Inhaltsänderung (der auf jeder Seite angezeigte Projektname ändert sich), und schaue wie einfach/kompliziert sich dies jeweils erledigen lässt.

        echo "$verabschiedung $name";

        1. Vielen Dan für die ausführlichen Antworten...

          Gruss
          caya