Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [PHP]podział na klasy
Forum PHP.pl > Forum > Przedszkole
perhydrol
Witam,

od jakiegoś czasu eksperymentuję z obiektowym PHP, jednak moje strukturalne myślenie skutecznie mi to utrudnia.
Tylko proszę nie odsyłajcie do przeszukiwania tematów jak wielokrotnie twierdzicie iż temat był poruszany tysiące razy,
ja jednak szukając odpowiedzi w postach już istniejących na tym forum nie znalazłem satysfakcjonującej odpowiedzi.

Mam prośbę, czy mógł by ktoś mi wypisać na przykładzie sklepu internetowego podział na klasy, oraz zakwalifikować je do odpowiednich części:
Model, View, Controller

Coś w stylu listingu klas, i nie jest to z mojej strony wyręczanie się Wami, a jedynie chciał bym dostrzec jak szczegółowo należy podejść do podziału danej dziedziny sklepu internetowego na klasy. Dzięki przypisaniu ich do odpowiednich części MVC, myślę że będę mógł szybciej i lepiej zrozumieć to z czym się borykam. Dodam może że do UML-a używam "NetBeans IDE 6.7.1"

a interesujące mnie rozwiązanie to coś w stylu:

  1. class DB_Connect // klasa inicjująca połączenie z baza danych
  2. class DB_View // klasa zapytań do bazy danych
  3. .... // tu kolejne pytanie czy jest sens przechowywać wszystkie zapytania do bazy danych, np SELECT ... w jednym pliku?
  4. .... // no i jeszcze jak by mógł mi ktoś zaznaczyć która klasa do jakiej części modelu MVC się zalicza
thek
MVC robią dokładnie to co sugeruje nazwa. Różnica polega na tym, że pewne klasy nie będą miały wszystkich trzech. Przykład? Klasa połączenia z bazą danych. Zauważ, że ona nie musi mieć widoku, gdyż nigdzie nie prezentujesz danych tylko przepychasz je do innych klas. Myśl własnie w ten sposób. Jeśli chcesz jakieś dane uwidaczniać, to projektuj do tego widok. Chcesz wyciągać dane? Pomyśl o modelu. Gdy trzeba nad tym wszystkim odpowiednio popracować to nie obejdzie się bez kontrolera.. Jak więc widzisz kontroler jest w zasadzie zawsze, bo musi kierować poczynaniami i przepływem danych. Czasem niepotrzebny jest klasie model, jeśli nie operujesz na danych pobieranych z zewnątrz (baza czy pliki).
Ja osobiście robię tak, że mam zawsze główny kontroler dziedziczący po jakimś template i do niego wrzucam widoki. Jeśli któryś z widoków pobiera dane to ma on dostęp albo do dedykowanego mu modelu, albo korzysta z pewnego globalnego modelu. Jeśli zaś ten widok ma podwidoki, to mają one zazwyczaj własne kontrolery i własne widoki. Całość układa się wtedy w swego rodzaju drzewko zależności. W takiej sytuacji do widoku głównego przez główny kontroler są wysyłane mniejsze widoki korzystające z własnych kontrolerów, widoków i modeli (choć mogą korzystać z pewnego głównego modelu dla danych lub nie korzystać z modeli wcale). By je odróżnić nadaję im odpowiednie nazewnictwo. Ciekawostką dla początkujących jest choćby klasa autoryzacji. Zauważ, że nigdzie ona nie prezentuje danych, czyli jest klasą bez widoku. Ma model by pobrać dane ze źródła, Kontroler dla przepływu i obróbki danych i nic więcej nie trzeba do szczęścia.
perhydrol
dzięki wielkie "thek" za zrozumiały opis i wytłumaczenie,
prosił bym jednak w dalszym ciągu o rozpisanie dziedziny sklepu internetowego na klasy(listy nazw klas na jakie należy podzielić aplikację), tak aby mieć punkt odniesienia jak szczegółowo do tego podchodzić, mile widziana jakaś przykładowa, sensowna konwencja nazewnicza klas, metod...

-pozdrawiam
--------------------------------------------------------------------------------------------------------------

może inaczej zapytam, pomoże ktoś przy wydzieleniu klas?
powiedzmy tak poglądowo (nazewnictwo w języku PL, wyłącznie dla zobrazowania przykładu)
np:
  1. class Uzytkownik {
  2. //:: pytanie czy rozbijać na dwie f-cje, czy w f-cji rejestracja umieścić tę część
  3. function walidacjaDanychRejestracji{
  4. ..........
  5. }
  6. //::
  7. function rejestracja(){
  8. ..........
  9. }
  10. //::
  11. function logowanie(){
  12. ..........
  13. }
  14. //::
  15. function wylogowanie{
  16. ..........
  17. }
  18. //:: f-cja pozwalająca na wygenerowanie przez system, nowego hasła dla użytkownika
  19. function rzadanieNowegoHasla{
  20. ..........
  21. }
  22. //:: f-cja umożliwiająca wprowadzenie nowego hasła przez użytkownika
  23. function ustawNoweHaslo{
  24. ..........
  25. }
  26. //:: f-cja umożliwiająca zmianę wybranych danych użytkownika
  27. function zmienDaneUzytkownika{
  28. ..........
  29. }
  30. //::
  31. function pokazPrzyslugujacyRabat{
  32. ..........
  33. }
  34.  
  35. }


wiem również że w tym przykładzie wymieszałem części modelu M V C, ale interesuje mnie jak podejść do sprawy podziału wypisanych funkcji na klasy, prosił bym kogoś o pogrupowanie oraz podział na klasy(z małym komentarzem dlaczego), interesuje mnie jak Wy to robicie.

-pozdrawiam
jmail
hmmmmm. zdaje mi się czy pomyliliśmy pojęcia MVC? winksmiley.jpg

Za wiki

Cytat
Użycie MVC można przedstawić na przykładzie aplikacji bazodanowej. Wtedy:

modelem jest encja (lub ich zbiór) z bazy danych,
widokiem tabela danych pokazywana użytkownikowi, a
kontrolerem przyciski do manipulacji.
Kontroler jest odpowiedzialny za odczyt danych z bazy danych (utworzenie modelu) i przekazanie ich do warstwy widoku (interfejsu użytkownika). Gdy użytkownik wybierze pokaż kolejne, wtedy odwołanie jest przekazywane do kontrolera, który ponownie pobiera dane i przekazuje do widoku tą samą drogą.


Załóżmy że napisaliśmy sobie jakąś klasę do łączenia i pobierania danych z bazy (nie widzę sensu rozbijać tego na dwie klasy) i mamy drugą klasę do wyświetlania template'ów wiadomo, że zazwyczaj jedno i drugie będzie wykorzystywane na stronach. I teraz tak klasa do łączenia będzie naszym modelem (właściwie powłoką do oglądania tych danych) automat do parsowania templatek (na przykład Smarty) będzie widokiem a to co się dzieje w naszych funkcjach to kontroler.

I to aplikacja dzieli się na taki podział a nie pojedyncze klasy winksmiley.jpg

Czy się pomyliłem ? biggrin.gif
perhydrol
wiec tak dla sprostowania, pojęć nie pomyliłem, może w jednym miejscu źle sformułowałem zdanie, ale tak jak pisze w pierwszym poście, chcę przejść na obiektowe programowanie aplikacji, w odniesieniu do "wzorca" ? MVC
odnośnie MVC, "thek" udzielił mi wyczerpującej odpowiedzi, za co dziękuję.
Na przykładzie Zend Framework jeśli by ktoś potrzebował to myślę że warto przeczytać:
http://www.heavymind.net/zend-framework-tutorial/#100

natomiast moje ostatnie pytanie, a może raczej prośba o pomoc dotyczy podziału danej dziedziny w założeniach sklepu internetowego, czy tez na przykładzie funkcji odnoszących się do UŻYTKOWNIKA, jak ma to miejsce w ostatnim poście na klasy.

Ponieważ powtarzam, mam problem z odpowiednim wydzieleniem i zakwalifikowaniem pewnych metod do poszczególnych klas(z małym komentarzem dlaczego tak), więc poprosiłem o przedstawienie propozycji przez kogoś bardziej doświadczonego w kodowaniu Obiektowym, abym mógł sobie znaleźć jakąś analogie na konkretnym przykładzie.
Proszę nie przytaczajcie przykładu klasy obsługującej bazę danych(wiem że to dobry przykład) ponieważ ona ma sama w sobie bardzo logiczny podział, proszę o pomoc przy przykładzie który ja zamieściłem smile.gif


-dzięki za pomoc smile.gif
thek
Zauważ, że przykładową klasą Użytkownik mieszasz już nieco strukturę. Są w niej bowiem funkcje z różnych "warstw". I tak:
Walidacja -> Model
Rejestracja -> Model
Logowanie -> Kontroler
Wylogowanie -> Kontroler
Nowe hasło -> Model
Zmień hasło -> Model
Zmień dane -> Model
Pokaż rabat -> Widok
Problem w tym, że nawet to jest w sposób, który nie do końca jest prawidłowy. Dlaczego?Bo część z tych operacji jest wykonywana poprzez więcej niż 1 "warstwę". Przykładowo Pokaż rabat jest tak naprawdę Widokiem, ale musi posłużyć się metodami Modelu by uzyskać te dane. Tak naprawdę więc nie ma w klasie usera żadnego widoku. Jest za to jeden wielki model danych użytkownika, którego metodami posługują się inne klasy. Nie ma czegoś takiego jak logowanie czy wylogowanie, bo to kontroler zupełnie innej klasy. Tak naprawdę w klasie użytkownik masz metody służące do jego dodania, usunięcia, wyciągnięcia pewnych lub wszystkich danych bądź ich zmiany. Rabat jest tutaj czymś zupełnie obcym i można powiedzieć, że jest osobną klasą. Walidacja nie jest osobną metodą, gdyż musi być implementowana w każdej z metod odpowiedzialnych za edycję. Nie napiszesz nigdy zunifikowanej wersji musiałbyś dla każdego typu pola pisać osobną metodę walidacji go. Inaczej bowiem waliduje się adres e-mail a inaczej adres www lub konkretne pole liczbowe czy znakowe. Poza tym czasem jakieś pola przy edycji w określonych przypadkach walidujesz, a inne nie, gdyż nie są nawet wypełniane. Jak wyobrażasz sobie choćby metodę walidacji z jedną metodą gdy raz posyłamy ją tylko do zmiany hasła, a innym razem do dodawania usera?
Najprościej rzecz ujmując istnieją jakby dwie klasy użytkownik. Jedna to kontroler, druga to model. User_Kontroler może wywołać metodę rejestracja, która zainicjuje User_Model łącząc ją ze źródłem danych, zwaliduje dane i gdy zwróci błąd (banalne hasło, login już istnieje, nieprawidłowe dane w formularzu lub nieprawidłowy ich format) poinformuje User_Kontroler o tym. Jeśli zaś wszystko ok to doda do źródła danych i zrobi ewentualnie coś jeszcze.

Sklep internetowy podziel na role wpierw i pomyśl co Ci potrzebne. Na pewno użytkownik, na pewno produkt i koszyk To minimum, ale w zależności od stopnia komplikacji rozszerzy się to zapewne o inne. Nie ma jasnej i gotowej odpowiedzi o podział, gdyż niemal każdy inaczej widzi go, choć teoretycznie każdy korzysta z tej samej definicji winksmiley.jpg
jmail
Cytat(thek @ 15.10.2009, 09:24:26 ) *
Zauważ, że przykładową klasą Użytkownik mieszasz już nieco strukturę. Są w niej bowiem funkcje z różnych "warstw". I tak:
Walidacja -> Model
Rejestracja -> Model
Logowanie -> Kontroler
Wylogowanie -> Kontroler
Nowe hasło -> Model
Zmień hasło -> Model
Zmień dane -> Model
Pokaż rabat -> Widok
Problem w tym, że nawet to jest w sposób, który nie do końca jest prawidłowy. Dlaczego?Bo część z tych operacji jest wykonywana poprzez więcej niż 1 "warstwę". Przykładowo Pokaż rabat jest tak naprawdę Widokiem, ale musi posłużyć się metodami Modelu by uzyskać te dane. Tak naprawdę więc nie ma w klasie usera żadnego widoku. Jest za to jeden wielki model danych użytkownika, którego metodami posługują się inne klasy. Nie ma czegoś takiego jak logowanie czy wylogowanie, bo to kontroler zupełnie innej klasy. Tak naprawdę w klasie użytkownik masz metody służące do jego dodania, usunięcia, wyciągnięcia pewnych lub wszystkich danych bądź ich zmiany. Rabat jest tutaj czymś zupełnie obcym i można powiedzieć, że jest osobną klasą. Walidacja nie jest osobną metodą, gdyż musi być implementowana w każdej z metod odpowiedzialnych za edycję. Nie napiszesz nigdy zunifikowanej wersji musiałbyś dla każdego typu pola pisać osobną metodę walidacji go. Inaczej bowiem waliduje się adres e-mail a inaczej adres www lub konkretne pole liczbowe czy znakowe. Poza tym czasem jakieś pola przy edycji w określonych przypadkach walidujesz, a inne nie, gdyż nie są nawet wypełniane. Jak wyobrażasz sobie choćby metodę walidacji z jedną metodą gdy raz posyłamy ją tylko do zmiany hasła, a innym razem do dodawania usera?
Najprościej rzecz ujmując istnieją jakby dwie klasy użytkownik. Jedna to kontroler, druga to model. User_Kontroler może wywołać metodę rejestracja, która zainicjuje User_Model łącząc ją ze źródłem danych, zwaliduje dane i gdy zwróci błąd (banalne hasło, login już istnieje, nieprawidłowe dane w formularzu lub nieprawidłowy ich format) poinformuje User_Kontroler o tym. Jeśli zaś wszystko ok to doda do źródła danych i zrobi ewentualnie coś jeszcze.

Sklep internetowy podziel na role wpierw i pomyśl co Ci potrzebne. Na pewno użytkownik, na pewno produkt i koszyk To minimum, ale w zależności od stopnia komplikacji rozszerzy się to zapewne o inne. Nie ma jasnej i gotowej odpowiedzi o podział, gdyż niemal każdy inaczej widzi go, choć teoretycznie każdy korzysta z tej samej definicji winksmiley.jpg



Bo mnie zaraz coś trafi. Nie myl koledze.

Walidacja -> Model - bzdura - kontroler
Rejestracja -> Model - bzdura - kontroler
Logowanie -> Kontroler - no wreszcie coś rozsądnego
Wylogowanie -> Kontroler - i znowu coś rozsądnego
Nowe hasło -> Model - bzdura! - kontroler
Zmień hasło -> Model - bzdura! - kontroler
Zmień dane -> Model - bzdura! - kontroler
Pokaż rabat -> Widok - bzdura! - kontroler

Nie myl pojęć

Aplikacja ma być podzielona na warstwy a nie klasy. Poza tym są klasy do łączenia się z bazą danych i do wykonywania zapytań. Więc to one są Model'em. Widok to będzie mechanizm parsujący templatkę, a wszystko co się między nimi odbywa jest kontrolerem. Stworzenie zapytania (stringa) może być częścią kontrolera albo modelu - Twoja sprawa jak to zrobisz. Ale już walidacja w php (czy wszystkie dane zostały wpisane) jest czystym kontrolerem.

Podział na klasy w sklepie (według mnie):

klasa Modelu - do łączenia z bazą, tworzenia zapytań
klasa Widoku - na przykład smarty
klasa użytkownika (z wszelkimi niezbędnymi metodami)
klasa koszyka
klasa zamówienia
klasa modułu (za chwilę to wyjaśnię)
klasa dostawy (wprowadzanie świeżego towaru do sklepu)

To chyba wszystko co Ci jest potrzebne

Co do modułu. Na stronę moim zdaniem trzeba spojrzeć jak na wielki moduł, w którym są ładowane różne inne moduły.

Może obrazek tongue.gif



Każdy z zaznaczonych na obrazku modułów składa się dokładnie z tego samego

1. tworzysz moduł
2. napełniasz go danymi
3. parsujesz template


Teraz załóżmy że budujesz klasę w PHP która będzie się nazywać modul

  1.  
  2. <?php
  3.  
  4. class modul{
  5. public $smarty = '';
  6. public $module = '';
  7. public $page = '';
  8. public $top = '';
  9. public $bottom = '';
  10. public $conn = null;
  11. public $queries = null;
  12. public $tables = null;
  13. public $parameters = null;
  14.  
  15. public function __construct($module, $connections, $top = 1, $bottom = 1){
  16. //tu coś się dzieje
  17. $this->CreateConnections($connections);
  18. $this->ReadQueries();
  19. }
  20.  
  21. public function getPage(){
  22. $this->page = $this->smarty->fetch($this->module.'.tpl');
  23. }
  24.  
  25. public function CreateConnections($connections){
  26. //tworzy połączenia do baz danych
  27. }
  28.  
  29. public function ReadQueries(){
  30. //zaczytuje zapytania do wykonania w module
  31. }
  32.  
  33. public function constructQuery($i, $j){
  34. //to jest niezbędne do obsługi silnika DB
  35. }
  36.  
  37. public function executeQuery($i){
  38. //wykonuje konkretne zapytanie
  39. }
  40.  
  41. public function assignVar($name = '', $value = ''){
  42. $this->smarty->assign($name, $value);
  43. }
  44.  
  45. public function display(){
  46. echo $this->top.$this->page.$this->bottom;
  47. }
  48. }
  49. ?>
  50.  
  51.  


to jest przykład z modelu MVC

teraz tak. Każdy z modułów na stronie może robić dokładnie to samo jak już pisałem wcześniej.

Założmy taki newsletter

  1.  
  2. <?php
  3. class Newsletter extends modul{
  4. public function __construct($connections){
  5. parent::contruct('newsletter', $connections, 0, 0);
  6. //dodałem zera do konstrukcji rodzica bo one akurat wskazują czy nagłowek i stopka mają być dodane do modułu. to jest fragment strony więc nie potrzeba nam ani nagłówka ani stopki
  7. //newsletter zawsze będzie się wyświetlał tak samo, więc gdzie to wywoływać parsowanie? gdzie indziej? a po co? biggrin.gif
  8. $this->getPage();
  9. //jeszcze ważna rzecz trzeba pamiętać, ze ktoś może się chcieć dopisać
  10. if(isset($_POST['save']) && $_POST['save'] == 'newsletter'){
  11. $this->addToNewsleetter();
  12. }
  13. }
  14.  
  15. public function addToNewsletter(){
  16. //tu dopisujemy do newslettera osoby :)
  17. }
  18. }
  19. ?>
  20.  


a teraz sama strona - założmy index w którym załóżmy że wystepuje newsletter

  1.  
  2. <?php
  3.  
  4. class index extends module{
  5. public function __construct($connections){
  6. parent::contruct('index', $connections);
  7. //jak widać teraz nagłowek i stopka zostaną do strony dołączone ;)
  8.  
  9. //tu oczywiście inne rzeczy mogą się jeszcze dziać
  10. }
  11. }
  12.  
  13. ?>
  14.  


I na końcu sklejenie tego wszystkiego w całość

  1.  
  2. <?php
  3. require_once('includes/application.php'); //w tym pliku trzymam wszystkie dane niezbędne do połączenia i na przykład funkcję __autoload
  4. $index = new index($connections);
  5. $newsletter = new Newsletter($connections);
  6. $index->assignVar('newsletter', $newsletter->page);
  7. $index->getPage();
  8. $index->display();
  9. ?>
  10.  


i templatka (index.tpl)

  1. <div>
  2. {newsletter}
  3. </div


Mam nadzieję, że rozumiesz o co mi chodzi ^^
fenix.robi
Ja proponuje skorzystać z fameworka CAKE'a, który jest oparty o MVC, i przerobić pierwszy tutorial o blogu, łatwo na nim sie nauczyc co za co odpowiada, (btw Walidacja jest w modelu), jak Ci się nie spodoba to nie będziesz używał,a le chociaż zasmakujesz tematu.
thek
To może jmail wyjaśnię dlaczego podałem taki a nie inny podział dla jasności. Jadę na Kohana 2.X co implikuje pewne podejście do programu. Tam jest, jak zapewne wiesz, podział na zasadzie klasy modeli, kontrolerów i widoków osobno. Dla zachowania przenośności i skalowalności klasy mam niejako 3 wersje w dużej ilości wypadków: Nazwa_klasy_Controller, Nazwa_klasy_Model, Nazwa_klasy_View, gdyż traktuję ją nieraz jako osobny "panel", który mogę w łatwy sposób wyrzucić, zmienić.
I tak wiele z tego co zostało wymienione jest tak naprawdę tylko wywołaniem w kontrolerze odpowiednich metod Modelu. Zauważ, że w takim wypadku niemal wszystko sprowadziłeś do poziomu kontrolera. A powiedz mi czy Kontroler tak naprawdę wie czy w bazie danych można zarejestrować kogoś? Nie wie. Musisz w kontrolerze zapytać model czy taki user już istnieje i dopiero on Ci odpowie zgodą lub nie na operację. W kontrolerze więc wywołuję metodę user_model->nowy_user($_POST) i oczekuję na efekty. To na poziomie modelu przebiegnie walidacja danych, a ja jedynie zwrot dostanę czy operacja przebiegła prawidłowo czy nie( w pewnej formie przeze mnie wybranej) i ewentualnie od niej podejmę jakieś działanie. Ujmując to inaczej... Napisałem gdzie tak naprawdę dzieje się podstawowa część danej opcji. Bo czy napisanie w kontrolerze funkcji do zmiany hasła w stylu
public function zmien_haslo($user, $nowe) {
(...)
}
jest inne od tego samego w modelu? To model ma się zająć operacjami na danych a nie kontroler. Ja co najwyżej mogę z kontrolera modelowi to zlecić i zinterpretować wynik otrzymany. Myślę, że rozumiesz moje podejście. Kontroler sterować ma przepływem danych, a nie na nich operować (walidować, obrabiać). Z takim podejściem na model zrzuciłeś jedynie wykonanie zapytań do źródła danych, ale całość operacji masz w kontrolerze. Odnosisz się do niego jedynie by pobrać lub wysłać dane.
Login i logout są w kontrolerze, gdyż bezpośrednio tyczą klasy User i stanu w jakim się znajduje na portalu poprzez operacje na sesji użytkownika. Koszyk zbiera informacje o wyborze produktów na stronach. Pokaz_rabat to odwołanie do wysłanie do widoku danych z jakiegoś modelu. Realizuje to kontroler tak jak napisałeś, ale to na widoku spoczywa prezentacja danych w odpowiedniej formie. Stąd tak przyporządkowałem. Nie na zasadzie gdzie programuję daną funkcję, ale gdzie jest jej meritum, na jaki element mamy zwrócić uwagę w szczególności. Wracając do Pokaż_rabat to czy nie jest to po prostu(skrótowo, bo powinienm jeszcze sprawdzić wyjście funkcji pokaz_rabaty(parametry) ) ):
  1. User_widok_rabaty->dane = user_model->pokaż_rabaty(parametry);
  2. User_widok_rabaty->display();
Powiedz mi więc gdzie w tym kodzie dzieje się najważniejsza część. Bo dla mnie nie byłby to kontroler winksmiley.jpg A to jaki widok mi odpowiada do prezentacji danych modelu ustalam kontrolerem. Jak dla mnie to jest właśnie MVC: Model wybiera i obrabia dane, Widok je wyświetla, a Kontroler przekazuje dane Modelu do odpowiedniego Widoku. W Twoim przypadku jest tak: Kontroler pobiera dane z Modelu i dopiero w kontrolerze je obrabia i potem każe widokowi wyświetlić. Dla mnie to już pogwałcenie zasad MVC, ale wiem, że wielu programistów tak robi, zwłaszcza przy walidacji danych, bo likwiduje to kilka problemów, choćby z przepychaniem informacji o błędach i powtórnym wypełnieniu formularza tymi samymi danymi co przed wysłaniem. Rozumiem więc, że Twoje podejście jest wywołane doświadczeniami praktycznymi. Dlatego też nie uważam tego co napisałeś za błąd i nie zamierzam się kłócić w tak naprawdę nieistotnej sprawie. Po prostu zastosowałeś podejście praktyczne do pewnych problemów wynikające z doświadczenia programistycznego. Ja mam inne doświadczenia i stąd te różnice między nami.

Po prostu patrzymy na to samo z innych perspektyw. To widać już na poziomie podziału na klasy. Ja dla przykładu nie widzę sensu klasy dostawa, za to brakuje mi klasy produktu, bo z opisu sądząc można odnieść wrażenie, że dostawa istniejącego produktu do sklepu jest czymś nowym smile.gif A przecież to tylko zmiana pola dostępna_ilosc dla klasy produkt. Dostawa może być ewentualnie klasą widoczną dla admina z wyszczególnieniem, że tego dnia a tego do sklepu dotarło tyle jednostek produktu takiego i takiego lub informacje związane z obsługą dostawy do klienta jego zamówienia. Jak już podkreśliłem ja i wielu innych w tematach -> wykładni MVC jest tyle ile stosujących je osób. Dla mnie patrzysz na MVC z poziomu aplikacji jako całości strony, a ja od strony roli różnych klas w niej. Stąd co innego wkładamy do nazwanych tak samo klas. By można mówić o błędnym myśleniu któregokolwiek z nas, ale musielibyśmy mieć już z góry narzucony schemat podziału klas. Tego zaś nie mamy...
jmail
nie no to są dwie zupełnie różne rzeczy.

Jeżeli klasy rozpatrujesz jako model MVC to oczywiście masz rację, ale przez to zaciemniasz cały obraz. Rozdrabniasz aplikację na setki mniejszych "aplikacji"

Poza tym

Model - baza danych i operacje się na niej dziejące
Kontroler - logika aplikacji (w tym również walidowanie danych tongue.gif)
Widok - wyświetlenie danych


Co do dostawy to wydzieliłem tą klasę z jednego prostego powodu. Dla mnie dostawa zawsze wiązała się z fakturą i całą księgowością, dlatego poleciała na osobną klasę. Co do produktu masz rację. Brakuje go jako klasy. Brakuje również klasy magazyn (skąd wiesz, że sklep ma jeden winksmiley.jpg ) i tak dalej. Wszystko zależy od perspektywy z jakiej patrzysz na sklep internetowy.

smile.gif

Ale to nie programista jest od tego, żeby to planować biggrin.gif dlatego właśnie analitycy projektowi koszą taką kapuchę za zamodelowanie rzeczywistości w aplikacji. Bo jak źle zamodelują to będzie kaszana z wydajnością.
dr4ko
Cytat(jmail @ 15.10.2009, 12:53:12 ) *
Bo mnie zaraz coś trafi. Nie myl koledze.

Walidacja -> Model - bzdura - kontroler
Rejestracja -> Model - bzdura - kontroler
Logowanie -> Kontroler - no wreszcie coś rozsądnego
Wylogowanie -> Kontroler - i znowu coś rozsądnego
Nowe hasło -> Model - bzdura! - kontroler
Zmień hasło -> Model - bzdura! - kontroler
Zmień dane -> Model - bzdura! - kontroler
Pokaż rabat -> Widok - bzdura! - kontroler

Nie myl pojęć

Aplikacja ma być podzielona na warstwy a nie klasy. Poza tym są klasy do łączenia się z bazą danych i do wykonywania zapytań. Więc to one są Model'em. Widok to będzie mechanizm parsujący templatkę, a wszystko co się między nimi odbywa jest kontrolerem. Stworzenie zapytania (stringa) może być częścią kontrolera albo modelu - Twoja sprawa jak to zrobisz. Ale już walidacja w php (czy wszystkie dane zostały wpisane) jest czystym kontrolerem.


Pozwolę się nie zgodzić. MVC nie jest takie proste. Po pierwsze - model nie powinien bezpośrednio grzebać w bazie, od tego jest Data Access Layer. Co prawda DAL jest powiązany nierozłącznie z modelem ale samo połączenie z bazą i jego obsługa nie powinna się znajdować bezpośrednio w modelu. No ale nie o tym chciałem tu napisać.

Jak sama nazwa mówi, warstwa kontrolera służy do tego by kontrolować aplikację.Weźmy przykład zmiany hasła użytkownika:

View - wyświetla formularz zmiany hasła, użytkownik wpisuje hasło i klika button "zapisz". Informacja zostaje wysłana do controllera.
Controller - niech to będzie metoda changeUserPassword(). Powinna ona pobrać model aktualnie zalogowanego użytkownika, pobrać z posta nowe hasło, wykonać za pomocą helpera walidację hasła i wywołać na modelu użytkownika metodę changePassword(). W razie wystąpienia błędów powinna wyrzucić je do widoku.
Model - changePassword() wywołuje klasę DAL, ustawia za jej pomocą nowe hasło, zapisuje dane i wykonuje dodatkowe operacje typu ustawienia daty aktualizacji, czy wysłanie maila do użytkownika.

Controller jest warstwą przejściową, nie powinna ona być świadoma warstwy modelu. Ją interesują tylko dane wchodzące i wychodzące i czy są one prawidłowe, a nie co się z nimi dzieje. W skrócie - w controllerze nie przetwarzamy danych, co najwyżej je formatujemy i walidujemy.
jmail
dr4ko czekaj czekaj. czy to nie jest tak, że model to jest własnie baza danych? przecież DAL jest właśnie warstwą programowalną modelu. to do niej kontroler musi wysłać dane i powiedzieć co z nimi zrobić. Nie możesz mówić, że kontroler ma być nieświadomy modelu bo on musi model poprosić o konkretne dane, albo konkretne dane mu przekazać.

Z widokiem masz rację bezdyskusyjnie.

Cytat
Kontroler jest odpowiedzialny za odczyt danych z bazy danych (utworzenie modelu) i przekazanie ich do warstwy widoku (interfejsu użytkownika). Gdy użytkownik wybierze pokaż kolejne, wtedy odwołanie jest przekazywane do kontrolera, który ponownie pobiera dane i przekazuje do widoku tą samą drogą.


co z resztą jest zbieżne z tym czego mnie na uczelni uczono

Cytat
Praktycznie każda aplikacja WWW korzysta z baz danych - operacje na bazach stanowią modele danych. Szablony HTML odpowiedzialne za wygląd i wyświetlanie danych są częścią widoków (np. Smarty). Kod odpowiedzialny za wykonanie określonych operacji (spinający wszystko razem) tworzy sterowniki.


Poza tym jak to sobie wyobrażasz? Kontroler niświadomy modelu to co? Podaj mi jakiekolwiek dane a ja z nimi nie będę wiedział co zrobić? oO Przecież to się kupy logicznej nie trzyma oO
dr4ko
Cytat(jmail @ 15.10.2009, 16:03:40 ) *
dr4ko czekaj czekaj. czy to nie jest tak, że model to jest własnie baza danych? przecież DAL jest właśnie warstwą programowalną modelu. to do niej kontroler musi wysłać dane i powiedzieć co z nimi zrobić. Nie możesz mówić, że kontroler ma być nieświadomy modelu bo on musi model poprosić o konkretne dane, albo konkretne dane mu przekazać.


Może źle się wyraziłem. Kontroler nie powinien być świadomy charakteru modelu. Co kontrolerowi do tego czy wysłać maila potwierdzającego czy nie? Albo co kontrolerowi do tego czy dane są pobierane z bazy czy z pliku xml? Kontroler reaguje na sygnał z UI, sprawdza jego poprawność i przekazuje dalej, nic poza tym. Po prostu cała logika biznesowa powinna być zawarta w modelu. Może bardziej obrazowy przykład:

Użytkownik za pomocą programu chce włączyć drukarkę. Klika przycisk. Informacja wędruje do kontrolera który sprawdza czy to na pewno dobry przycisk. Jeśli tak wywołuje komendę "włącz drukarkę" na modelu który w tym przykładzie jest fizyczną drukarką i oczekuje na informację zwrotną czy drukarka się włączyła czy nie. I nic poza tym, kontroler nie zajmuje się inicjalizacją wszystkich podzespołów i protokołów drukarki, drukarka robi to sama. Dzięki temu gdy kupimy nową drukarkę innej firmy nie musimy przepisywać całego kontrolera, tylko podpinamy ją pod już istniejący i będzie działała jak trzeba bo cała logika specyficzna dla danej drukarki jest zamknięta w samej drukarce, czyli naszym modelu.

Mam nadzieję że to co napisałem jest zrozumiałe smile.gif

Aha. Co do modelu będącego jednocześnie DAL czyli bazą danych. W prostych aplikacjach to może działać ale w dużych nie ma szans. Prosty przykład z systemu który rozwijamy w mojej firmie - mamy kilka rodzajów użytkowników, każdego opisuje około 5 tabel, z których tylko 3 są wspólne. Powiedz mi jak to obsłużyć korzystając tylko z DAL tongue.gif
jmail
no i właśnie o to chodzi. model jest tylko zbiorem danych. logika właśnie spoczywa na kontrolerze. obrazując podobnie do Twojego przykładu

mamy workflow z 20-oma krokami. user wykonał 11-ty krok. To kontroler decyduje o tym, żeby do modelu wysłać sygnał daj mi dane 12 kroku. oczywiście nie interesuje go co jest pod spodem czy xml czy cvs czy baza. on chce dostać dane. i teraz włąśnei DAL sprawdza co w konifugracji przechowuje dane i zwraca dane 12 kroku. Natomiast kontroler musi rozdzielić logikę biznesową, czy teraz maila wysłać czy może sms'a.

W założeniu które napisałeś wychodziłoby na to, ze kontroler powinien nazywać się walidator a nie sterownik czy kontroler
dr4ko
Logika biznesowa powinna być zawarta w modelu, a kontroler służyć tylko do obsługi UI. Robiąc inaczej niszczy się cały zysk z MVC czyli wymienialność modelu bez ruszania całej reszty. Powiedzmy że wymieniłeś model na inny. Teraz przy dodaniu wpisu do bazy użytkownik nie ma dostać smsa tylko np informacja ma się pojawić na twitterze. Nie dosyć że musisz zaimplementować tą funkcjonalność w modelu, to jeszcze musisz usunąć wszystkie odwołania do sendSms() w kontrolerze i dodać wywołania addToTwitter(). Podwójna robota. Zrozum, że użytkownik nie prosił o wysłanie smsa czy maila, on prosił o dodanie wpisu i tylko tym się powinien zająć kontroler, a to że model biznesowy przewiduje notyfikację użytkownika o akcji jest już poza kręgiem zainteresowania kontrolera.
jmail
to jest Twoja opinia. moja jest taka, że jeżeli wpis ma się pojawić na twitterze to trzeba tą funkcjonalność zaimplementować w kontrolerze a nie w modelu. Bo co? przebudujesz bazę danych żeby na twitterze się pojawiało? Model to baza danych i ewentualnie jej warstwa programowalna a nie funkcjonalności w php jakie sobie umyślisz
dr4ko
Ok, ja się poddaję, nauczyciel ze mnie kiepski. Ale tu masz to wytłumaczone czarno na białym:
http://java.sun.com/blueprints/patterns/MVC-detailed.html

Może producent javy, dla której wymyślono MVC, będzie dla ciebie bardziej wiarygodnym źródłem wiedzy niż ja.
thek
Dr4co ma IHMO rację. Przyłącze się do przykładu z wysłaniem danych. To model ma wiedzieć gdzie są dane i w jakiej postaci. Rzucono przykład zmiany z sms na twittera. Myślę, że dobrze ilustrujący problem. Jak dodano, pociąga to za sobą zmiany zarówno w kontrolerze, jak i modelu w przypadku stosowania modelu aplikacji przedstawionego przez jmaila. Tymczasem jest to do wychwycenia już przez sam model, który może przechwycić tę informację z pliku konfiguracyjnego bez pośrednictwa kontrolera. Kontroler z kolei może mieć wpływ na konfigurację, ale przed lub po operacji. Nie w trakcie. Powiedz mi czy przesyłając formularzem fotografię decydujemy w kontrolerze czy wywołamy createimagefrompng, gif czy jpg? To analogia. Model ma wykryć z czym ma do czynienia i się dostosować. Można więc porownać to do obróbki w gd. Nieważne co wprowadzamy... Kontroler oczekuje zawsze obiektu gd, nie zaś pliku jpg, bo nie wie, czy to na pewno jpg będzie. A może ktoś przysłał plik png. Czy użycie imagecreatefromjpg na pliku png da prawidłowy rezultat? Nie. Każdy format jest przetwarzany do takiego obiektu, na którym operacje są już niezależne od źródła, natywnego dla narzędzia. Przykład z drukarką też jest dobry. Kontroler wywołuje jedynie funkcję print(). To model ma wiedzieć, która drukarka jest domyślna, czy drukować w skali szarości czy kolorze. A może do pliku? To wszystko jest zaszyte w konfiguracji i model ma do tego dostęp. W zależności od sytuacji sam zdecyduje jak się zachować. Model ma to w swoich atrybutach. Wcale nie przeszkadza to wymuszaniu przez kontroler na modelu konkretne zachowanie, niezależne od jego domyślnego. Wystarczy, że sam zainicjuje model o innych parametrach. A to kontroler może zrobić tyle, że model z kolei może mu na to nie pozwolić, twierdząc że drukowanie nie jest możliwe bo brak mu sterownika tej drukarki winksmiley.jpg
jmail
ale chwilę. To co dałeś to jest modyfikacja pure MVC. W załozeniach MVC Model z Widokiem nie mają żadnego powiązania a w linku który dałeś jest. To jakby zbliżone już jest do modelu WPF a nie czystego MVC

LOL? z tym zdjeciem? To może zacznijmy zdjecia do blobów do bazy pchać..... wtedy to rzeczywiście model może to obsługiwać....
dr4ko
Bzdura. To co podałem to jest najczystsze MVC jakie może być bo ze źródła które je wymyśliło. W założeniu widok powinien otrzymać callback po operacji bezpośrednio z modelu lub za wykorzystaniem observera z pominięciem kontrolera. To dla php uproszczono sprawę i włączono do kontrolera funkcję obserwatora.
Co do dalszej części twojej wypowiedzi to zacytuję, bo chyba poza obejrzeniem obrazków nie czytałeś:
Cytat
Model - The model represents enterprise data and the business rules that govern access to and updates of this data. Often the model serves as a software approximation to a real-world process, so simple real-world modeling techniques apply when defining the model.


A co jeśli aplikacja nie ma bazy danych? Z twoim tokiem myślenia to architektura MVC nie mogłaby istnieć w takim przypadku co już u samo w sobie jest kompletnym absurdem.
jmail
to jak chcesz zrobić logikę biznesową bez bazy danych? Bez względu na to czy to będzie silnik DB, czy plik XML, czy plik tekstowy itp to i tak baza jest pod spodem.

Możemy dyskutować jeszcze bardzo długo na ten temat i każdy będzie miał swoje zdanie

Poza tym to nie SUN wymyślił MVC tylko sobie przywłaszczył autorstwo. Tak samo jak MS czy Apple przywłaszczyli i przywłaszczają sobie, że są pierwszym systemem okienkowym. Koncepcja MVC tak samo jak systemu okienkowego zrodziła się w jednej i tej samej firmie w XEROX'ie w latachj 70 i została utworzona na potrzeby smalltalk a nie Javy. Java wtedy nawet spłodzona nie była (MVC powstało kilkanaście lat przed Javą). Następnym razem sprawdź źródła historyczne a później opowiadaj bajki kto i co wymyślił.

Cytat
Model-View-Controller is the concept introduced by Smalltalk's inventors (TrygveReenskaug and others) of encapsulating some data together with its processing (the model) and isolate it from the manipulation (the controller) and presentation (the view) part that has to be done on a UserInterface.
A model is an object representing data or even activity, e.g. a database table or even some plant-floor production-machine process.
A view is some form of visualization of the state of the model.
A controller offers facilities to change the state of the model.



I na końcu pytanie o czym rozmawiamy? O tym co wymyślono dla SmallTalka, o tym co wymyślono dla Javy czy jeszcze inne definicje rozważamy? Bo znam conajmniej 5 i każda różni się definicją funkcjonalności kotrolera i modelu. Co do widoku wszystkie są zgodne tongue.gif
dr4ko
A gdybym chciał zrobić aplikację obliczeniową? Np kalkulator albo generator wykresów? Baza danych nie byłaby mi kompletnie potrzebna a model i tak mógłby istnieć.

Co do przywłaszczania sobie pomysłu to nie jest tak.To że koncepcja rozdzielenia warstw zrodziła się w XEROXie nie znaczy że to oni wymyślili MVC. Teraz już nie pamiętam całej historii ale generalnie wtedy to się nazywało inaczej i miało trochę inny obieg danych. Jak będę miał kiedyś nadmiar wolnego czasu to znajdę ciekawy artykuł na ten temat.
jmail
no to teraz sam sobie zaprzeczasz. kalkulator albo wykresy bez danych? biggrin.gif niezłe niezłe biggrin.gif

A co do XEROX'a to od samego początku to się MVC nazywało
thek
jmail... my mówimy teraz o braku danych czy braku połączenia z bazą danych smile.gif Bo czytając posty dr4ko wyraźnie pisze on o braku tego drugiego, a Ty o pierwszego. Zauważ, że wpychając wszystko do kontrolera to Ty decydujesz jakie jest źródło danych, czy plik, czy baza czy cokolwiek innego, nie zezwalając na nic modelowi. Tymczasem to model sam ma zdecydować co to będzie. Taka jest idea MVC. Bardziej obrazowo pokazując:
Twoje podejście to:
(kontroler): modelu, pobierz dane z bazy danych!
(model): tak jest kontrolerze!
(kontroler): modelu, pobierz dane z pliku XML!
(model): tak jest kontrolerze!
(kontroler): modelu, pobierz dane!
(model): nie określiłeś skąd pobrać!

Podejście dr4ko i moje:
(kontroler): modelu, pobierz dane!
(model): dobrze! pobieram z domyślnie ustawionego źródła jakim jest baza danych
(kontroler): modelu, użytkownik kazał zmienić domyślne pobieranie na plik XML
(kontroler): modelu, pobierz dane!
(model): dobrze! pobieram z domyślnie ustawionego źródła jakim jest plik XML
(kontroler): modelu, pobierz dane!
(model): dobrze! pobieram ponownie z domyślnie ustawionego źródła jakim jest plik XML

Widzisz różnicę?
Dla modelu w drugim przypadku nieważne jest CZYM drukuje i skąd pobiera. Ma to w konfigu. Odwołując się raz jeszcze do przykładu drukarki... Metoda print() jest uniwersalna i nie muszę tworzyć metod: printXML(), printPrinterA(), PrintPrinterB(), printGhostScript(). One mogą gdzieś istnieć, ale są zaszyte tak naprawdę w sterowniku drukarki, który jest na poziomie modelu i dołączany dynamicznie w razie potrzeby użycia. Wyobrażasz sobie jaki musiałby być kontroler ogromny by miał możliwość wyboru kilkunastu tysięcy wersji urządzeń do drukowania? A i tak z każdym kolejnym wychodzącym co chwilę, stawałby się nieaktualny. To od modelu zależy urządzenie wyjściowe choć kontroler może próbować wymuszać to, jeśli tylko ma taką możliwość. Dane dostarczane do kontrolera mają być w zunifikowanej postaci dla niego zrozumiałej. Nieważne skąd je pobrał. Ty wymuszasz to dołączając model, do którego dopisujesz metody obsługujace kolejne typy danych i musisz zmieniać także mocno kontroler. Dr4co i ja zaszyjemy tę opcje w danych nie zmieniając nic innego niż dopisując tylko metodę w modelu. Bo zmiany dokonamy dopisując kolejną nie na sztywno w kontrolerze, ale sam on sobie ja dynamicznie doda do "arsenału".

A co gdybym chciał do kalkulatora wysłać plik z wyrażeniem, zamiast klepać je ręcznie? Przebudowałbyś kod kontrolera by to uwzględnić i jeszcze metody w modelu czy lepiej by dodać to do modelu tylko i w konfiguracji oznaczyć, że choć domyślnie kalkulator jako urządzenie wejściowe i wyjściowe przyjmuje dane z klawiatury oraz ekran to można mu w konfigu zmienić, oba na plik w określonym formacie (choćby MathML). Albo wejście na plik a wyjście na ekran. Dla mnie skalowalne i konfigurowalne jest to drugie i to ono jest MVC. Ale to tylko kwestia rozumienia MVC. Ja je widzę jak opisałem powyżej, bo uniezależnia kontroler całkowicie od źródła danych i kontrolera przy zmianach nawet nie tykam palcem. Czy dodając do obsługi w przeglądarce obrazów nowy format pliku musisz zmieniać coś w aplikacji? Twoim podejściem - tak, bo musisz dopisać w kontrolerze metody, która ów typ obsłużą przy pobieraniu, zapisywaniu i obróbce. Ja i dr4co to zrobimy w modelu i dołączymy jako kolejny model lub metody głównej klasy modelu, bez ingerencji w szkielet aplikacji. To czy użytkownik zacznie robić głupoty próbując wymuszać nieprawidłowe zachowania nas nie obchodzi. Możemy temu jedynie przeciwdziałać w niewielkim stopniu poprzez system obsługi błędów. Ale tak robi każdy skalowalny software. Równie dobrze mógłbyś jako programista Twoim sposobem każdy błąd na sztywno walić do /dev/null zamiast do pliku logu. Ale możesz też dać to do wyboru userowi z poziomu GUI. Ty także możesz to zrobić, ale zmieniając aplikację w znacznie większym stopniu i reagując na zapotrzebowanie rynku. Ja wystarczy że podam strukturę danych jakie są potrzebne udostępniając API i każdy może sobie plugin napisać. Twoje podejście uniemożliwia taką skalowalność. Brak bowiem abstrakcyjnych metod obsługi danych. Obsłużysz tylko znane Ci w momencie pisania kodu typy danych bo tylko takie zna Twój kontroler. Nawet podpięcie gotowego pluginu nic nie da bo kontroler nie ma w spisie znanych mu metod tej mogącej plugin zainicjować do współpracy. Dopiero dopisanie jej to by sprawiło. Czy jest sens, by pisząc program w C++, Javie czy innym języku kompilowanym, po dodaniu pluginu zmieniać kod programu głównego i ponowna go kompilować? Widziałeś by twórcy jakiegokolwiek softu robili zmiany po wyjściu dowolnego nowego modelu drukarki? Wypuszczą po sterowniku na każdy system operacyjny na daną architekturę systemową i wsio gra i buczy winksmiley.jpg
dr4ko
Cytat(jmail @ 15.10.2009, 19:13:04 ) *
no to teraz sam sobie zaprzeczasz. kalkulator albo wykresy bez danych? biggrin.gif niezłe niezłe biggrin.gif



Nie bez danych tylko bez bazy danych. A po co mi baza danych do przeprowadzenia obliczeń? Podaję dane wejściowe, skrypt przelicza w oparciu o algorytmy zawarte w modelu i zwraca wynik w postaci tablicy do widoku gdzie pobiera go np flash i wyświetla wykres. Gdzie widzisz problem? Jak ci w PHP to nie pasuje to można to zrobić w javie albo c++, nie ma różnicy. Modelowałem w ten sposób działanie silników elektrycznych i z żadnej bazy danych nie korzystałem.

Nie wiem kto ci nawciskał takich kitów ale zamiast tej osoby powinieneś posłuchać i poczytać wypowiedzi ludzi którzy robili naprawdę duże biznesowe rozwiązania.
perhydrol
Panowie, czytam wasze wypowiedzi i osobiście każdą jedną uważam za bardzo ciekawą i bardzo istotną, ponieważ cała ta "debata" uzmysławia mi masę rzeczy do jakich doszedł bym pewnie za jakiś czas.
Jak już wcześniej wspominałem analizowałem działanie Zend Framework-a, i w nim znajduje się podział na klasy w sposób

Cytat
klasyWidoku
klasyKontrolera
KlasyModelu


od razu zauważalna jest duża przejrzystość kodowania w taki sposób, łatwo odnaleźć cześć kodu odpowiadającą za konkretną funkcjonalność

Przykładowo ja wypisałem sobie na kartce papieru jak to zwykle robię przed zakodowaniem czegoś, listę funkcji jakie będzie wymagał system aby prawidłowo funkcjonował, i staram się je zakwalifikować do poszczególnych "warstw", wzorca MVC tyle że właśnie stanąłem przed problemem w momencie gdy zaszła potrzeba zdecydować czy ma ona należeć do Modelu, czy też Kontrolera
a chciał bym wyrabiać od początku sobie nawyk kodowania w taki sposób by aplikacje były skalowalne.
Ostatni post "thek"-a zawiera mocne argumenty i trzyma się to wszystko kupy, nie mowie że "dr4ko" i "jmail" słabo argumentują.

przepraszam że tak uparłem się tych moich własnych przykładów i tak nimi Was molestuję, ale czy taki podział zmierza w dobra stronę?
prosi bym o korektę z małym komentarzem

  1. //::
  2. class MailSender_model{
  3. function _constructor()
  4. {..........}
  5. function potwierdzRejestracje()
  6. {..........}
  7. function rzadanieZmianyHasla()
  8. {..........}
  9. function rzadanieWystawieniaOpini()
  10. {..........}
  11. }
  12. //::
  13. class Zamowienia_model{
  14. function _constructor()
  15. {..........}
  16. function dodajZamowienieDoBazyDanych() //---a może ta metoda powinna być w klasie kontroler questionmark.gif
  17. {..........}
  18. function dodajPunktyRabatuUzytkownikowi()
  19. {..........}
  20. function odejmijPunktyRabatuUzytkownikowi()
  21. {..........}
  22. function zmienStatusZamowienia() //---jeśli DotPay potwierdził
  23. {..........}
  24. }
  25. //::
  26. class Zamowienia_widok{
  27. function _constructor()
  28. {..........}
  29. function listaZamowienUzytkownika()
  30. {..........}
  31. function szczegolyZamowienUzytkownika()
  32. {..........}
  33. function listaZamowienWszystkich() //---w PA
  34. {..........}
  35. function szczegolyWybraneZamowienie() //---w PA
  36. {..........}
  37. }
  38. //::
  39. class Koszyk_model{
  40. function _constructor()
  41. {..........}
  42. function dodajProduktDoKoszyka()
  43. {..........}
  44. function usunProduktZKoszyka()
  45. {..........}
  46. function wyczyscKoszyk()
  47. {..........}
  48. function przeliczRabatKoszyka()
  49. {..........}
  50. }
  51. //::
  52. class Koszyk_widok{
  53. function _constructor()
  54. {..........}
  55. function pokazKosyzk()
  56. {..........}
  57. function pokazIleRabatuMam()
  58. {..........}
  59. }
  60. //::
  61. class Uyztkownik_model{
  62. function _constructor()
  63. {..........}
  64. function rejestrujUyztkownika() //---dodaj ?
  65. {..........}
  66. function zmienHaslo()
  67. {..........}
  68. function zmienWybraneDane()
  69. {..........}
  70. function Logowanie()
  71. {..........}
  72. function Wzlogowanie()
  73. {..........}
  74. }


tyle ze z tym kontrolerem to już sobie tak namieszałem w głowie ze nie wiem co w nim umieścić, pasuje mi po prostu zwykły switch jak to robilem dotychczas
hehehhe
  1. switch($link_function)
  2. {
  3. //::Basket -------------------
  4. case "add_item": { $_basket -> add_item(intval($_GET['id_item']), 1); break; }
  5. case "update_item": { $_basket -> update_item($_GET['id_item'], $_GET['count_item']); break; }
  6. case "del_item": { $_basket -> del_item($_GET['id_item']); break; }
  7. case "buy_items": { $_basket -> buy_items(); break; }
  8. }
dr4ko
Spoko, chodzi przecież o to żebyś zrozumiał. Jest już późno więc na razie tylko zwrócę ci uwagę na budowę klasy mailera. W twojej wersji jest ona świadoma istnienia innych modeli i ich funkcjonalności. To jest złe podejście. Za każdym razem gdy dodasz nową funkcjonalność wymagającą wysłania maila będziesz musiał grzebać w klasie mailera a to powinno być zbędne. Ta klasa powinna mieć zaimplementowane podstawowe funkcjonalności takie jak: ustawOdbiorce(), ustawTresc(), ustawNaglowki() itp. Natomiast metody które wypisałeś powinieneś rozmieścić w odpowiednich modelach czyli m.in. w Uzytkownik_model. Wtedy chcąc dodać kolejny model, tworzysz w nim metodę odpowiedzialną za wysyłanie maila i konfigurujesz go w niej za pomocą klasy mailera bez ingerencji w MailSender_model.

Aha, i nie używaj podkreśleń w nazwach klas, dobrą praktyką jest używanie tylko camel case'a. Podkreślenia stosuje się raczej w nazwach zmiennych lokalnych w funkcjach.
perhydrol
właśnie się zastanawiałem czy rozbijać MailSender_model, ale jak czytam twojego poprzedniego posta, uświadamiam sobie że to oczywista konieczność.
I już wiem o co chodzi z tą świadomością innych modeli, funkcji. Zaczynam odczuwać jak mi w głowie powoli zaczynają zaskakiwać trybiki hehehe smile.gif
-dzięki

Natomiast co do nazewnictwa to zdaje sobie z tego sprawę, czytałem już kiedyś kilka art. na ten temat i naprawdę staram się, tu akurat nie popisałem się, w dodatku zamieszałem nazewnictwo ang, z pl, jakoś tak odruchowo z tym MailSender-em tongue.gif
PL nazewnictwo wyłącznie do przykładów.
jmail
no dr4ko wreszcie się z czymś zgadzamy biggrin.gif potwierdż rejestrację i inne duperele powinny być nie w senderze tylko w kontrolerze rejestracji na przykład.

tak samo jak notacja wielbłądzia tongue.gif ja bym to jeszcze przemieszał z notacją węgierską dla pełni szczęścia.

czyli dla przykładu

intJakasZmienna - zawsze wiemy jaki typ reprezentuje

perhydrol nie wiem czy zauważyłeś ale constructor chyba z podwójnym podkreśleniem powinien być zanotowany winksmiley.jpg

jeżeli ja miałbym to rozdzielać winksmiley.jpg to bym to zrobił tak (na przykładzie Twojej klasy zamówienie)

  1.  
  2. <?php
  3.  
  4. class Zamowienia_kontroler{
  5. function __constructor()
  6. {..........}
  7. function dodajZamowienieDoBazyDanych() //---a może ta metoda powinna być w klasie kontroler questionmark.gif
  8. {..........}
  9. function dodajPunktyRabatuUzytkownikowi()
  10. {..........}
  11. function odejmijPunktyRabatuUzytkownikowi()
  12. {..........}
  13. function zmienStatusZamowienia() //---jeśli DotPay potwierdził
  14. {..........}
  15. function przygotujListeZamowienUzytkownika($intId /*zakładam, że loosera podajesz przez jego id*/)
  16. {
  17. /*zakładam, że klasa globalna wykonywania zapytań na bazie danych jest utworzona i że wiesz co chcesz tutaj zrobić
  18.  
  19.   Właśnie taka budowa - w mojej opinii sprawia, że model nie ma pojęcia o kontrolerze i o widoku. poproszony jest o konkretne dane (wywołane są odpowiednie jego funkcjonalności)
  20.  
  21.   */
  22. $klasaBazy->parseQuery($parametry, $znacznikQuery); /* bez względu na to co jest pod spodem do model ma wiedzieć jak sparsować takie query - ty masz do środka przekazać parametry i znacznik które query cię interesuje */
  23. $klasaBazy->executeQuery($znacznikQuery); //przykładowa nazwa - tu każesz modelowi pobrać te dane.
  24. $wyniki = $klasaBazy->grabData(); //tutaj zasysam dane - i je chwytasz
  25. /*
  26.  
  27.   tu następuje ich przetworzenie - robisz na nich to co potrzebujesz co w ostateczności ustawia nam jakąś $tablica
  28.  
  29.   */
  30. $this->przygotujDlaWidoku($tablica, 'template_jaki_ma_byc_parsowany');
  31. }
  32. function przygotujSzczegolyZamowienUzytkownika()
  33. {..........}
  34. function przygotujListeZamowienWszystkich() //---w PA
  35. {..........}
  36. function przygotujSzczegolyWybraneZamowienie() //---w PA nie wiem czy to jest w ogóle potrzebne - czy to nie dubluje funki ze szczegółami zamówień użytkownika?
  37. {..........}
  38. function przygotujDlaWidoku($parse, $template){
  39. $widok = new globalnaKlasaWidoku($template);
  40. $widok->assignVars($parse);
  41. $widok->view->display();
  42. }
  43. }
  44.  
  45. class globalnaKlasaWidoku{ /* dlaczego globalna? ponieważ uważam, że Widok również nie powinien być świadomy kontrolera. On daje funkcjonalności, które pozwalają wyświetlić dane w sposób jaki tego oczekujemy, ale tworzenie widoku dla każdej klasy jest zbędne moim zdaniem. Jak już mówiłem wcześniej - cała logika dzieje się w kontrolerze, model dostarcza danych, widok je wyświetla, a kontroler musi to powiązać ze sobą */
  46. $this->module = '';
  47. public function __constructor($module){
  48. $view = new Smarty(); //zakładam wykorzystanie Smartów i funkcji __autoload
  49. $this->module = $module;
  50. }
  51.  
  52. public function assignVars(&$parse){
  53. foreach($parse as key => $value){
  54. $this->view->assign($key, $value);
  55. }
  56. }
  57. }
  58.  
  59. ?>
  60.  
  61.  



thek -> widzę, ze się całkowicie rozmijamy ze zrozumieniem.

Mnie chodzi o to, że dla mnie baza danych to nie jest konkretny silnik bazodanowy. to nie jest MySQL Postgre XML CSV FB IB SQL Server ORACLE czy bóg wie jeszcze co.

Pokazałem w przykładzie powyżej.

Ja chcę wywołać parseQuery z parametrami i wskazaniem które dane mnie interesują - bez względu na to czy to jest XML, baza danych (w sensie silnika), czy to jest kij wie co.

Na przykład zapisałem sobie w XML'u swoje zapytania do bazy danych (podkreślam ponownie to jest przykład! nie chodzi mi o konkretny silnik bazy danych)

załóżmy

  1. <query id="1">
  2. select * from {param1} where {param2} = {param3}
  3. <query>


teraz chcę to wywołać w kontrolerze

  1.  
  2. $params[1] = 'prametr1';
  3. $params[2] = 'prametr2';
  4. $params[3] = 'prametr3';
  5. $klasaBazy->parseQuery($params, 1); // sparsuj zapytanie 1 (gowno mnie obchodzi czy dla pliku XML czy dla silnika bazy danych czy dla pliku tekstowego - to już ma wiedzieć model z czego korzysta), używając tablicy params
  6. $klasaBazy->executeQuery(); //wykonaj to sparsowane zapytanie - znowu mnie nie obchodzi czy na Oracle'u czy na liczydle
  7. $wynik = $klasaBazy->grabData(); //chcę dostać wyniki niezależne od silnika bazodanowego - chcę dostać prostą tablicę asocjacyjną klucz - wartosc
  8.  


i teraz sobie mogę na tym wyczyniać co mi się żywnie podoba

Co do na przykład wysyłki. Ja osobiście uważam, że to kontroler musi wiedzieć jakie opcje wysyłki mam możliwe. założmy:

1. GG
2. mail
3. twitter

user przekazuje do kontrolera 4 to co on ma to bez sprawdzenia do modelu przekazać? oczywiście że nie chodzi mi o to, żeby on wykonał mail(). Chodzi mi o to, żeby on wywołał wyslij(typ wysyłki, dane_z_formularza). Ale też musi wiedzieć czy typ wysyłki jest obsługiwany - dzięki temu od razu zwraca userowi błąd


Hmmmmmmm. jak na to wszystko patrzę to chyba chodzi nam o to samo ale inaczej nazywamy biggrin.gif funkcjonalności biggrin.gif
thek
Dla mnie termin baza danych jest jednoznaczny i stąd tak to odebrałem. W takich sytuacjach by unikać nieporozumień używam po prostu dane lub źródło danych. To terminy na tyle abstrakcyjne, że można pod nie podpiąć jakąkolwiek formę danych.

Co do notacji wielbłądziej jestem zwolennikiem. W PHP sens nabiera węgierska, bo nieraz w locie się zmienia typ, a to nie zawsze jest pożądane.

Odnosząc się do przykładu zgadzam się, że tak powinno to wyglądać, z tym, że tak naprawdę sparsowanie nie jest potrzebne kontrolerowi. Ono się już wewnątrz samego modelu powinno wykonać. Ty podajesz parametry a w modelu odbiera je, parsuje i przetwarza w powiązaniu ze źródłem danych. Tak zwrócone dane kontroler dystrybuuje do widoku (lub kilku). Dla mnie w takim wypadku nie ma znaczenia czy kontroler wie czy nie z czym ma do czynienia. Gdyby jakiś przykład podać, to myślę, że dobrym mógłby by być JS, a konkretnie aplikacja z AJAX i powiązanymi select. Kontroler nie wie jakie ma dostępne możliwości. Źródło danych dynamicznie je dołącza. Kontroler ma tylko dostęp do metod ogólnych, a źródło wybiera na podstawie dynamicznie dołączanej listy. W ten sposób możemy w zależności od sytuacji okrajać lub poszerzać możliwości aplikacji, zaś kontroler mimo braku świadomości warstwy niżej może nakazać modelowi stosowanie konkretnego typu danych. Po prostu wywoła metodę ogólną i to będzie jego jedyne zadanie, ewentualnie ustawi źródło danych. Raz będzie to plik, a raz baza. Z tą wykładnią modelu jaką obecnie podałeś mogę więc się zgodzić. Pisząc jednak o świadomości kontrolera co wywołuje mimowolnie narzucasz odbiorcy myślenie w formie istnienia funkcji kontrolera, która zajmuje się określonym wariantem, czyli dla mnie i zapewne wielu osób zamiast get(param) -> getXML(param), getDB(param). I stąd nasze nieporozumienie. Można powiedzieć, że nie mówiliśmy niewystarczająco abstrakcyjnie winksmiley.jpg

Co do tego czy kontroler wie co może być źródłem danych, to on nie musi tego wiedzieć. Tę świadomość ma klasa związana z konfiguracją. Kontroler ma do niej dostęp i wpływać może na nią, co skutkuje także wpływem na używanie przez model określonego źródła danych. Instalacja "pluginu" jest, przykładowo, poinformowaniem konfiguracji "hej... pojawił się nowy typ źródła danych. Jakby co poinformuj odpowiednie kontrolery, że mogą mnie wybrać, a modele mają przeciążone pewne funkcje". To, jak to będzie rozwiązane to już osobna sprawa smile.gif Może to być choćby zwykły dopisek w pliku konfiguracyjnym, że w ścieżce takiej a takiej jest plik, który można użyć i służy on do obsługi innego źródła danych niż dotychczasowo znane. Myślę, że opisałem moje podejście zrozumiale, bo nie wiem czy można jeszcze inaczej. Tak chyba było najprościej. Może co prawda zajść sytuacja, że ma coś w konfigu, ale user perfidnie wywalił pliki odpowiadające za dany typ źródła danych. No ale od tego jest system obsługi błędów winksmiley.jpg

Ogólnie widzę, że zaczynamy już dochodzić do consensusu w sprawie terminologii i postrzegania wielu aspektów, a co za tym idzie, mniej pojęć i zdań niejednoznacznych smile.gif

EDIT: Bym zapomniał... Wspomniane
Cytat
Chodzi mi o to, żeby on wywołał wyslij(typ wysyłki, dane_z_formularza)
Może ostatecznie być rozwiązane na dwa sposoby:
1) Kontroler dowiaduje się z klasy konfiguracji co mamy za źródła danych i jakie z nich jest domyślne (tak odbieram teraz Twoje podejście)
2) Informacje o źródle danych pobiera sam model, też z konfiguracji, przykładowo podczas wywołania konstruktora (to podejście podzielam ja) i w ten sposób nie jest potrzebne określanie typu wysyłki.
Efekty będą identyczne.
dr4ko
Idealną sytuacją by było gdyby ani kontroler ani model nie były świadome charakteru źródła danych (sytuacja możliwa gdy DAL będzie identyczny niezależnie od źródła, czyli gdy korzystamy z PDO). Wtedy odpowiedni PDO byłby ładowany przez autoloader a jego wywołanie byłoby zawsze takie samo i by wyglądało mniej więcej tak:
  1. Controller
  2. {
  3. public function changePassword()
  4. {
  5. if(Helper::checkPassword($_POST['new_pasword'])
  6. {
  7. return $user->changePassword($_POST['new_pasword']);
  8. }
  9. else $this->doSomething();
  10. }
  11. }
  12.  
  13. UserModel
  14. {
  15. protected $dataSource;
  16.  
  17. public function __construct()
  18. {
  19. $this->dataSource = Context::getInstance()->getDataSource();
  20. }
  21.  
  22. public function changePassword($new_password)
  23. {
  24. $user_record = $this->dataSource->getUserRecord();
  25. $user_record ->setPassword($new_password);
  26. $user_record ->setDateUpdated('now');
  27. return $user_record ->save();
  28. }
  29. }


W ten sposób nawet jeśli podmienimy źródło nie musimy ruszać nic poza plikiem konfiguracyjnym. Dodanie kolejnych rodzajów źródeł nie będzie wymagało dopisywania w kontrolerze kolejnych case'ów w switchu bo PDO (czyli nasz DAL) wczyta odpowiednie źródło na podstawie konfiguracji i będzie dane serwowało za pomocą tych samych obiektów.
phpion
Cytat(jmail @ 15.10.2009, 17:01:21 ) *
Model to baza danych i ewentualnie jej warstwa programowalna a nie funkcjonalności w php jakie sobie umyślisz

Spłycasz problem. Model to nie baza danych, ale model może korzystać z bazy danych. Równie dobrze model może pobierać dane z pliku tekstowego, poprzez SOAP czy z satelity z kosmosu. To tak w ramach wyjaśnień.
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.