Kann / sollte in MVC das Abrufen grundlegender Daten aus dem Modell in der Ansicht erfolgen?

10

Sollte man angesichts des Konzepts von "Skinny Controllern, Fat Models" und der allgemeinen Akzeptanz, dass Views Models direkt aufrufen können, wenn Daten für die Ausgabe benötigt werden, in Betracht ziehen, die "Get and Display" -Teile von Anforderungen in den Views und nicht den Controller zu behandeln? Zum Beispiel (versucht, den Code ziemlich allgemein zu halten):

Regler

<?php

class Invoice extends Base_Controller {

    /**
     * Get all the invoices for this month
     */

    public function current_month() {

        // as there's no user input let's keep the controller very skinny,
        // DON'T get data from the Model here, just load the view

        $this->load->view('invoice/current_month');

    }

}

Aussicht

<?php

// directly retrieve current month invoices here

$invoices = $this->invoice_model->get_current_month();

// get some other display-only data, e.g. a list of users for a separate list somewhere on the page

$users = $this->user_model->get_users();

?>

<h1>This month's invoices</h1>

<ul>
<?php foreach ($invoices as $invoice) { ?>

<li><?php echo $invoice['ref']; ?></li>

<?php } ?>
</ul>

Für mich ist dies zumindest in den Fällen sinnvoll, in denen eine Anfrage im Wesentlichen nur eine Ansicht ist. Warum sollte der Controller die Daten sammeln und an die Ansicht weitergeben müssen, wenn er sie nur selbst abrufen kann? Dadurch bleibt der Controller offen für die reine Verarbeitung auf Anwendungsebene (z. B. Bearbeitung von GET / POST-Anforderungen, Verwaltung von Zugriffsrechten und Berechtigungen usw.) sowie für die Wiederverwendbarkeit der Modelle und aller anderen guten Dinge.

Wenn dieses Beispiel erweitert wurde, damit ein Benutzer die Ergebnisse filtern kann, verarbeitet der Controller nur den POST aus dem Formular und übergibt die Filter an die Ansicht, die dann die Daten erneut anfordert, diesmal mit den Filtern.

Ist dies ein gültiger Ansatz für die Entwicklung einer MVC-Anwendung? Oder übersehe ich einen wichtigen Teil der Rolle, die ein Controller spielen sollte?

Adam Westbrook
quelle

Antworten:

17

Ja, das ist technisch möglich. Nein, das sollte nicht gemacht werden. Und ja, Sie vermissen ein bisschen, wofür der Controller da ist.

Der Controller ist dazu da, die Ansicht vom Modell zu entkoppeln. Die Entkopplung ist vorteilhaft, da Sie die Ansicht als fast wegwerfbaren Code betrachten sollten. Wenn sich Ihre UI-Technologie ändert, möchten Sie die Nacharbeit minimieren, die zum Generieren einer neuen Ansicht erforderlich ist. Der Controller ermöglicht diese Entkopplung und bietet einen Platz für Ihren Code, der die UI-Technologien unterstützt.

Es funktioniert auch umgekehrt, wenn Sie Ihr Modell ergänzen oder ändern müssen. Alle vorgelagerten Änderungen werden im Controller enthalten sein und Ihre Ansichten werden in Ruhe gelassen.

Das andere Risiko besteht darin, dass die Ansicht zwar jetzt sehr einfach ist , Sie jedoch weniger Garantie dafür haben, dass sie während ihres gesamten Lebens so einfach bleibt. Indem Sie das Modell direkt aus der (sehr einfachen) Ansicht aufrufen, haben Sie die Tür ein wenig geöffnet, damit sich später zusätzliche schlechte Praktiken einschleichen können, wenn die sehr einfache Ansicht nicht so einfach werden muss. Ein zukünftiger Entwickler wird versucht sein, mehr Modellaufrufe aus der nicht ganz so einfachen Ansicht zu tätigen, anstatt den Code umzugestalten und mit einem Controller zu interagieren.


quelle
1
Tolle Antwort, danke. Erweitern Sie Ihr Szenario "Vorausschau" leicht. Wenn auf einer Seite allgemeine Informationen vorhanden sind, die nicht den Anforderungen entsprechen (z. B. wenn der Benutzer ein bestimmtes Produkt anzeigt, wird nebenbei eine allgemeine Liste der neuesten Sonderangebote angezeigt), wie / wo soll der Anruf offers_model->get_latest()erfolgen? Das Hinzufügen zu jeder Methode im Controller (wie ich es dummerweise zuvor versucht habe) scheint übertrieben und deutlich ungetrocknet zu sein.
Adam Westbrook
2
@AdamWestbrook Schauen Sie sich MVVM an. Der ViewModel-Teil davon kann dieses spezielle Problem beheben. Sie können das offers_model->get_latest()zu einer ProductViewModelBasisklasse oder ähnlichem hinzufügen .
Zachary Yates
1
Großartig, ich werde mich auf jeden Fall mit MVVM befassen, nochmals vielen Dank.
Adam Westbrook
Sehr gute Antwort, wird dies trotzig in der Hauptrolle halten. Persönlich bin ich auch ein großer Fan von MVVM :)
Benjamin Gruenbaum
@BenjaminGruenbaum Verwenden Sie MVVM in PHP? Wenn ja, verwenden Sie ein bestimmtes Framework dafür?
Adam Westbrook
6

Angesichts des Konzepts von "Skinny Controllern, Fat Models" und der allgemeinen Akzeptanz, dass Views Models direkt aufrufen kann, wenn Daten für die Ausgabe benötigt werden

Nein, das ist nicht richtig. View kann Models nicht direkt aufrufen. Ansichten sollten keinen Zugriff auf Modellobjekte haben, es sei denn, der Programmierer hat diese Objekte aus irgendeinem Grund für die Ansicht verfügbar gemacht.

Sollte man in Betracht ziehen, die Teile von Anforderungen in den Ansichten abzurufen und anzuzeigen, und nicht den Controller?

Das löscht im Grunde den Controller und besiegt den Punkt, sie zu haben.

Warum sollte der Controller die Daten sammeln und an die Ansicht weitergeben müssen, wenn er sie nur selbst abrufen kann?

Der Controller sammelt die Daten nicht. Das Modell sammelt die Daten. Der Controller entscheidet, ob diese Daten an die Ansicht übergeben werden sollen. In der Ansicht werden nur die Daten dargestellt.

Wenn dieses Beispiel erweitert wurde, damit ein Benutzer die Ergebnisse filtern kann, verarbeitet der Controller nur den POST aus dem Formular und übergibt die Filter an die Ansicht, die dann die Daten erneut anfordert, diesmal mit den Filtern.

Nein.

Der Controller prüft, ob die POST-Daten gültig sind, übergibt diese Daten dann als Optionen an das Modell, das dann die Datenquelle abfragt und die Daten zurückgibt, und der Controller übergibt diese an die Ansicht.

Ist dies ein gültiger Ansatz für die Entwicklung einer MVC-Anwendung? Oder übersehe ich einen wichtigen Teil der Rolle, die ein Controller spielen sollte?

Der Controller fungiert als Handler für Anforderungen des Browsers. Ein Dispatcher sendet die Anforderung an die Aktion eines Controllers, die die Anforderung wiederum an die Modelle verteilt. Die Modelle enthalten die gesamte Geschäftslogik (dies ist der fette Teil) und geben die Daten an den Controller zurück. Der Controller kann die Daten dann vereinfachen und anpassen, damit die Ansicht sie leichter darstellen kann.

Der Zweck der Ansicht besteht darin, die Struktur und Abhängigkeit zwischen der Darstellung von HTML und der DataSource zu entkoppeln. Dies kann zwar schwierig sein. Ansichten zeigen nicht immer Daten an, die direkt von einem Modell stammen. Der Controller fügt häufig relevante relevante Daten hinzu.

Ich bin mir sicher, dass es auf MVC viele Tutorials gibt. Ich würde empfehlen, einige davon zu lesen.

Reactgular
quelle
Danke Mathew. Zur Verdeutlichung habe ich bisher immer die Ansicht und das Modell mit dem Controller entkoppelt, wie gelesen und vorgeschlagen. Seit ich mich über das Halten von "dünnen" Controllern informiert habe, habe ich mich nur gefragt, was aus ihnen herausgenommen werden soll / könnte. Der Denkprozess, der mich zu dieser Frage führte, war ein oder zwei Schritte zu weit!
Adam Westbrook
Wenn Sie anfangen, Modelle zu verwenden, die von vielen Controllern verwendet werden. Die Notwendigkeit, dass sie fett sind, wird sehr deutlich. Wenn die Ansicht viel PHP enthält, wissen Sie, dass Ihr Controller zu dünn ist. Wenn Ihre Controller sehr fett sind. Es ist schwierig, andere Controller dazu zu bringen, auf die gleiche Weise zu arbeiten (z. B. Hinzufügen eines API-Dienstes).
Reactgular
3

Ich fand Ihre Frage sehr interessant, da ich kürzlich beim Erlernen von Python auf dasselbe Problem gestoßen bin.

Während die gegebenen Antworten ein überzeugendes Argument darstellen, dachte ich, ich würde eine andere Meinung hinzufügen, auf die ich gestoßen bin, in der die Ansicht den Status des Modells erhält, ohne den Controller zu durchlaufen.

MVC

Es ist wichtig zu beachten, dass sowohl die Ansicht als auch der Controller vom Modell abhängen. Das Modell hängt jedoch weder von der Ansicht noch von der Steuerung ab. Dies ist einer der Hauptvorteile der Trennung. Durch diese Trennung kann das Modell unabhängig von der visuellen Darstellung erstellt und getestet werden. Die Trennung zwischen Ansicht und Controller ist in vielen Rich-Client-Anwendungen zweitrangig, und tatsächlich implementieren viele Benutzeroberflächen-Frameworks die Rollen als ein Objekt. In Webanwendungen hingegen ist die Trennung zwischen Ansicht (dem Browser) und Controller (den serverseitigen Komponenten, die die HTTP-Anforderung verarbeiten) sehr gut definiert.

Model-View-Controller ist ein grundlegendes Entwurfsmuster für die Trennung von Benutzeroberflächenlogik und Geschäftslogik. Leider hat die Popularität des Musters zu einer Reihe fehlerhafter Beschreibungen geführt. Insbesondere wurde der Begriff "Controller" verwendet, um verschiedene Dinge in verschiedenen Kontexten zu bedeuten. Glücklicherweise hat das Aufkommen von Webanwendungen dazu beigetragen, einige der Unklarheiten zu beseitigen, da die Trennung zwischen der Ansicht und dem Controller so offensichtlich ist.

In der Anwendungsprogrammierung in Smalltalk-80: Verwendung von Model-View-Controller (MVC) [Burbeck92] beschreibt Steve Burbeck zwei Varianten von MVC: ein passives Modell und ein aktives Modell.

Das passive Modell wird verwendet, wenn ein Controller das Modell ausschließlich manipuliert. Der Controller ändert das Modell und informiert die Ansicht darüber, dass sich das Modell geändert hat und aktualisiert werden sollte (siehe Abbildung 2). Das Modell in diesem Szenario ist völlig unabhängig von der Ansicht und der Steuerung. Dies bedeutet, dass das Modell keine Möglichkeit hat, Änderungen in seinem Status zu melden. Das HTTP-Protokoll ist ein Beispiel dafür. Es gibt keine einfache Möglichkeit im Browser, asynchrone Updates vom Server abzurufen. Der Browser zeigt die Ansicht an und reagiert auf Benutzereingaben, erkennt jedoch keine Änderungen an den Daten auf dem Server. Nur wenn der Benutzer explizit eine Aktualisierung anfordert, wird der Server nach Änderungen abgefragt.

MVC - Passives Modell

Ich bin nicht in der Lage zu sagen, welche der Meinungen "richtig" ist, und um ehrlich zu sein, bin ich etwas verwirrter, nachdem ich die Antworten hier und den verlinkten Artikel gelesen habe.

Vollständiger Text des Artikels hier .

Alnafie
quelle
Richtig, und die andere Sache, die Verwirrung stiftet, ist der Client-Server, den die ursprüngliche SmallTalk MVC nicht wirklich berücksichtigt hat. Auf dem Client-Server (z. B. mit Javascript) befinden sich präsentationsorientierte Modelle, Ansichten und Controller auf dem Client sowie domänenorientierte Ansichten und Controller auf dem Server, obwohl der Server auch eine präsentationsorientierte Verarbeitung durchführt, was zu Verwirrung führt. Manchmal möchten wir auch, dass eine Domänenansicht eine gewisse Persistenz aufweist. Dies bedeutet, dass die Ansichtsparameter ein eigenes Modell bilden, das nicht unbedingt Teil des Domänenmodells ist.
Erik Eidt
Vielen Dank für den Link, ich wusste, dass ich nicht böse war, das zu denken! Dies ist im Wesentlichen das, was ich gemacht habe, bevor ich die Idee etwas zu weit gebracht habe, solange das Modell von nichts abhängig ist. Was macht es aus, wie / wo darauf zugegriffen wird? Ich habe noch nicht entschieden, welchen Ansatz ich bei meiner nächsten Entwicklung verfolgen werde, aber das hilft definitiv.
Adam Westbrook
1

Eine andere zu berücksichtigende Sache ist, dass Sie anscheinend das automatisch geladen haben user_modelund der invoice_modelAnsicht erlauben, auf sie zuzugreifen. Damit dies zuverlässig funktioniert, laden Sie wahrscheinlich alle Ihre Modelle automatisch (weil es $this->load->model()in einer Ansicht einfach falsch aussieht, nicht wahr ...)

Wenn Sie dies tun, wird Ihr Stapel unnötig aufgebläht, indem Sie eine Reihe von Dingen laden, die möglicherweise nie verwendet werden. Ein Grund für die Verwendung mehrerer Modelle besteht darin, dass Sie die zugehörige Logik kapseln und nur das laden können, was Sie für eine bestimmte Aufgabe benötigen.

Dies sieht aus wie CodeIgniter. Ich habe viel CI-Entwicklung betrieben und kann aus persönlicher Erfahrung mitteilen, dass Sie wirklich nicht mehr automatisch laden möchten, als Sie wirklich müssen. Versuchen Sie, $this->output->enable_profiler(TRUE);den Konstruktor eines Controllers hinzuzufügen, und spielen Sie mit Autoloads (einschließlich Hilfsprogrammen wie database): Sie werden wahrscheinlich eine signifikante Änderung der Lade- und Ausführungszeiten feststellen, insbesondere aber der Speicherzuweisung.

msanford
quelle
1
Gute Punkte, Sie haben Recht, dies basiert auf CI, obwohl ich aus Gründen der Klarheit einige der spezifischen Syntax entfernt habe. Ich habe mir angewöhnt, so ziemlich alles aus größtenteils zeitlichen und trockenen Gründen automatisch zu laden. Es schien ein bisschen verrückt zu sein, load->modelin den meisten Controllern und Methoden viel Gleiches zu haben . Die Nichtverwendung einer richtigen Autoload-Funktion ist eines der Dinge, die ich an der Abwärtskompatibilität von CI am wenigsten mag, aber das ist eine ganz andere Diskussion ...
Adam Westbrook
0

Die kurze Antwort lautet, dass die Form Ihres Codebeispiels täuschend intuitiv ist. Es scheint, dass dies ein "leicht zu denkender" Weg ist.


Problem Nr. 1

Ihre Modelund ViewObjekte werden eng miteinander verbunden.

Wenn Sie jemals Methoden in der hinzufügen oder entfernen müssenModel , müssen Sie diese möglicherweise Viewentsprechend ändern .

Grundsätzlich wird MVC aus den Befehls- und Beobachtermustern abgeleitet . Sie möchten ein unabhängiges 'Modell', das über eine Schnittstelle / API bearbeitet wird , in die Controllersich der Benutzer einbinden kann (dh Delegierung).

Häufig bedeutet dies, dass Instanzen in a injiziert Model und als Eigenschaften von diesen gespeichert werden . Übergeben Sie dann unter Verwendung einer Methode des (dh eines Befehls) als Arbeitsbereich Daten an a von ( nachdem das Modell die Aktualisierung des Anwendungsstatus abgeschlossen hat ).ViewControllerControllerController View Model

Durch das Übergeben von Daten (Arrays, iterierbare Objekte usw.) bleibt die Kopplung zwischen Modelund ViewInstanzen locker . Wenn Sie die ModelInstanz in das injizieren View, lesen Sie Problem Nr. 1 oben.

Denken Sie daran, dass Viewses sich nach einer REST-Methode (Representation State Transfer Method) um HTML, JSON, Text, XML, HTTP-Header, YAML oder fast alles andere handeln kann .

Der Schlüssel zum Verständnis, wie die Beziehung zwischen Modelund verwaltet werden kann, Viewsbesteht darin, die Beziehung so zu sehen, wie sie ist, eins zu viele (potenziell)! Genau dafür wurde das Observer- Muster entwickelt.

Während die meisten Setups jeweils nur eine Ansicht bearbeiten müssen, hindert nichts das MVC-Architekturmuster daran, mehrere Ansichten gleichzeitig zu aktualisieren! Die Arbeit mit herkömmlichen CRUD-Webanwendungen lässt die Leute eins zu eins denken , aber das ist das kleinste Beispiel dafür, wie das Observer-Muster funktionieren könnte ( eins zu viele ist das andere ).

Wenn Sie also eine Modeloder mehrere hatten Views, wird das potenzielle Problem , den gesamten Views'Implementierungscode zu aktualisieren , weil Sie etwas an der Model'sAPI / den Methoden geändert haben , jetzt akut .

Übergeben Sie Daten an Views , nicht an Instanzen von Models .

Anthony Rutledge
quelle