Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [mvc] Problem z obsługą błędów
Forum PHP.pl > Forum > PHP > Pro > Archiwum Pro
Vengeance
Oto mały przykład "wycięty" z mojego, powstającego jeszcze frameworka (jak widac pisanego mniej wiecej wg zasad MVC).
Przy starcie aplikacji wywołujemy metode Application::run() która wybiera akcje oraz widok. Uruchamia akcje, która z kolei może poprzez 'return' zwrócić:
+ Obiekt dziedziczący z klasy Action - wtedy kontroler uruchomi tę akcje (w ten sposób obsługiwane są łańcuszki akcji.
+ Inne, dowolne dane - wtedy kontroler traktuje je jako wynik działania akcji(łańcuszka) i przekazuje go do widoku(poprzez konstruktor). Na końcu kontroler wyświetla widok.

Po 1. Powiedzcie co tu może być źle przezemnie zrozumiane itd. Co można poprawić/zmienić. Jestem otwarty na propozycje ;]

Po 2. Mam problem z obsłużeniem błędów akcji. Takich jak np. w akcji 'showNews' brak newsa w bazie danych. Na razie obsługuje to jako łańcuszek jednak problem pojawia się, gdy akcja 'showError' korzysta z innego widoku niż akcja 'showNews'. Trzeba by było jakoś sensownie ten widok podmieniać. Jak ?
Albo jak rozwiązań to "fajnie". Mam wrażenie (pewnie uzasadnione), że to co istnieje teraz jest dość felernym rozwiązaniem.

ps. nie chce wykorzystywać czegoś ala 'fallback-action' gdyz to umożliwia generowanie tylko jednego komunikatu błędu gdy ogólnie akcja się "spsuje".

Prosze o pomoc smile.gif

Model:
  1. <?php
  2. class NewsContainer
  3. {
  4.    public function getNewsByID($iID)
  5.    {
  6.       if($iID <> 1) throw new NewsNotFoundException();
  7.       return new News('Jakis Tytul', 'Jakas Tresc');
  8.    }
  9. }
  10.  
  11. class News
  12. {
  13.    public $title, $content;
  14.    public function __construct($title, $content)
  15.    {
  16.       $this->title = $title;
  17.       $this->content = $content;
  18.    }
  19. }
  20. ?>


Akcja:
  1. <?php
  2.  
  3.    class showNews extends Action
  4.    {
  5.       public function display()
  6.       {
  7.          try {
  8.             $model = new NewsContainer();   
  9.             $news = $model->getNewsByID(1);
  10.             return $news;
  11.          } catch(NewsNotFoundException $e) {
  12.             return new showError('Informacja', 'News o podanym ID nie istnieje');
  13.          }
  14.       }   
  15.    }
  16.  
  17. ?>


Widok:
  1. <?php
  2.  
  3.    class showNewsAsHTML extends View
  4.    {
  5.       private $news, $_template;
  6.  
  7.       public function __construct($oNewsRecord)
  8.       {
  9.          $this->news = $oNewsRecord;
  10.          $this->_template = new Smarty();
  11.       }
  12.  
  13.       public function display()
  14.       {
  15.          $this->_template->assign('news', $news);
  16.          $this->_template->display('html/showNews.tpl');
  17.       }   
  18.    }
  19.  
  20. ?>


Kontroler:
  1. <?php
  2.  
  3. class Application
  4. {
  5.       public function run()
  6.       {
  7.          $sActionName = $_GET['action'];
  8.          $sViewName = $_GET['view'];
  9.             
  10.          $mDataForView = $this->performAction(new $sActionName());
  11.          $oView = new $sViewName($mDataForView);
  12.          $oView->display();
  13.       }
  14.  
  15.       public function performAction(Action $oAction)
  16.       {
  17.          $result = $oAction->perform();
  18.          /* Obsługa łańcuszków akcji */
  19.          if($result instanceof Action)
  20.             $this->performAction($result);
  21.          else 
  22.             return $result;
  23.       }
  24. }
  25.  
  26. ?>
ActivePlayer
  1. <?php
  2.  
  3. die(&#092;"Brak newsa\");
  4.  
  5. ?>


jestes w stanie zarzucic mi w celach edukacyjnych cały kod ?
bela
W Oceny jest smile.gif
Vengeance
http://forum.php.pl/index.php?showtopic=26844

Tu jest cały kod jednak w starszej wersji. Tzn tam jest rozdzielenie na akcje widok/logiki. Teraz jednak pracuje nad oddzieleniu tego wszystkie i stowrzeniu poprawnych akcji i widoków winksmiley.jpg Jak ktoś ma pomysł i może pomóc to prosze.
Kinool
tak na marginesie to:
Cytat
public function __construct($title, $content)
konstruktor powienien byc raczej bez deklarowaia go jako public, domyslnie jest private, no chybaze ze to zamierzony zamiar wywolania ale nie bardzo rozumie do czego?
Vengeance
No Coś ty ! Od kiedy kontruktor jest 'private'!

Takie coś stosuje się tylko w Singletonie. Przcież ustawnienei 'private'
uniemożliwi ci zrobienie '$obj = new Object();' hehe

Dzieki i tak za uwage ;] czekam na kolejne ;p
chmolu
Na początek, jako że jestem nowy na tym forum, chciałbym przywitać wszystkich forumowiczów winksmiley.jpg

Widzę, że i tu ludzie zmagają się z MVC.

Cytat
Przy starcie aplikacji wywołujemy metode Application::run() która wybiera akcje oraz widok. Uruchamia akcje, która z kolei może poprzez 'return' zwrócić:
+ Obiekt dziedziczący z klasy Action - wtedy kontroler uruchomi tę akcje (w ten sposób obsługiwane są łańcuszki akcji.

Osobiście uważam, że łańcuszki akcji prawie nigdy ci się nie przydadzą. Lepiej operacje typu 'Usuń newsa' -> 'usuń artykuł' umieszczać w jednej akcji wywołując kilka klas modelu. W swoim frameworku mam metodę forward($action) kontrolera, którą może wywołać inna akcja. Tak realizuję łańcuszek. To akcja wywołuje następne akcje. Plus jest taki, że można na przykład wywołać inną akcję przed uruchomieniem właściwej. Można to zrobić tak jak u ciebie - to też jest dobre rozwiązanie. U mnie by się ono nie sprawdziło. Ale, tak jak powiedziałem, w praktyce 'łańcuszki akcji' spotyka się bardzo rzadko.

Cytat
+ Inne, dowolne dane - wtedy kontroler traktuje je jako wynik działania akcji(łańcuszka) i przekazuje go do widoku(poprzez konstruktor). Na końcu kontroler wyświetla widok.

To jest ciekawe rozwiązanie. Można w ten sposób na przykład przekazać listę błędów Validatora formularzy.

Cytat
Po 2. Mam problem z obsłużeniem błędów akcji. Takich jak np. w akcji 'showNews' brak newsa w bazie danych. Na razie obsługuje to jako łańcuszek jednak problem pojawia się, gdy akcja 'showError' korzysta z innego widoku niż akcja 'showNews'. Trzeba by było jakoś sensownie ten widok podmieniać. Jak ?
Albo jak rozwiązań to "fajnie". Mam wrażenie (pewnie uzasadnione), że to co istnieje teraz jest dość felernym rozwiązaniem.

W moim frameworku nie ma akcji typu showNews/showArticle/showXX. To jest zadanie widoków. Przez akcję rozumiem coś, co zmienia model (addArticle, addNews, deleteNews). A zatem, jeśli http://www.mysite.com/index.php?view=showNews to wtedy nie ma potrzeby uruchamiania akcji. Odpalam tylko odpowiedni widok. Jeśli natomiast http://www.mysite.com/index.php?action=deleteNews to odpalam akcję, która decyduje, który widok wyświetlić (np. $controller->setView($name)).

Widoki w moim frameworku chcę rozwiązać podobnie jak to jest w WACT. Też będą komponenty, tagi, z tym, że to wszystko będzie oparte na drzewie DOM (tyle, że bez używania DOM::save/load - to muszę niestety napisać sam :/).

Jak rozwiązać problem błędów? Nie ma akcji typu showNews, więc sprawdzenia, czy dany news istnieje możesz dokonać w widoku. Nie wiem jak to będzie w smarty, bo odczuwam niechęć do tego systemu. Zobacz, jak to jest rozwiązane w WACT - IMHO znakomity sposób (z wykorzystaniem komponentów). A jeśli błąd wystąpi w akcji typu deleteNews to możesz przerwać akcję i wywołać setView('Error', $dane /* komunikaty o błędach */);

Są różne sposoby. Właśnie przez takie drobne wydawałoby się problemy MVC staje się strasznie trudne do implementacji :/. Ja też na razie nie mogę wymyślić, jak radzić sobie np. z błędami Validatora, czy z autoryzacją. Odezwij się na gg to może razem coś wymyślimy winksmiley.jpg
bela
Łańcuszki rzadko ? To zależy czy akcję są podzielone na akcje widoku i logiki. Te pierwsze mają za zadanie wyświetlić dane z modelu, a drugie wprowadzić zmiany w modelu. Przy takim układzie łańcuszki to rzecz naturalne: dodajemy news a później wyświetlamy informacje, albo przy niepowodzeniu, akcje logiki wyrzuca fallback(wyjatek), czyli w gruncie też akcje widoku.

Co sie w smarty moze się nie podobac dry.gif

Problem z łańcuszkami jest gdy chcemy wrzucić 2 akcje logiki do łańczuszka, bo co zrobić gdy jedna przejdzie a druga wyrzuci fallback.
Vengeance
@chmolu:
Łańcuszki właśnie przydają się często smile.gif Przynajmniej ja widze, że będzie mi to przydatne.

Cytat
W moim frameworku nie ma akcji typu showNews/showArticle/showXX. To jest zadanie widoków. Przez akcję rozumiem coś, co zmienia model (addArticle, addNews, deleteNews).

Wiesz, ja wychodze z założenia, że podel sam z siebie nie może być "uruchomiony". Tzn _zawsze_ musi wystąpić akcja, nawet jeśli jest działanie ogranicza się do zrobienia 'return true;'. Wtedy dopiero kontroler inicjuje widok. Pozatym w widoku nie pobieram żadnych danych bezpośrednio z modelu. Tego dokonuje akcja przekazując następnie wynik do modelu. Z tego właśnie wynika, że stosuje akcje typu showNews/showArticle/showXX.

Cytat
A jeśli błąd wystąpi w akcji typu deleteNews to możesz przerwać akcję i wywołać setView('Error', $dane /* komunikaty o błędach */);

Także miałem pomysł, aby wykorzystać "coś" co jest w stanie zmienić widok. Metoda setView czy też jaki dodatkowy parametr zwracany w 'return'. Jednak wydawało mi się to jakieś "dziwne". Po to właśnie storzyłem ten wątek aby uzyskać inne sposoby winksmiley.jpg
chmolu
A może model zamiast wywalać wyjątek powinien po prostu zwrócić "pusty" obiekt, albo obiekt typu Error:

  1. <?php
  2.  
  3. class Error
  4. {
  5.  private $message;
  6.  
  7.  public function __construct($message)
  8.  {
  9.  $this->message = $message;
  10.  }
  11.  
  12.  public function getMessage()
  13.  {
  14.  return $this->message;
  15.  }
  16. }
  17.  
  18. ?>


Wtedy twoj kod moglby wygladac tak:

  1. <?php
  2.  
  3. class NewsContainer
  4. {
  5.  public function getNewsByID($iID)
  6.  {
  7. if(!news_exists) 
  8. {
  9. return new Error('News o podanym ID nie istnieje');
  10. }
  11. return new News('Jakis Tytul', 'Jakas Tresc');
  12.  }
  13. } 
  14.  
  15. class showNews extends Action
  16. {
  17.  public function display()
  18.  {
  19.  $model = new NewsContainer();  
  20.  $news = $model->getNewsByID(1);
  21.  return $news;
  22.  }  
  23. }
  24.  
  25. class Application
  26. {
  27. private $view = DEFAULT_VIEW;
  28.  
  29. public function run()
  30. {
  31.  $sActionName = $_GET['action'];
  32.  $this->view = $_GET['view'];
  33.  
  34.  $mDataForView = $this->performAction(new $sActionName());
  35.  $oView = new $this->view($mDataForView);
  36.  $oView->display();
  37. }
  38.  
  39. public function performAction(Action $oAction)
  40. {
  41.  $result = $oAction->perform();
  42.  /* Obsługa łańcuszków akcji */
  43.  if($result instanceof Action)
  44. $this->performAction($result);
  45.  else
  46.  {
  47. if ($result instanceof Error) $this->view = 'Error';
  48. return $result;
  49.  }
  50. }
  51. }
  52.  
  53. ?>
Vengeance
Zauwaz, że akcja czesto dokonuje operacji na danych z modelu smile.gif
A wiec wole wyjatki (ladniej wylapuje sie blad).

Jedyne co moge potwierdzic, to ze juz myslalem aby w performAction()
dodac jeden warunek 'instanceof Error' co powodowalo by np. zmiane widoku, czy zdefiniowanej przez uzytkownika akcji, obslugujacej bledy.
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.