Czy w MVC można / należy pobierać podstawowe dane z modelu w widoku?

10

Biorąc pod uwagę koncepcję „chudych kontrolerów, grubych modeli” i ogólną akceptację, że widoki mogą bezpośrednio wywoływać modele, gdy wymagają danych do danych wyjściowych, czy należy rozważyć obsługę części „pobierania i wyświetlania” żądań w widokach, a nie kontrolera? Na przykład (próbował zachować kod dość ogólny):

Kontroler

<?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');

    }

}

Widok

<?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>

Dla mnie ma to przynajmniej sens w przypadkach, w których żądanie jest zasadniczo tylko widokiem. Dlaczego administrator powinien zbierać i przekazywać dane do widoku, skoro może je po prostu odzyskać? To pozostawia kontroler otwarty na przetwarzanie „na poziomie aplikacji” (np. Obsługę żądań GET / POST, zarządzanie prawami dostępu i uprawnieniami itp.), A także zachowanie Modelów wielokrotnego użytku i innych dobrych rzeczy.

Jeśli ten przykład został rozszerzony, aby umożliwić użytkownikowi filtrowanie wyników, kontroler po prostu obsłużyłby test POST z formularza i przekazał filtry do widoku, który następnie zażądałby danych ponownie, tym razem z filtrami.

Czy jest to poprawne podejście do tworzenia aplikacji MVC? Czy też pomijam ważną rolę, jaką powinien odgrywać Kontroler?

Adam Westbrook
źródło

Odpowiedzi:

17

Tak, technicznie można to zrobić. Nie, nie należy tego robić. I tak, brakuje ci trochę tego, do czego służy kontroler.

Kontroler służy do oddzielenia widoku od modelu. Oddzielenie jest korzystne, ponieważ należy patrzeć na widok jako prawie wyrzucany kod. Wraz ze zmianą technologii interfejsu użytkownika, chcesz zminimalizować liczbę przeróbek wymaganych do wygenerowania nowego widoku. Kontroler umożliwia to oddzielenie i zapewnia miejsce na Twój kod, który będzie działał dzięki technologiom interfejsu użytkownika.

Działa również w odwrotnej kolejności, jeśli chcesz dodać lub zmienić swój model. Wszystkie wcześniejsze zmiany będą zawarte w Kontrolerze, a Twoje Widoki pozostaną same.

Innym ryzykiem jest to, że chociaż Widok jest teraz bardzo prosty , masz mniej gwarancji, że pozostanie taki prosty przez całe życie. Dzwoniąc do Modela bezpośrednio z (bardzo prostego) Widoku, otworzyłeś nieco drzwi, aby umożliwić dodatkowe złe praktyki, aby wkraść się później, gdy bardzo prosty Widok musi stać się nie tak bardzo prosty. Przyszły programista będzie miał pokusę, aby wykonać więcej wywołań Modelu z niezbyt prostego Widoku zamiast refaktoryzować kod i wchodzić w interakcje z Kontrolerem.


źródło
1
Świetna odpowiedź, dziękuję. Lekko przedłużając scenariusz „patrzenia w przyszłość”; jeśli na stronie znajdują się wspólne informacje, które są odrębne od żądanych (np. użytkownik ogląda określony produkt, z boku pokazana jest ogólna lista „najnowszych ofert specjalnych”), w jaki sposób / gdzie należy zadzwonić offers_model->get_latest()? Dodanie tego do każdej metody w kontrolerze (jak wcześniej głupio próbowałem) wydaje się przesadą i wyraźnie pozbawione SUSZENIA.
Adam Westbrook,
2
@AdamWestbrook Spójrz na MVVM. Część ViewModel, która może rozwiązać ten konkretny problem. Można dodać offers_model->get_latest()do ProductViewModelklasy bazowej lub coś podobnego.
Zachary Yates
1
Świetnie, na pewno zajrzę do MVVM, jeszcze raz dziękuję.
Adam Westbrook,
Bardzo dobra odpowiedź, zdecydowanie zachowa tę gwiazdkę. Osobiście jestem także wielkim fanem MVVM :)
Benjamin Gruenbaum
@BenjaminGruenbaum Czy używasz MVVM w PHP? Jeśli tak, czy używasz do tego konkretnego środowiska?
Adam Westbrook
6

Biorąc pod uwagę koncepcję „chudych kontrolerów, modeli tłuszczu” i ogólną akceptację, że widoki mogą bezpośrednio wywoływać modele, gdy wymagają danych do wydruku

Nie. To nie jest poprawne. Widok nie może bezpośrednio wywoływać modeli. Widoki nie powinny mieć dostępu do obiektów modelu, chyba że z jakiegoś powodu programista wystawił te obiekty na widok.

należy rozważyć obsługę części „pobierz i wyświetl” w widokach, a nie w kontrolerze?

To zasadniczo wymazuje kontroler i pokonuje sens posiadania go.

Dlaczego administrator powinien zbierać i przekazywać dane do widoku, skoro może je po prostu odzyskać?

Administrator nie zbiera danych. Model zbiera dane. Administrator decyduje, czy dane te należy przekazać do widoku. Widok wykonuje tylko prezentację danych.

Jeśli ten przykład został rozszerzony, aby umożliwić użytkownikowi filtrowanie wyników, kontroler po prostu obsłużyłby test POST z formularza i przekazał filtry do widoku, który następnie zażądałby danych ponownie, tym razem z filtrami.

Nie.

Kontroler sprawdza, czy dane POST są poprawne, a następnie przekazuje te dane jako opcje do modelu, który następnie wysyła zapytanie do źródła danych i zwraca dane, a kontroler przekazuje je do widoku.

Czy jest to poprawne podejście do tworzenia aplikacji MVC? Czy też pomijam ważną rolę, jaką powinien odgrywać Kontroler?

Kontroler działa jako moduł obsługi żądań przeglądarki. Dyspozytor wysyła żądanie do akcji kontrolera, która z kolei rozsyła żądanie do modeli. Modele zawierają całą logikę biznesową (jest to gruba część) i zwracają dane administratorowi. Kontroler może następnie uprościć i dostosować dane, aby widok mógł je łatwiej wyświetlić.

Celem widoku jest rozdzielenie struktury i zależności między prezentacją HTML a źródłem danych. Chociaż może to być trudne. Widoki nie zawsze prezentują dane pochodzące bezpośrednio z modelu. Administrator często dodaje dodatkowe istotne dane.

Jestem pewien, że na MVC jest wiele samouczków. Poleciłbym przeczytać niektóre z nich.

Reactgular
źródło
Dzięki Mathew. Dla wyjaśnienia, do tej pory zawsze odłączałem widok i model od kontrolera zgodnie z treścią i sugestią. Jednak odkąd zacząłem czytać o utrzymywaniu „chudych” kontrolerów, właśnie zastanawiałem się, co należy / można z nich usunąć, wydaje się, że proces myślowy, który doprowadził mnie do tego pytania, był o krok za daleko!
Adam Westbrook,
Kiedy zaczniesz otrzymywać Modele używane przez wiele kontrolerów. Potrzeba, aby były tłuste, staje się bardzo wyraźna. Kiedy widok zaczyna zawierać dużo PHP, wiesz, że twój kontroler jest cienki. Kiedy twoi kontrolery są bardzo grubi. Trudno jest zmusić inne kontrolery do działania w ten sam sposób (na przykład poprzez dodanie usługi API).
Reactgular,
3

Uważam, że twoje pytanie jest bardzo interesujące, ponieważ podczas nauki języka Python napotkałem ten sam problem.

Choć udzielone odpowiedzi stanowią przekonujący argument, pomyślałem, że dodam kolejną opinię, w której natknąłem się na widok, w którym widok uzyskuje stan modelu bez przechodzenia przez kontroler.

MVC

Należy zauważyć, że zarówno widok, jak i kontroler zależą od modelu. Jednak model nie zależy ani od widoku, ani od kontrolera. Jest to jedna z kluczowych korzyści z separacji. Ta separacja pozwala zbudować i przetestować model niezależnie od prezentacji wizualnej. Separacja między widokiem a kontrolerem jest drugorzędna w wielu aplikacjach z bogatym klientem, a w rzeczywistości wiele ram interfejsu użytkownika implementuje role jako jeden obiekt. Z drugiej strony w aplikacjach internetowych separacja między widokiem (przeglądarką) a kontrolerem (komponenty po stronie serwera obsługujące żądanie HTTP) jest bardzo dobrze zdefiniowana.

Model-View-Controller to podstawowy wzorzec projektowy służący do oddzielenia logiki interfejsu użytkownika od logiki biznesowej. Niestety popularność tego wzoru spowodowała szereg błędnych opisów. W szczególności termin „kontroler” został użyty do oznaczenia różnych rzeczy w różnych kontekstach. Na szczęście pojawienie się aplikacji internetowych pomogło rozwiązać niektóre niejasności, ponieważ separacja między widokiem a kontrolerem jest tak widoczna.

W Programowaniu aplikacji w Smalltalk-80: Jak korzystać z Model-View-Controller (MVC) [Burbeck92], Steve Burbeck opisuje dwie odmiany MVC: model pasywny i model aktywny.

Model pasywny jest stosowany, gdy jeden kontroler manipuluje modelem wyłącznie. Sterownik modyfikuje model, a następnie informuje widok, że model się zmienił i należy go odświeżyć (patrz rysunek 2). Model w tym scenariuszu jest całkowicie niezależny od widoku i kontrolera, co oznacza, że ​​model nie ma możliwości zgłaszania zmian w swoim stanie. Protokół HTTP jest tego przykładem. W przeglądarce nie ma prostego sposobu na uzyskanie asynchronicznych aktualizacji z serwera. Przeglądarka wyświetla widok i reaguje na dane wprowadzone przez użytkownika, ale nie wykrywa zmian danych na serwerze. Tylko wtedy, gdy użytkownik wyraźnie zażąda odświeżenia, serwer jest pytany o zmiany.

MVC - model pasywny

Nie jestem w stanie powiedzieć, która z opinii jest „słuszna”, i szczerze mówiąc, jestem trochę bardziej zdezorientowany po przeczytaniu tutaj odpowiedzi i powiązanego artykułu.

Pełny tekst artykułu tutaj .

alnafie
źródło
Zgadza się, a drugą kwestią, która wprowadza zamieszanie, jest klient-serwer, którego tak naprawdę nie uwzględniał oryginalny SmallTalk MVC. W kliencie-serwerze (np. Z javascript) istnieją modele, widoki i kontrolery zorientowane na prezentację na kliencie oraz widoki i kontrolery zorientowane na domenę na serwerze, chociaż serwer wykonuje również pewne przetwarzanie zorientowane na prezentację, co powoduje zamieszanie. Czasami chcemy też, aby widoki domen miały pewną trwałość, co oznacza, że ​​parametry widoku tworzą własny model, który niekoniecznie jest częścią modelu domeny.
Erik Eidt
Dziękuję za link, wiedziałem, że nie oszalałem na myśl o tym! Zasadniczo o to mi chodziło, zanim trochę posunąłem się za tym pomysłem, o ile Model nie jest zależny od niczego, co ma znaczenie, w jaki sposób / gdzie jest dostępny? Nie zdecydowałem jeszcze, jakie podejście zamierzam zastosować przy kolejnym rozwoju, ale to zdecydowanie pomaga.
Adam Westbrook
1

Inną rzeczą do rozważenia jest to, że prawdopodobnie załadowałeś ją automatycznie user_modeli invoice_modelpozwalasz widokowi na dostęp do nich. Aby działało to niezawodnie, prawdopodobnie automatycznie ładujesz wszystkie swoje modele (ponieważ $this->load->model()po prostu wygląda źle w widoku, prawda?)

Robiąc to niepotrzebnie nadymasz swój stos, ładując kilka rzeczy, które mogą nigdy nie zostać wykorzystane. Jednym z powodów posiadania wielu modeli jest umożliwienie enkapsulacji powiązanej logiki i załadowanie tylko tego, czego potrzebujesz do danego zadania.

To wygląda jak CodeIgniter. Dużo opracowałem CI i mogę podzielić się z własnego doświadczenia, że ​​tak naprawdę nie chcesz automatycznie ładować więcej, niż naprawdę musisz. Spróbuj dodać $this->output->enable_profiler(TRUE);konstruktora kontrolera i bawić się przy autoloadach (w tym pomocnikach takich jak database): prawdopodobnie zauważysz znaczącą zmianę w czasie ładowania i wykonania, ale szczególnie w alokacji pamięci.

msanford
źródło
1
Dobre punkty, masz rację, to jest oparte na CI, chociaż usunąłem niektóre ze specyficznej składni dla przejrzystości. Przyzwyczaiłem się do „automatycznego ładowania” prawie wszystkiego z powodów czasowych i SUCHYCH. Wydawało mi się, że mam trochę tego samego load->modelw większości kontrolerów i metod. Niewłaściwe użycie funkcji automatycznego ładowania jest jedną z rzeczy, których najbardziej nie lubię w kompatybilności wstecznej CI, ale to zupełnie inna dyskusja ...
Adam Westbrook
0

Krótka odpowiedź brzmi: forma próbki kodu jest zwodniczo intuicyjna. Wydawałoby się, że jest to droga „bezproblemowa”.


Problem nr 1

Twoje Modeli Viewprzedmioty będą ściśle powiązane.

Jeśli kiedykolwiek będziesz musiał dodać lub usunąć metody w Model, być może będziesz musiał odpowiednio zmienić View.

Zasadniczo MVC jest pozbawione wzorców Dowodzenia i Obserwatora . Chcesz niezależnego „Modelu”, którym można manipulować za pomocą interfejsu / interfejsu API , do którego Controllermożna się podłączyć (tj. Delegowanie).

Często oznacza to wstrzykiwanie Model i Viewinstancje do Controlleri przechowywanie ich jako właściwości wspomnianych Controller. Następnie, używając metody Controller(tj. Polecenia) jako obszaru roboczego, przekaż dane do View z Model ( po zakończeniu `Modelu aktualizacji stanu aplikacji ).

Uboczny danych (tablice, iterable obiekty, cokolwiek) utrzymuje sprzęgających pomiędzy Modeli Viewinstancje luźne . Jeśli wstrzykniesz Modelinstancję do View, zobacz problem nr 1 powyżej.

Pamiętaj, że Viewsmoże to być HTML, JSON, Tekst, XML, nagłówki HTTP, YAML lub prawie wszystko, zgodnie z metodologią transferu stanu reprezentacji (REST) .

Zatem kluczem do zrozumienia, jak zarządzać relacją między Modeli, Viewsjest dostrzeżenie relacji takiej, jaka jest, jeden do wielu (potencjalnie)! Właśnie do tego został zaprojektowany wzór Observer .

Podczas gdy większość konfiguracji ma tylko jeden widok, którym można się zajmować na raz, nic nie stoi na przeszkodzie, aby wzorzec architektoniczny MVC aktualizował wiele widoków jednocześnie! Praca z tradycyjnymi aplikacjami sieciowymi CRUD sprawia, że ​​ludzie myślą w sposób jeden do jednego , ale jest to najmniejszy przykład tego, w jaki sposób mógłby działać wzorzec Observer ( jeden do wielu ).

Tak więc, jeśli miałeś jeden Modeli wiele Views, potencjalny ból głowy związany z aktualizacją całego Views'kodu implementacji, ponieważ zmieniłeś coś w Model'sAPI / metodach, staje się teraz ostry .

Przekaż dane Views , a nie wystąpienia Models .

Anthony Rutledge
źródło