Ankündigung

Einklappen
Keine Ankündigung bisher.

MVC / wo Benutzereingaben validieren?

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • MVC / wo Benutzereingaben validieren?

    Hallo,

    Ich habe bis jetzt nur relativ wenig Erfahrung mit OOD und MVC und mich beschäftigt zurzeit eine vielleicht etwas "blöde" Frage. In welche Schicht bei einer MVC-Archtitektur gehören eigentlich die für Benutzereingaben zuständigen Validatoren?

    View : ist ausschließlich für Präsentation zuständig und daher wahrscheinlich am wenigsten geeignet

    Controller: könnte dafür zwar "mißbraucht" werden, ist aus meiner Sicht allerdings auch nur suboptimal geeignet und würde wahrscheinlich auf lange Sicht zu viel Copy&Paste Code führen

    Model: kennt zwar seine Daten und könnte sie strenggenommen auch selbst validieren, aber es ist eigentlich nicht sein Aufgabenbereich, außerdem kann es auch gut sein, daß die Daten (z.B. bei einer Suchanfrage) zuerst von einem DataMapper verarbeitet werden und es somit noch gar keine Instanz des Models existiert

    Momentan habe ich also den Eindruck, daß die Validatoren in keiner der drei Schichten wirklich gut aufgehoben sind.

    Mich würde mal interessieren wie ihr das sieht und wie man es eigentlich richtig macht.

    Vielen Dank im Voraus
    jack
    -

  • #2
    Such mal nach „fat model, skinny controller“
    [COLOR="#F5F5FF"]--[/COLOR]
    [COLOR="Gray"][SIZE="6"][FONT="Georgia"][B]^^ O.O[/B][/FONT] [/SIZE]
    „Emoticons machen einen Beitrag etwas freundlicher. Deine wirken zwar fachlich richtig sein, aber meist ziemlich uninteressant.
    [URL="http://www.php.de/javascript-ajax-und-mehr/107400-draggable-sorttable-setattribute.html#post788799"][B]Wenn man nur Text sieht, haben viele junge Entwickler keine interesse, diese stumpfen Texte zu lesen.“[/B][/URL][/COLOR]
    [COLOR="#F5F5FF"]
    --[/COLOR]

    Kommentar


    • #3
      Und stell dir MVC nicht als Schichtenarchitektur vor, in die die gesamte Anwendung gepresst wird (so war es nie vorgesehen) sondern als Pattern für die Präsentationsschicht - im klassischen Sinne war das die GUI, in Web-Frameworks alles was direkt mit Request-Verarbeitung und Response-Generation zu tun hat. Andere Schichten wie Service- und Persistenzschicht können und sollten völlig unabhängig von MVC existieren.

      außerdem kann es auch gut sein, daß die Daten (z.B. bei einer Suchanfrage) zuerst von einem DataMapper verarbeitet werden und es somit noch gar keine Instanz des Models existiert
      Ein Denkanstoß dazu: Models (oder im REST-Jargon Ressourcen) müssen nicht immer Datensätze sein. Auch eine Suchanfrage kann als Objekt modelliert werden, das wiederum einen Validator hat. Je nach Anwendungsfall kann das aber auch Overkill sein, es spricht nichts dagegen, simple Prüfungen wie "Parameter nicht-leer" einfach in den Controller zu schreiben, der dann meist auch direkt Methoden zur Fehlerausgabe hat.
      [IMG]https://g.twimg.com/twitter-bird-16x16.png[/IMG][URL="https://twitter.com/fschmengler"]@fschmengler[/URL] - [IMG]https://i.stack.imgur.com/qh235.png[/IMG][URL="https://stackoverflow.com/users/664108/fschmengler"]@fschmengler[/URL] - [IMG]http://i.imgur.com/ZEqflLv.png[/IMG] [URL="https://github.com/schmengler/"]@schmengler[/URL]
      [URL="http://www.schmengler-se.de/"]PHP Blog[/URL] - [URL="http://www.schmengler-se.de/magento-entwicklung/"]Magento Entwicklung[/URL] - [URL="http://www.css3d.net/"]CSS Ribbon Generator[/URL]

      Kommentar


      • #4
        hallo, danke erst mal für die schnelle Antwort.

        Den Artikel über das fette Model und den schlanken Controller kenne ich übrigens bereits Den Ansatz halte ich auch für absolut richtig. Aber wo platziere ich den Validator wenn es das Model noch gar nicht gibt? Bei einer PLZ-Suche würde ich den Parameter "zipcode" z.B. gerne im Controller direkt an den zipcodeMapper->find($zipcode) übergeben. Dabei scheint mir jedoch weder der Mapper noch der Controller der richtige Ort für eine Zipcode-Validierung zu sein. Im Zipcode-Model könnte ich zwar einen Zip-Validator implementieren, aber um diesen zu benutzen müßte ich extra zuvor ein leeres ZipcodeModel (Ghost) erstellen bevor ich anschließend den zipcodeMapper aufrufe um ein echtes ZipcodeModel anhand der PLZ zu erzeugen. Irgendwie scheint mir das nicht die eleganteste Lösung zu sein.

        vg
        jack
        -

        Kommentar


        • #5
          vielleicht sagt ihr jetzt "nicht das schon wieder" aber ich verweise immer wieder gerne auf Kohana PHP Framework.

          dort wird es so geregelt dass ein Model eine rules funktion hat in der man für jedes feld bestimmte validierungen definiert

          zb Model

          PHP-Code:
          class Model_User extends ORM{

          public function 
          rules(){
          return array(
          'username'=>array(
          array(
          'not_empty'),
          array(
          'alpha_numeric')
          )
          )
          }

          im controller wird es beim speichern bzw updaten dann valdiert

          PHP-Code:
          class Controller_Foo extends Controller{
          public function 
          action_index(){
          $users ORM::factory('user'); //oder new Model_User();
          $user->username '@foo_baz';
          try{
          $user->save();
          }catch(
          ValidationException $e ){
          $this->response->body($e->error);
          }

          }

          ist halt eine idee die man sich anschauen könnte

          MFG
          apt-get install npm -> npm install -g bower -> bower install <package> YOLO [URL]https://www.paypal.me/BlackScorp[/URL] | Mein Youtube PHP Kanal: [url]https://www.youtube.com/c/VitalijMik[/url]

          Kommentar


          • #6
            Aber wo platziere ich den Validator wenn es das Model noch gar nicht gibt?
            Mach halt ein eigenständiges Objekt draus. Brauchst Du es im Model, injeziere es ins Model.
            [COLOR="#F5F5FF"]--[/COLOR]
            [COLOR="Gray"][SIZE="6"][FONT="Georgia"][B]^^ O.O[/B][/FONT] [/SIZE]
            „Emoticons machen einen Beitrag etwas freundlicher. Deine wirken zwar fachlich richtig sein, aber meist ziemlich uninteressant.
            [URL="http://www.php.de/javascript-ajax-und-mehr/107400-draggable-sorttable-setattribute.html#post788799"][B]Wenn man nur Text sieht, haben viele junge Entwickler keine interesse, diese stumpfen Texte zu lesen.“[/B][/URL][/COLOR]
            [COLOR="#F5F5FF"]
            --[/COLOR]

            Kommentar


            • #7
              @BlackScorp
              ich habe in meinen Models eine ähnliche Implementierung bereits teilweise umgesetzt. Mit speziellen Metadata-Angaben lassen sich für jede Property beliebige Filter und Validatoren festlegen. Diese werden dann einfach bei jedem Setter durchlaufen. So kann ich sicher sein, das die Daten im Model immer gültige Werte enthalten und eine zusätzliche Validierung im Mapper ist nicht mehr erforderlich.

              Mein Problem ist allerdings, daß ich nicht für jede Suchanfrage in der Art

              PHP-Code:
              mapper->findUserByLastname($searchValue);
              mapper->findUserByEmail($searchValue);
              mapper->findUserById($searchValue); 
              etc.

              jeweils zusätzlich eine extra Validierungsklasse schreiben will, da ich für jede Property bereits im Model die entsprechenden Validatoren-Rules definiert habe.
              Somit müßte ich den Code quasi duplizieren.

              insofern dachte ich evtl. an eine solche Lösung:
              PHP-Code:
              $user = new Model_User(); // ein leeres Model nur zu Validierungszwecken instanziern

              if($user->isValid($property$searchValue) === true) {
                
              $user mapper->findUserByName($searchValue);



              würde sowas evtl. Sinn machen oder ist das kompletter Unsinn?

              vg
              jack
              -

              Kommentar


              • #8
                Sind valide Suchanfragen bei dir wirklich äquivalent zu validen Attributen? Im Allgemeinen ist das nämlich nicht so.
                [IMG]https://g.twimg.com/twitter-bird-16x16.png[/IMG][URL="https://twitter.com/fschmengler"]@fschmengler[/URL] - [IMG]https://i.stack.imgur.com/qh235.png[/IMG][URL="https://stackoverflow.com/users/664108/fschmengler"]@fschmengler[/URL] - [IMG]http://i.imgur.com/ZEqflLv.png[/IMG] [URL="https://github.com/schmengler/"]@schmengler[/URL]
                [URL="http://www.schmengler-se.de/"]PHP Blog[/URL] - [URL="http://www.schmengler-se.de/magento-entwicklung/"]Magento Entwicklung[/URL] - [URL="http://www.css3d.net/"]CSS Ribbon Generator[/URL]

                Kommentar


                • #9
                  insofern dachte ich evtl. an eine solche Lösung:
                  PHP-Code:
                  $user = new Model_User(); // ein leeres Model nur zu Validierungszwecken instanziern

                  if($user->isValid($property$searchValue) === true) {
                    
                  $user mapper->findUserByName($searchValue);

                  … 
                  würde sowas evtl. Sinn machen oder ist das kompletter Unsinn?
                  Was genau willst Du da überhaupt validieren?
                  [COLOR="#F5F5FF"]--[/COLOR]
                  [COLOR="Gray"][SIZE="6"][FONT="Georgia"][B]^^ O.O[/B][/FONT] [/SIZE]
                  „Emoticons machen einen Beitrag etwas freundlicher. Deine wirken zwar fachlich richtig sein, aber meist ziemlich uninteressant.
                  [URL="http://www.php.de/javascript-ajax-und-mehr/107400-draggable-sorttable-setattribute.html#post788799"][B]Wenn man nur Text sieht, haben viele junge Entwickler keine interesse, diese stumpfen Texte zu lesen.“[/B][/URL][/COLOR]
                  [COLOR="#F5F5FF"]
                  --[/COLOR]

                  Kommentar


                  • #10
                    Guten Morgen. Also ich würde die validatoren auch in ein vom model oder controller losgelöstes Objekt stecken. Dann kannst diese entweder über eine factory Methode der model Basis klasse oder eine factory methode der controller basis gezielt da wo Xu es willst eine validator instans erstellen oder wie nikosch schrieb via DI in model oder controller injizieren.
                    Aus dem Dynamo Lande kommen wir. Trinken immer reichlich kühles Bier. Und dann sind wir alle voll, die Stimmung ist so toll. Aus dem Dynamo Lande kommen wir.
                    [URL]http://www.lit-web.de[/URL]

                    Kommentar


                    • #11
                      Sind valide Suchanfragen bei dir wirklich äquivalent zu validen Attributen? Im Allgemeinen ist das nämlich nicht so.
                      Das verstehe ich nicht ganz. Im allgemeinen entsprechen die Tabellenspalten doch den Attributen eines Objektes. Eine Suchanfrage bezieht sich somit in der Regel direkt auf eine oder mehrere Spalten bzw. Attribute. Wenn ich also in einer Integer-Spalte nach „abc“ suche, oder in einer PLZ-Spalte nach „123456“ dann sind das von vornherein ungültige Suchanfragen.

                      Was genau willst Du da überhaupt validieren?
                      In dem o.g. Beispiel will ich z.B. alle User mit einem bestimmten Namen finden.

                      PHP-Code:
                      if ($user->isValid("name""mei#er") === true) {
                          
                      $user $mapper->findUserByName("mei#er");
                      } else {
                          echo 
                      "Der Username enthält leider ungültige Zeichen";

                      Da in einem Namen bestimmte Zeichen wie „?#+*“ etc. nichts zu suchen haben, würde ich das gerne prüfen bevor ich die Abfrage an die Datenbank Absätze.


                      Also ich würde die validatoren auch in ein vom model oder controller losgelöstes Objekt stecken.
                      Das klingt an sich ganz gut aber, wie würde man das in der Praxis konkret umsetzen? Wenn ich ein Model definiere, dann mache ich das in etwa so wie von Blackscorp beschrieben

                      PHP-Code:
                      class Model_Album extends Model_Abstract {

                      protected 
                      $_propertyDeclaration = array(
                      "id" => "INTEGER",
                      "title" => "STRING:validator(NotEmpty):validator(MaxLength(50))",
                      "info" => "STRING:validator(MaxLength(500))",
                      "artist" => "OBJECT:mapper(Model_Mapper_Artist):class(LS_Model_Artist):column(artist_fid)"
                      );

                      Wenn ich die Validierungen aus dem Model wieder rausnehme dann müsste ich doch für jede Eigenschaft einen eigenen Validator erstellen, den ich dann eigentlich jedes mal in das Model injezieren müsste:

                      PHP-Code:
                      Class Model_Validator_Album_Title {
                          

                      }

                      Class 
                      Model_Validator_Album_Info {
                          
                      .
                      }

                      $album = new Album();
                      $album->addValidator("title", new Model_Validator_Album_Title);
                      $album->addValidator("info", new Model_Validator_Album_Info); 
                      Das erscheint mir ziemlich umständlich, oder habe ich den Ansatz falsch verstanden?
                      -

                      Kommentar


                      • #12
                        Eine Suchanfrage bezieht sich somit in der Regel direkt auf eine oder mehrere Spalten bzw. Attribute. Wenn ich also in einer Integer-Spalte nach „abc“ suche, oder in einer PLZ-Spalte nach „123456“ dann sind das von vornherein ungültige Suchanfragen.
                        Nur so lange du ein exaktes Attribut suchst. Postleitzahlensuche könnte auch als Präfixsuche implementiert werden, so dass ich z.B. "50" für Köln eingeben kann, "50" ist aber keine valide Postleitzahl. Andere Beispiele wären Suche mit Wildcards oder Suche mit mehreren Werten (OR).

                        Und nur weil in einem Namen normalerweise kein "#" vorkommt, muss ich nicht zwingend die Suche nach "mei#er" verbieten - sie wird halt zu keinem Ergebnis führen. Auf der anderen Seite könnte es Attribute geben, die leer sein dürfen, man möchte aber vielleicht keine Suche ohne Wert erlauben (das hängt natürlich wieder davon ab, ob es eine exakte Suche sein soll oder nicht).

                        Du siehst, es gelten im allgemeinen völlig andere Regeln, was eine gültige Suchanfrage ist im Vergleich zu einem gültigen Datensatz. Es ist also nur logisch, dies auch getrennt zu handhaben.
                        [IMG]https://g.twimg.com/twitter-bird-16x16.png[/IMG][URL="https://twitter.com/fschmengler"]@fschmengler[/URL] - [IMG]https://i.stack.imgur.com/qh235.png[/IMG][URL="https://stackoverflow.com/users/664108/fschmengler"]@fschmengler[/URL] - [IMG]http://i.imgur.com/ZEqflLv.png[/IMG] [URL="https://github.com/schmengler/"]@schmengler[/URL]
                        [URL="http://www.schmengler-se.de/"]PHP Blog[/URL] - [URL="http://www.schmengler-se.de/magento-entwicklung/"]Magento Entwicklung[/URL] - [URL="http://www.css3d.net/"]CSS Ribbon Generator[/URL]

                        Kommentar


                        • #13
                          Wenn ich die Validierungen aus dem Model wieder rausnehme dann müsste ich doch für jede Eigenschaft einen eigenen Validator erstellen, den ich dann eigentlich jedes mal in das Model injezieren müsste:
                          Dann musst du dir ein vernünftiges Design für deine Validatoren einfallen lassen so das du dieses Problem nicht hast.

                          Man könnte zum Beispiel in der Abstrakten Model Klasse eine Factory Methode haben der du entweder einen String für einen Validator übergibst, oder ein Array für mehrere Validatoren.

                          Du musst im Prinzip deine Validatorfunktionalität so umsetzen das Validatoren leicht austauschbar sind und vor allem das du ohne großen Aufwand neue Validatoren einbringen kannst.

                          MVC ist das eine, aber bei einer Anwendung gehört eben dann doch noch einiges mehr dazu was benutzt werden muss und vernünftig designed werden sollte.

                          Ich weiß jetzt auf Anhieb auch nicht wie ich das konkret umsetzen würde, habe auch nicht wirklich die Zeit mir darüber jetzt Gedanken zu machen. Das mit der Fabrik ist nur ein Grundgedanke.

                          Gruß Litter
                          Aus dem Dynamo Lande kommen wir. Trinken immer reichlich kühles Bier. Und dann sind wir alle voll, die Stimmung ist so toll. Aus dem Dynamo Lande kommen wir.
                          [URL]http://www.lit-web.de[/URL]

                          Kommentar


                          • #14
                            @fab
                            Ok, das ist richtig. Aber wie könnte man es nun konkret implementieren?

                            Wäre es z.B. sinnvoll alle Validatoren für Suchanfragen aus einem bestimmten Bereich in eine entsprechende Klasse zu packen in der Art:
                            PHP-Code:
                            Class PlzSearch_Validator {

                                public function 
                            checkPlz($plz) {
                                    
                            // prüft ob der Wert für eine PLZ-Suche zulässig ist
                                
                            }

                                public function 
                            checkOrt($ort) {
                                    
                            //  prüft ob der Wert für eine Ort-Suche zulässig ist
                                
                            }
                                
                            // evtl. weitere prüfmethoden 
                            }

                            // controler plzSearch 

                            $searchValidator = new PlzSearch_Vaidator();
                            if (
                            $searchValidator->checkPlz($plzValue)) {
                                
                            $plzMapper = new plzMapper();
                                
                            $plzModels $plzMapper->findByPlz($plzValue);
                            } else {
                                
                            // die Suchanfrage wird nicht weiter verarbeitet


                            Bei Requests die keine Suchanfragen (select) sind, sondern Daten modifizieren (update, insert, delete), könnte man die Prüfung hingegen nur im Model belassen und bräuchte somit kein zusätzliches Validator-Objekt davor zu schalten.

                            PHP-Code:
                            $arrFormUserData $_POST["user"];

                            $user = new Model_User($arrFormUserData);
                            if(
                            $user->isValid()) {
                                
                            $mapper = new Mapper_User();
                                
                            $mapper->save($user);
                            } else {
                                echo 
                            $user->getValidatorErrorMessages();

                            Wäre das eine praktikabler Ansatz?

                            vg
                            jack
                            -

                            Kommentar


                            • #15
                              Geht in die richtige Richtung, allerdings sollten auch nur Methoden in eine Klasse, die auch gemeinsam genutzt werden. Wenn die Suche nach Postleitzahl und die Suche nach Ort getrennt voneinander sind, hat checkOrt() nichts in PlzSearchValidator zu suchen.

                              Ich greife nochmal den Ansatz auf, für die Suche selbst ein Domain-Objekt anzulegen, am Beispiel einer Suche nach PLZ und/oder Ort (ein Formular, eins von beiden muss ausgefüllt sein).
                              PHP-Code:
                              class LocationSearch
                              {
                                private 
                              $postcode;
                                private 
                              $city;

                                public function 
                              __construct($postcode$city)
                                {
                                  
                              $this->postcode trim($postcode);
                                  
                              $this->city trim($city);
                                }

                                public function 
                              isValid()
                                {
                                  if (empty(
                              $this->postcode) && empty($this->city)) {
                                    
                              // beide Felder leer: invalide
                                    
                              return false;
                                  }
                                  if (!empty(
                              $this->postcode) && !ctype_digit($this->postcode)) {
                                    
                              // PLZ ausgefüllt und nicht aus Ziffern: invalide (Nur sinnvoll mit deutschen Postleitzahlen!)
                                    
                              return false;
                                  }

                                  return 
                              true;
                                }

                                public function 
                              hasPostcode()
                                {
                                  return !empty(
                              $this->postcode);
                                }
                                public function 
                              getPostcode()
                                {
                                  return 
                              $this->postcode;
                                }
                                public function 
                              hasCity()
                                {
                                  return !empty(
                              $this->city);
                                }
                                public function 
                              getCity()
                                {
                                  return 
                              $this->city;
                                }

                              PHP-Code:
                              //Controller:
                              $search = new LocationSearch($_POST['postcode'], $_POST['city']);
                              if (
                              $search->isValid()) {
                                
                              // Hier entweder anhand hasPostcode() und hasCity() entscheiden, wie die Suche
                                // durchgeführt wird oder $search direkt an Mapper/Repository übergeben
                              } else {
                                
                              // Fehler

                              [IMG]https://g.twimg.com/twitter-bird-16x16.png[/IMG][URL="https://twitter.com/fschmengler"]@fschmengler[/URL] - [IMG]https://i.stack.imgur.com/qh235.png[/IMG][URL="https://stackoverflow.com/users/664108/fschmengler"]@fschmengler[/URL] - [IMG]http://i.imgur.com/ZEqflLv.png[/IMG] [URL="https://github.com/schmengler/"]@schmengler[/URL]
                              [URL="http://www.schmengler-se.de/"]PHP Blog[/URL] - [URL="http://www.schmengler-se.de/magento-entwicklung/"]Magento Entwicklung[/URL] - [URL="http://www.css3d.net/"]CSS Ribbon Generator[/URL]

                              Kommentar

                              Lädt...
                              X