Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: MVC, a co z całą resztą?
Forum PHP.pl > Forum > PHP > Object-oriented programming
Asmox
MVC jest oczywiście jest wzorcem na ściąganie danych i obróbkę i wyświetlenie. Ale co z całą resztą? Można napisać klasy do tylko potrzebnych rzeczy, ale gdzie je poumieszczać i gdzie dawać ich wywołania?
Powiedzmy, że mam taki układ folderów:
Kod
application
|- models
|- views
|- controller

I napisałem sobie klasę powiedzmy do obsługi sesji. Gdzie ją umieścić i gdzie wstawiać kod operujący na tej klasie? Mam na myśli bardziej ogólne rozwiązanie, bo nie wiem co robić z różnymi dodatkowymi klasami, które nie mają nic wspólnego z MVC, ale są mi równie potrzebne
skowron-line
Układ katalogów to akurat najmniej istotna sprawa. Daj sobie folder classes na równi z Application
Crozin
Tak się składa, że akurat tzw. "sesja" to nic innego jak kolejny model.
Asmox
No dobra, to inny przykład: filtr danych, który zawierałby w sobie parser BB codu, cytowanie, nl2br i takie tam w klasie np. Filter. Albo klasa przerabiająca drzewko z mysql na tablicę to wypisania, która jednak nie może być wyświetlona "tak po prostu", tylko może być częścią treści?
Crozin
Dobra to inaczej:
Cytat
MVC jest oczywiście jest wzorcem na ściąganie danych i obróbkę i wyświetlenie.
Nie. MVC to wzorzec, który kreuje architekturę całej aplikacji, sposób w jaki poszczególne warstwy aplikacji się ze sobą komunikują.

To jak wygląda struktura katalogów to już jest kompletnie bez znaczenia - ot, może ona odwzorowywać nazwę klasy. Patrz na to jakie będziesz miał te klasy:
- MyProject\Controller\AbstractController
- MyProject\Views\TableView
- MyProject\TextUtils\BBCodeParser
- AnotherProject\ORM\BlahBlah
- YetAnotherProject\TableDecorator
- YetAnotherProject\NumberUtil\Converter
Zyx
Asmox -> to też jest model korzystający z usług dostarczanych przez framework smile.gif. Masz kod aplikacji i masz usługi dostarczane przez framework, a także te specyficzne dla aplikacji. One leżą sobie oddzielnie, a kod aplikacji zbudowany na bazie MVC czy czegokolwiek innego po prostu z nich sobie korzysta.

W ogóle taka dygresja, niekoniecznie związana z Twoją osobą smile.gif. Patrzę na ilość tematów poświęconych MVC i tak się zastanawiam, czemu ci wszyscy ludzie, zamiast porządnie nauczyć się programowania obiektowego, od razu rzucają się na głębokie rozkminki ze wzorcami architektonicznymi i zadają pytania typu "czy umieszczenie klasy sesji w katalogu Foo jest zgodne z MVC". Tak się składa, że osoby, które naprawdę rozumieją obiektówkę, potrafią sobie same na takie pytania odpowiedzieć i uzasadnić racjonalnie swój wybór.
thek
Asmox... Najprościej i najogólniej rzecz ujmując można napisać tak:
Model - operuje na danych, różnych jego formach, manipuluje nimi.
View - służy do prezentacji danych.
Controller - steruje przepływem informacji i procesami aplikacji.

Rzuciłeś sesją. Ma ona gdzieś pokazywana? Nie. Wylatuje widok. Czy sesja w jakiś sposób steruje przepływem informacji w aplikacji? Nie sama w sobie jest informacją i sama siebie kontroluje oraz modyfikuje. Jest więc warstwą operacji na danych - jest więc modelem.

Rzucasz filtry przykładowe? Proszę bardzo... Zwróć uwagę, że to nic innego niż zmiana, manipulacja danymi, nie wykraczająca dalej. Nie są one bowiem ani wyświetlane, ani nie jest wymagana ingerencja zewnętrzna w ten proces. Dostajesz polecenie: weź A i zrób z tego B. Znowu kłania się warstwa modelu. Warstwy mogą ze sobą współpracować.Przykład? Widok wyświetla pewne informacje, które idą do Kontrolera. Ten decyduje na podstawie działań użytkownika, że Model musi zmienić strukturę danych do innej formy i przekazać ją do Widoku ponownie.Na tym właśnie polega MVC. Rozgraniczanie obsługi pewnych czynności i funkcjonalności do wybranych warstw za nie odpowiedzialnych.

Tak jak powiedział Zyx... Nie zrozumiesz, lub zrobisz to w sposób niekoniecznie dobry jeśli zaczniesz naukę "od dupy strony" winksmiley.jpg Nie buduje się samolotu, nie mając pojęcia o aerodynamice. Tak samo zrozumienie wzorców projektowych bez zrozumienia czym jest i z czym się je programowanie obiektowe, jest zwyczajnie skakaniem do wody bez znajomości dna. Niektórym się uda złapać ideę "na czuja" ale część złamie kręgosłup.

Ja przez naprawdę długi czas nie wiedziałem o czymś takim jak wzorce projektowe. Ale nieświadomie je stosowałem bo wychodziły naturalnie podczas pisania. gdy zacząłem je poznawać po prostu usystematyzowałem wiedzę i tyle. Różnica jednak polegała na tym, że zanim zacząłem je stosować świadomie, miałem już kilka lat nauki obiektówki w C++ i liźnięcie Javy, która jest bardziej rozbudowana w tych dwóch językach niż w PHP. Tam specyfikatory dostępu, dzielenie zakresów działania, dziedziczenie, hermetyzacja są naturalnym elementem języka. Obiektówki się na tej bazie można uczyć i zrozumienie potem tego co w PHP lub dowolnym innym języku z nią próbują zrobić jest analogiczne. Są różnice, ale znając ogólne zasady widzisz tylko je w porównaniu do standardu. I tylko ich się uczysz. Dzięki temu nie uczysz się standardów kilku języków, ale jeden i różnice w stosunku do niego. Jest to prostsze.
Kuziu
Podczepię się pod temat smile.gif

thek napisałeś:
Cytat
Model - operuje na danych, różnych jego formach, manipuluje nimi.
View - służy do prezentacji danych.
Controller - steruje przepływem informacji i procesami aplikacji.


Ok, dajmy na to mamy edycję posta na forum.
Zadania jakie mamy do wykonania to:
1. Odebranie zapytania http
2. Pobranie posta
3. Sprawdzenie czy on istnieje
4. Wpisanie nowej treści
5. Zapis
6. Wyświetlenie

I teraz moje pytanie kto ma za co odpowiadać:
1. Controller
2. Controller za pomocą Modelu
3. Controller za pomocą Modelu
4. Controller za pomocą Modelu
5. Controller za pomocą Modelu
6. Controller za pomocą Widoku

Czy dobrze rozumuję? Jeśli tak to czy taki kod z użyciem propela byłby poprawny?

Kod w kontrolerze:
  1. protected function editPost()
  2. {
  3. $post = PostQuery::create()->findOneById($_POST['postId']);
  4. if($post)
  5. {
  6. $post->setText($_POST['text']);
  7. $post->save();
  8.  
  9. // Tutaj wyswietlenie posta lub przekierowanie na topic w ktorym byl zawarty
  10. }else{
  11. //Blad
  12. }


O co mi chodzi... Wszystko co piszę, piszę praktycznie tylko i wyłącznie w Kontrolerze (oczywiście poza widokiem), a Modelu praktycznie nie dotykam. Wywołuję tylko jego metody których dostarcza mi Propel. Czy to poprawne podejście? Czy np. sprawdzenie czy post istnieje i wpisanie treści do niego nie powinno odbyć się w modelu ?

Z góry dzięki za jakiekolwiek rady.
Crozin
1. Nie, MVC akurat w ogóle nie określa jak ma zostać obsłużone żądanie HTTP.
2. Tak.
3. Model - w przypadku nieistnienia może wywalić wyjątek. Kontroler może co najwyżej obsłużyć ten wyjątek - ale nie musi.
4. Tak.
5. Tak.
6. Widok za pomocą modelu.

Cytat
Jeśli tak to czy taki kod z użyciem propela byłby poprawny? [...] Wywołuję tylko jego metody których dostarcza mi Propel. Czy to poprawne podejście?
Utożsamienie modelu z ORMem to podstawowy błąd. Model może co najwyżej tego ORMa wykorzystywać w swoich wnętrznościach.
Kuziu
Czyli powinienem mieć np. klasę modelu obsługującą posty forum a w niej funkcję editPost() i z kontrolera wywołać tylko model->editPost() z ewentualnie zwróconym wyjątkiem tak? A resztą powinien zająć się model.

Czyli tak:

Klasa modelu
  1. public function editPost()
  2. {
  3. $post = PostQuery::create()->findOneById($_POST['postId']);
  4. if($post)
  5. {
  6. $post->setText($_POST['text']);
  7. $post->save();
  8. }else{
  9. // Zwrócenie wyjątku do kontrollera
  10. }
  11. }


Klasa kontrolera:
  1. protected function editPost()
  2. {
  3. try
  4. {
  5. $modelForum->editPost(); // Przekazuję całą edycję do modelu
  6. //Przekierowanie na topic z tym postem
  7. }catch(Exception){
  8. $widok->KomunikatBledu();
  9. }
  10. }


O coś takiego chodzi?

A może jeszcze inaczej tak:
  1. protected function editPost()
  2. {
  3. try
  4. {
  5. $post = $modelForum->getPost(); // Pobieram posta
  6. $modelForum->editPost($post); // Edytuję go
  7. }catch(Exception){
  8. $widok->KomunikatBledu(); // Oczywiście odpowiedni na podstawie błędu
  9. }
  10. }


W tym pierwszym przypadku przekazuję całe zadanie modelowi, w drugim pobieram najpierw post, a potem przekazuję go spowrotem do modelu w celu edycji.


Cytat
Utożsamienie modelu z ORMem to podstawowy błąd.

A czy jeśli rozszerzam klasy ORM'a o własne metody, które modyfikują w jakiś sposób dane to nie są one modelem?
Zyx
Podstawowe kryterium brzmi: czy Twoja architektura pozwala Ci zamiast ORM-a użyć czegoś innego? Jeśli da się jako model reprezentować operacje na plikach, transmisję przez sieć czy cokolwiek innego, to OK. Od biedy takie rozwiązanie ujdzie, choć wg mnie jest ono mało elastyczne i w ten czy inny sposób prawie zawsze wymusi na Tobie przeniesienie części odpowiedzialności modelu gdzie indziej. Natomiast jeśli masz sytuację, gdzie model musi być ORM-em (błąd popełniany przez autorów co najmniej połowy frameworków), to jest już nieciekawie, bowiem Twoja aplikacja formalnie może tylko z bazy danych korzystać i z niczego innego, jako że nie możesz tego normalnie odwzorować w systemie.
Kuziu
Nie ograniczam się tylko do ORM chociaż szczerze mówiąc w 90% korzystam z jego funkcjonalność gdyż większość operacji jednak jest wykonywana na bazie danych.

Co do rozwiązania, mógłbyś trochę szczegółowiej opisać o tej elastyczności, którą według Ciebie tracę?
Crozin
1. Model nigdy, ale to absolutnie nigdy nie powinien korzystać z danych globalnych (w ogóle nic nie powinno z nich korzystać, poza kilkoma drobnymi wyjątkami) takich jak tablica $_POST.
2. Jeżeli chcesz używać ORMa w aplikacji MVC zastanów się nad użyciem Doctrine2 - ma on inną architekturę niż Propel czy wersja 1 - niewspółmiernie bardziej przyjazną dla środowiska MVC.
Zyx
Najprostszy możliwy przykład: masz sobie widok do wyświetlania listy elementów. I teraz wyobraź sobie, że chcesz wyświetlić sobie nagłówki czegośtam pobranego przez RSS lub jakąś usługę sieciową. Jeśli powiesz, że "OK, do operacji CRUD interfejsem modelu będzie ten definiowany przez ORM-a", masz lipę, ponieważ najprawdopodobniej Twój widok będzie umiał operować tylko na modelach z bazy i nie będzie potrafił obsłużyć modelu RSS, który formalnie będzie posiadać nieco inny interfejs. Gdyby Twój interfejs modelu dla operacji CRUD i wyświetlania listy elementów był bardziej ogólny, a operacje ORM byłyby pod nim ukryte, problem by nie istniał. Zresztą, nawet nie muszą to być usługi sieciowe. Wystarczy, byś chciał wyświetlić listę plików w katalogu, albo zawartość wgranej na serwer paczki z pluginem.

Drugi rodzaj elastyczności dotyczy samej struktury bazy danych i tego, co jest w niej trzymane. Podam Ci przykład z systemu do automatycznego testowania programów, który rozwijam. Programy są ładowane przez odpowiednie formularze, więc aplikacja operuje bardzo dużą ilością plików. W pierwotnej wersji pliki były składowane na dysku, a meta-informacje o nich były w bazie. Później jednak zdecydowaliśmy się wszystko wpakować do bazy, gdyż utrzymanie spójności takiej struktury stawało się coraz trudniejsze. I tu pojawiła się lipa, bo system był zrobiony na Zend Frameworku, gdzie ORM == Model (Doctrine 1) i nagle się okazało, że aby te pliki były w bazie, muszę przepisać... większość "kontrolerów". Fajnie, co nie?
thek
W przypadku edycji posta i MVC część wyjaśnił Ci już Crozin. Ogólnie wyglądało by to tak, że kontroler ze strony dostaje wszystkie dane i musi zdecydować co zrobić. Ogólnie powinien te dane pchnąć do modelu (nawet dane superglobalne powinny do niego iść jako parametr a nie jawnie poprzez użycie choćby $_POST już wewnatrz), który zacznie odwalać całą robotę. Błędy zaś zasygnalizuje kontrolerowi. Jeśli jednak wszystko pójdzie OK to są dwa podejścia:
1. Model sam wywołuje Widok
2. Model zwraca obrobione dane do Kontrolera, który decyduje o tym jaki Widok zajmie się ich wyświetleniem.
To drugie rozwiązanie jest najczęściej spotykane we frameworkach.
Crozin
@thek:

Ad. 1) Chyba na odwrót? Bo jak model miałby wywołać widok?
Ad. 2) I nie ma to wtedy zbyt wiele wspólnego z MVC... w dodatku w takim przypadku widok = szablon.
zzeus
No to ja też mam małe pytanko w temacie, gdzie np. walidować dane - w kontrolerze czy w modelu ?
marcio
Cytat(zzeus @ 17.12.2010, 09:56:07 ) *
No to ja też mam małe pytanko w temacie, gdzie np. walidować dane - w kontrolerze czy w modelu ?

kontroler....w modelu co najwyzej mozesz walidowac "operacje" na plikach/baza danych/sockety itp...itd...
thek
ad1) i tutaj właśnie dotykamy tego czym jest faktycznie i jak powinno działać MVC smile.gif W tym wzorcu każda z warstw może wywołać pozostałe. Jeśli tak napiszesz Model, że może sam zadecydować o formie wyjściowej danych (html, xml, pdf, co_tam_chcesz) na podstawie jakichś przesłanek podczas operacji na nich (informacja zaszyta w nich, przykładowo jako pole w bazie informujące z czym mamy do czynienia lub jaka jest preferowana forma), to powiedz mi, czy to nie jest wtedy możliwe? winksmiley.jpg Nie wychodzimy bowiem z Modelu nawet na ułamek sekundy. W zależności od typu Model sam zgłosi chęć wykorzystania właściwego dla danych Widoku. Tak samo Widok, poprzez użycie AJAX może bezpośrednio wywołać Model, by powiedzieć "daj mi tu inne dane, migiem!". Wiem, że mówię dość ogólnie teraz, ale na tym właśnie idea MVC prawdziwego polega. Każda z warstw ma bezpośredni dostęp do siebie, nie jak w pseudo-MVC, gdzie przykładowo Kontroler zestawia połączenie między Modelem i Widokiem.

ad2) Masz rację. Ale czyż widoki nie są tak naprawdę szablonami? Widok ma po prostu dostać dane i je zaprezentować w jakiejś formie, którą ma zaprogramowaną. W zależności od danych może je nieco inaczej przedstawić, ale na tym się jego funkcjonalność kończy. Zmiana formy prezentacji pociąga za sobą pewne zmiany w pracy Modelu, przestawienie go na nieco inny tryb, gdzie używa innych funkcji własnych. Najprościej to "pokazać". Masz dane które normalnie wyświetlasz jako strony, ale możesz także w GUI określić, że "jeśli mam ten przełącznik aktywny w widoku, to nie wyświetlaj ich, ale zwracaj mi w wyniku pliki pdf.". Takie działanie zmusiło by Model do nieco innej pracy.
Crozin
1) Pytałem w kontekście aplikacji webowej, gdzie jest to niemożliwe ze względu na środowisko pracy (protokół HTTP). Chociaż w sumie można to już teraz osiągnąć wykorzystując WebSockets (albo [tutaj nazwa projektu/ów] które to symulują tam gdzie nie ma natywnego wsparcia dla WS), ale nie AJAX.

2) Gdyby widok miałby być szablonem, to wzorzec by się nazywał MTC - nie byłoby potrzebny na wprowadzanie nowych terminów. Tak jednak nie jest.
  1. Widok może dostać dane (kontroler może mu je dostarczyć), ale widok głównie sam je sobie pobiera z modelu. Ten ostatni już raczej sam nie przekazuje danych widokowi co najwyżej informuje go, że coś się zmieniło, możesz sobie pobrać dane ponownie.
  2. Widok to nie tylko wyświetlanie danych, to także logika widoku. Ot załóżmy, że na stronie mamy Wyświetlaj 10, 20, 50 rekordów na stronę. To w gestii widoku jest poinformowanie modelu, że tym razem ma zwrócić 50, nie 10 rekordów.


Cytat
kontroler....w modelu co najwyzej mozesz walidowac "operacje" na plikach/baza danych/sockety itp...itd...
Oczywiście, że nie kontroler, a model. Ten pierwszy nawet nie do końca może wiedzieć z czym ma do czynienia, tylko i wyłącznie model wie jakie dane są dla niego odpowiednie.
mike
Cytat(thek @ 17.12.2010, 11:20:24 ) *
Tak samo Widok, poprzez użycie AJAX może bezpośrednio wywołać Model, by powiedzieć "daj mi tu inne dane, migiem!".
No teraz to już pojechałeś.
To co piszesz to nie jest odwołanie widoku do modelu tylko kompletnie niezależne żądanie HTTP. Kompletnie inne żądanie które uruchomi kontroler, model i widok (pewnie będzie coś w stylu plain).
Opis, który podałeś to nie jest żadne odwołanie widoku do modelu. Nie pisz bzdur.
Zyx
Widok to też nie szablon. Klasyczny przykład: gdzie konfigurujesz stronicowanie listy wyników? W kontrolerze czy w widoku? Powinieneś w widoku. Gdzie konfigurujesz stronicowanie listy wyników? W szablonie, czy w aplikacji PHP? Powinieneś w aplikacji PHP. Widzisz? Mała sprzeczność. Warstwa prezentacji też ma swoją własną logikę. W przypadku niektórych szablonów jest ona istotnie zredukowana do "pobierz dane i przekaż do szablonu", ale sytuacje, gdy trzeba dodatkowo coś skonfigurować itd. są tak częste, że aż szkoda o tym mówić. Ponadto co w sytuacji, gdy chcesz wyprodukować dokument PDF np. z fakturą? Generacja PDF-a na podst. danych z modelu to zadanie widoku, a jeszcze nie widziałem systemu szablonów, który potrafiłby coś takiego sensownie wykonać. Odpowiedź na żądanie AJAX? Format JSON lub XML się kłania - też obie te rzeczy dużo łatwiej jest obsłużyć w sposób inny, niż szablony.

Sprawdzanie poprawności danych zasadniczo także powinno być zadaniem modelu, ponieważ przecież to model wie, jakie dane przetwarza i w jaki sposób.

thek -> jaki AJAX? Widok PHP w środowisku WWW kończy pracę w momencie wysłania danych do przeglądarki.
marcio
Cytat
Sprawdzanie poprawności danych zasadniczo także powinno być zadaniem modelu, ponieważ przecież to model wie, jakie dane przetwarza i w jaki sposób.

W takim razie wykluczamy kontroler praktycznie ze wszystkiego, to po co on w ogole jest? sleep.gif'
Np walidacje formularzy mialbym trzymac w modelu...?!?
Crozin
Cytat
W takim razie wykluczamy kontroler praktycznie ze wszystkiego, to po co on w ogole jest?
Głównie do ustalenia, że ten widok ma współpracować z tym modelem(ami). Dodatkowo może jeszcze jakieś pierdoły robić jak sprawdzanie jakiegoś ACLa, logi itp. Chociaż to ostatnie w przypadku języków wspierających paradygmat AOP jest również dosyć mocno zredukowane. winksmiley.jpg

Cytat
Np walidacje formularzy mialbym trzymac w modelu...?!?
Tak exclamation.gif!111oneoneone Bo formularz to kolejny model. A dokładniej interfejs pozwalający przekazać temu modelowi pewne dane.

Model jest najbardziej rozbudowaną warstwą aplikacji.
marcio
Cytat
Model jest najbardziej rozbudowaną warstwą aplikacji.

Nom wlasnie to zauwazylem, jakos wole podejsce MVP jesli chodzi o aplikacje w srodowisku http jest to takie bardziej naturalne i bardziej pasuje do tego typu idei.

Cytat
Bo formularz to kolejny model. A dokładniej interfejs pozwalający przekazać temu modelowi pewne dane.

Jakby na to nie patrzec to kurde masz racje....
Crozin
W MVP nadal będziesz miał model-kobyłę. Aż tak wiele się tutaj nie zmienia. I oba podejścia są naturalne, podobnie jak tak samo naturalny jest system dziesiętny (0..9) i ósemkowy (0..7), ale jestem pewien, że ten pierwszy wydaje Ci się dużo naturalniejszy. Czemu? Bo jesteś do niego przyzwyczajony.
thek
Mike, Zyx: Wiem, ale to, że Widok nie ma możliwości wprost połączenia z Modelem jest właśnie ułomnością protokołu, a nie wzorca samego w sobie. Dlatego użyłem tego AJAX by służył jako obejście problemu i stał się "transporterem - strzałką" pomiędzy nimi, bo innej możliwości niż jakiś twór połączeniowy nie mamy by rozwiązać tę sytuację jakoś normalnie. Pełne zrobienie w PHP jest zwyczajnie niemożliwe. Zawsze będzie wymagane "coś pomiędzy" M i V, bo między M i C oraz C i V jest to możliwe. Dla tego wzorca tylko połączenie M i V musi być "emulowane". Właśnie w to miejsce najwygodniej wstawić AJAX, który może i łamie "czystość" tego wzorca, ale jest moim zdaniem jedną z niewielu sensownych możliwości rozwiązania tej ułomności protokołu. A i to jest niepełne, ponieważ tworzy połączenie zwrotne Model->Widok jedynie po Widok->Model, na dodatek nie zawsze. Model sam z siebie nie zrobi nic. To że wprowadza takie zagranie kolejne MVC (co powiedział mike) jest niestety nieuniknione, bo taka jest natura http. My możemy najwyżej to w jakiś sposób ukrywać poprzez stosowanie określonych technik, by było w jakiś sposób transparentne.

Myślę, że teraz trochę wyjaśniłem dlaczego tak napisałem. Moja wina jednak, że faktycznie za bardzo spłyciłem całość.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.