Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Wątpliowści odnośnie układu trójwarstwowego
Forum PHP.pl > Forum > PHP > Object-oriented programming
Helios
Witam

Tworze prosty szkielet aplikacji trojwarstwowej i chcialem Was zapytac juz na wstepie (i troche wykorzystac Wasze obycie z takimi schematami aplikacji), czy takie rozwiazanie bedzie mialo racje bytu, czy rozgrzebie jakikolwiek projekt i okaze sie w polowie, ze wszystko trzeba gruntowanie przebudowywac.

1. Sprawa kontrolera glownego, w mojej aplikacji jest to klasa MyApplication dziedzicaca po klasie Application oraz implementujaca interfejs Runnable. Klasa Application wywoluje metode init() na klasie potomka no i wymusza jej istnienie. Teraz pytanie, czy samo rozdzielenie takiego szkieletu na metode run() i init() wystarczy. Narazie nie widze wiekszych problemow, wszystko co trzeba zainicjowac na poczatku tworze w init() np. Registry, View itd.

2. Sprawa widoku. Moim zdaniem jezeli widok tworze w init() i wykonuje kolejne akcje to do momentu natrafienia na akcje o typie "widok" nie wiem na jakim widoku dokladnie bede pracowal. Stworzylem obiekt View, ktory przyjmuje dane i je udsotepnia. Obiekt widoku przekazuje do kontrolerow poszczegolnych akcji. Na samym koncu ustawiam odpowiedni obiekt renderuacy i wyswietlam widok, przekazujac do obiektu renderujacego pobrane wczesniej dane. Wyglada to mniej wiecej tak:

  1. <?php
  2. class View {
  3.  
  4.    private $renderer;
  5.    private $params;
  6.  
  7.    public function set($key, $value){}
  8.    public function get($key, $value){}
  9.    public function __set($key, $value){}
  10.    public function __get($key, $value){}
  11.  
  12.    public function setRenderer(Renderer $renderer){}
  13.  
  14.    public function setTemplate($template){
  15.        $this->renderer->setTemplate($template);
  16.    }
  17.  
  18.    public function render(){
  19.        $this->renderer->render();
  20.    }
  21.  
  22. }
  23.  
  24. abstract class Renderer {
  25.  
  26.    private $template;
  27.  
  28.    public function setTemplate(){}
  29.    public function getTemplate(){}
  30.    abstract public function render();
  31. }
  32.  
  33. class Renderer_Smarty extends Renderer {
  34.    public function render(){
  35.        // pobieramy szablon $template->getTemplate();
  36.        // iniciujemy smarty
  37.        // display();
  38.    }
  39. }
  40.  
  41. class MyApplication extends Application implements Runnable {
  42.  
  43.    private $view;
  44.  
  45.    public function init(){
  46.        $this->view = new View;
  47.    }
  48.  
  49.    public function run(){
  50.        $this->view->imie = 'Jan';// tak samo beda mialy dostep do widoku metody kontrolerow akcji
  51.        $this->view->setRenderer(new Renderer_Smarty);
  52.        $this->view->setTemplate('index.tpl');
  53.        $this->view->render();
  54.    }
  55.  
  56. }
  57. ?>


Chodzilo mi glownie o to, ze raz akcja moze wyswietlac dane przez Smarty innym razem bede chcial te same dane pobract przez JSON. Wiec mysle, ze takie rozwiazanie sie sprawdzi, chociaz trudno mi dokladnie powiedziec.

3. Sprawa to kontroler akcji. Zbior akcji dot. danego zagadnienia bede ubieral w kontroler akcji np. News_Controller, Guestbook_Controller itd. Czy poszczegolne metody w kontrolerze akcji powinny zwracac do kontrolera glownego jakies dane np. obiekt Response przekierowujacy kontroler na dana akcje. Czy metody kontrolera akacji powinny "znac" nazwy kontrolerow innych akcji, ich metody itd. Czy powinno to byc konfigurowalne na poziomie kontrolera. Czyli np. kontroler dostaje zadanie wykonania akcji ShowNews akcja sie wykonuje i z konfigu kontroler sie dowiaduje ze ma wywolac akcje CountVisit w kontrolerze Statistics? Czy te metody powinny miec jakis wplyw na sterowanie aplikacja w tym sensie, ze klasa abstrakcyjna kontrolera akcji mialaby zaimplementowany mechanizm przenoszenia forward(), wysylania naglowkow header() itd.? Czy znow dostep do tego ma tylko kontroler glowny?

4. Czy metody kontrolerow akcji "znaja" nazwy parametrow, ktore dostana z zadania. Tzn. zakladamy, ze router przeparsuje mi URL i dostapne z tego: id(32), name(root), czy akcje moga odwolywac sie np. do obiektu Request->get('id'), Request->get('name') itd.? Czy powinny odwolywac sie do tego np. przez Request->get(0)?

5. Stworzylem sobie zgodnie ze wzorcem Registry klase, ktora udostepnia statyczne metody set, get itd. Czy to jest dobry sposob na przechowywanie danych np. o uchwytach do PDO, configu itd.? Czy dobrym pomyslem, jest tworzyc w takim rejestrze blokade, ze jezeli obiekt o podanym kluczu istnieje to wyrzucac wyjatek i tworzyc instancje obiektu polaczenia z baza danych w init(), czy np. pozwolic metodom kotrolera akcji na tworzenie polaczen wtedy kiedy bedzie taka potrzeba (przy czym jedna nie bedzie wiedziala o tym, czy poprzednia przypadkiem nie rejestrowala juz polaczenia z baza)?

6. Router - czy dobrze rozumiem, ze router ma mi parsowac adres url i wyciagac z niego nazwe kontrolera i akcji oraz skladac url w druga strone? Czy jest to po to, abym np. w pliku szablonu mogl sie odwolac w ten sposob w celu wygenerowania linku: $router->getUrl($mapper->getRule('news'), array('id' => 32, 'name' => 'Wzorce_projektowe'))? Czy widok ma dostep do routera i moze sobie skladac url'e, czy kontroler ma dostarczyc te dane do widoku?

7. Lancuchy akcji. Widze, ze sporo rozwiazan opiera sie na XML'u. Czy konfiguracja akcji nie zamula w ten sposob calej aplikacji? W jaki sposob ewentualnie moge to jakos zacacheowac?

8. Docelowo w tym schemacie bede chcial korzystac z cache'u. Czy dobrym rozwiazaniem bedzie, aby kontroler akcji dopiero sprawdzal, czy dane istnieja w cache i jezeli nie to dopiero pobieral je pracujac na modelu. Czy macie jakies inne sprawdzone rozwiazania? Jak to wyglada w praktyce? Czy do cachu mam tworzyc jakis obiekt o wspolnym interfejsie, ze w razie czego podmienie tylko obiekt cache'ujacy, czy mam pracowac w kontrolerach bezposrednio na funkcjach jakis memcache itd.?

9. Obiekt kontekstu - czy jest to poprawne, zeby do obiektu kontekstu mialy dostep wszystkie kontrolery akcji. Tzn. w przypadku wykonywania lancucha akcji jedna akcja zapisuje do kontekstu jakies dane, nastepna te dane pobiera. Czy to sie nie gryzie? Moze te akcje nie powinny nic o sobie wiedziec? Jak uwazacie? Jak w takim razie zapewnic komunikacje i dostarczyc dane przetworzone przez jedna akcje do drugiej?

10. Sprawa autoryzacji - czy jezeli bede wymagal autoryzacji do wykonania jeden akcji, to gdzie mam ja sprawdzic i skad mam wiedziec czy ja sprawdzac. Zakladam, ze sprawdzam to w kontrolerze. Czy podobnie jak przy mapowaniu urli mam miec konfig i liste ról jakie musi miec uzytkownik, wraz z nazwa sterownika autoryzacji i ladowac je w kontrolerze? Co jezeli, kazda akcja wymaga innych rol i np. w polowie wykonywania lancucha akcji okaze sie, ze uzytkownik nie ma uprawnien do wykonania czesci akcji? Jak wtedy cofnac wprowadzone dane. A moze od razu sprawdzac, czy uzytkownik bedzie mogl wykonac wszystkie akcje i dopiero pozniej je uruchamiac?

I to chyba narazie tyle pytan, moze jeszcze cos mi sie przypomni. Bede wdzieczny za odpowiedzi.
LBO
Będzie nie po kolei smile.gif

10. Robisz tak, żeby móc odczytać uprawnienia dla poszczególnych akcji. Np. w pliku konfiguracyjnym. Jeżeli jedna akcja to u ciebie klasa możesz też zrobić coś w stylu Action::allow(), gdzie metoda ta zwraca uprawnienia potrzebne do jej odpalenia.

6. Router ma wyciągać jakiekolwiek dane z czegokolwiek. W przypadku HTTP będzie to URL i nagłówki, dla konsoli będą to parametry wywołania itd. I tak, tam gdzie potrzeba musi działać w drugą stronę.

7. Cachuj jako PHP, najlepiej jako kod integrujący się z Twoim frameworkiem.

8. Zależy co rozumiesz przez kontroler, bo jeżeli to obiekt, który wyszukuje akcje po plikach i je odpala to tak, on odpowiedzialny jest za cache (i nazywa się Dispatcher)... w najprostszym wydaniu. Poczytaj trochę o Interceptor Filters.

5 i 9 Jak dla mnie Registry i kontekst gryzą się ze sobą. Albo zamykasz aplikacje we front kontrolerze i nie ma do niczego dostępu z zewnątrz (wtedy kontekst) albo olewasz hermetyzacje i robisz co się podoba (za czym idzie burdel potem w kodzie).

4 Tak znają, na tym opiera sie cały routing, że przekazujesz odpowiednio nazwane parametry do odpowiednich akcji, które wiedzą co z tym zrobić.

3. headery i redirect powinny być w obiekcie Response. forward to część controlera/dispatchera.

2. Jeżeli widokiem nazywasz szablon to powinieneś wymyślić jakiś system domyślnych nazw szablonów dla akcji i ewentualnie z niej przechwytywać wybranie innego szablonu. Z tym, że szablon powinien być częścią renderera. Dla renderera PHP, albo Smarty szablon jest potrzebny z Ich natury. Natomiast dla renderera Json nie smile.gif

1. Przenieś powtarzalne rzeczy do plików konfiguracyjnych. init() mogłoby słuzyć za miejsce, gdzie musisz koniecznie wklepać kod żeby postawić aplikację (na przykład poustawianie include_path).
pinochet
1. Init i run jest raczej dobrym pomysłem - zawsze z dwóch można zrobić jeden - z jednego dwóch nie zrobisz :] pozatym patrz na znane i sprawdzające się rozwiązania np CURL.
2.
Cytat
public function setRenderer(Renderer $renderer) ....
public function setTemplate($template)
Tak jak napisał LBO jeżeli polimorfizm to konsekwentnie - uniwersalne szablony albo widoki którym nic z góry nie narzucasz - koncepcia "hawka", w której to widok pobiera dane z modelu. Wtedy każdy widok może robić zupełnie co innego bez dużych zmian minus jest taki, że widoków tworzy się dość duż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.