Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: MVC - stałe elementy strony
Forum PHP.pl > Forum > PHP
anas
Witam.

Po raz kolejny pytanie dotyczące MVC - tym razem chodzi mi o fakt kiedy na stronie mamy jakieś stałe elementy - czy każdy widok musi je generować z osobna i za każdym razem uzywajac systmu szablonów przypisywać jakieś wartości dla nagłówka, stopki, stałego menu - itd itd.

Tak naprawdę akcje decydują u mnie o tym co się wyświetla w głównej części serwisu i chciałbym żeby stałe elementy były wyświetlane wyłącznie w jednym pliku, a takze uruchamiany kontroler ktory zdecyduje co wygenerowac dla glownej czesci serwisu.

Czy to ma prawo bytu? Jezeli nie - jak najlatwiej rozwiazac ten problem - licze na wasze jak zawsze dobre pomysly.

pozdrowka

anas
bregovic
Nie do końca rozumiem o co tobie chodzi, lecz możesz zawsze użyć cachoe'owania - jeśli używasz Smarty możesz z powodzeniem cachoe'ować tylko niektóre elementy template'ów.
anas
Hej.

Co do cashowania to jasna sprawa - chodzi mi raczej o fakt iż mam serwis www, a w nim naglowek, stopke, menu z lewej strony - ktorej na kazdej podstronie wygladaja tak samo - zmienia sie czesc glowna serwisu(srodek) - tym ma zarzadzac wlasnie moja aplikacja oparta o MVC - ale nie chce tez rozdzielac jednego od drugiego - to co chcialbym osiagnac to taki defaultowy widok do ktorego dolaczane bylyby widoki wywolywane przez jakies tam akcje. A pytanie moje brzmi jak to zrobic - bo sama idee mam - gozej z implementacja... nie wiem od ktorej strony sie do tego zabrac.

pozdrawiam

anas
bregovic
To czego chcesz przypomina zalążek mojego frameworka - zakładając że używasz Smarty, robisz sobie jednego template'a - nazwijmy go index.tpl.
I teraz robisz tak, że w każdej akcji definiujesz zmienną template'ową "page" - używając:
  1. <?php
  2.  $objSmarty->assign('page', 'id-strony-do-wyswietlenia');
  3. ?>

A w templacie, na początku:
  1. {include file="$page.tpl" assign="zawartosc-templatu-podstrony"}

Dalej w templacie, już po zdefinowaniu headera, menu, itp, wyświetlasz zainkludowany środek strony, pisząc:
  1. {$zawartosc-templatu-podstrony}
aleksander
ja mam w plikui konfiguracyjnym sekcje preLoadingActions i postLoadingActions które zawierają nazwy akcji które mają być wykonane przed wywołaniem właściwej akcji i po.
bregovic
olo twoje rozwiązanie też jest jeśli reszta strony ma być niestatyczna. Można w sumie też mieć akcję bazową, w której wykonujemy wszystkie czynności które mają zostać wykonane na każdej stronie, a wszystkie inne akcje extend'ują od niej.
anas
@bregovic - tak tez zrobilem - popatrzylem i w phiend jest to rozwiazane wlasnie tak jak mowiles - klasa akcji bazowej, a po niej dziedzicznone pozostale klasy... sprytne rozwiazanie.

@olo - tez nieglupi pomysl - tylko martwi mnie fakt iz zmuszasz aplikacje do wywolanie czegos na stale - chociaz jak kto woli

Ps. a jak sie ma sprawa wydajnosci - moze testowaliscie co dziala szybciej - dziedziczenie po akcji bazowej czy taka sama akcja ktora ma przedakcje i akcje wywolywane po czesci glownej.

pozdrowka

anas
aleksander
albo można zrobić tak: dla każdej akcji masz zdefiniowane kiedy ma być odpanala (jaki argument GET ma być) i robisz tam always - czyli zawsze. tylko nie wiem za bardzo jak zdefiniować kolejność wyświetlania poszczególnych elementów.
bela
Cytat(olo @ 2004-12-29 16:08:14)
tylko nie wiem za bardzo jak zdefiniować kolejność wyświetlania poszczególnych elementów.

moze na chama smile.gif zserializowac tablice i do sesji wrzucic
DeyV
ja standardowo wykorzystuję dziedziczenie po róznych rodzajach akcji.
Jest to bardzo przydatne, bo można np. tylko raz zdefiniować, co ma być wykonywane na np. stronie z wydrukiem, (np. nie drukuje menu, wiec nie muszę go generować)

Następnie wszystkie akcje związane z wydrukiem dziedziczą po tejże, i odpowiednio ją rozszeżają.

Czasem można ten proces skomplikowac jeszcze bardziej. Jakiś przykład?
Sonda wyświetlana jest na głownej stronie, gdzie jest menu boczne i górne. Nie ma jednak innych dynamicznych elementów.
dziedziczy wiec po abstrakcyjnej pseudo akcji "strona głowna" "wyłączają" w niej pewne bloki.
A teraz pojawia się akcja OddajGłos ... Wyświetlana oczywiście w takim samym "środowskiu" . Wystarczy więc dziedziczyć z sondy, dodając tylko odpowiednie mechanizmy.

Oczywiśćie konieczna jest bardzo duża ostrożność, by nie zgbubić się w tych związkach, ale w wielu przypadkach może to znacznie ułatwić życie.
anas
@DeyV:
Tak tez to rozwiazuje - wlasnie sie tym zajalem i all dziala jak nalezy - jak dla mnie najbardziej przejrzysty i sprawdzony sposob smile.gif

Thx za tyle ciekawych propozycji... ide dalej meczyc klawiature.

pozdrowka

anas
aleksander
DeyV: możesz to pokazać na jakimś prostym przykładzie bo próbuję, ale nie mogę skumać smile.gif Tzn mniej więcej kumam ale jakoś nie mogę wpaść jak to elegancko rozwiązać - same badziewiacke rozwiązania przychodzą na myśl... za dużo tej marychy... smile.gif
splatch
Pozwolę sobie przedstawić moje rozwiązanie.
Moja koncepcja była bardzo prosta, mianowicie są elementy strony, które mogą się zmieniać (np. pojawiające się menu dostępne tylko dla zalogowanych osób ). Jako, że korzystam jak narazie tyko ze zwykłego widoku html obsługiwanego przez smarty dodałem tablicę zawierającą pliki do wywołania pod koniec tworzenia strony.
Przykładowy blok:
  1. <?php
  2. class LoginForm
  3. {
  4. function LoginForm( ) { }
  5. function run( ) {
  6. $reg = &Registry::instance( );
  7. $view = &$reg->load( 'view' );
  8. $io = &IO::instance( );
  9. if( $io->getSession( 'user_id' ) > 0 ) { //zalogowany
  10. $model = Init::model( 'user_model' );
  11. $data = $model->getUserById( $io->getSession( 'user_id' ) );
  12. $view->assign( 'username', $data['login'] );
  13. $view->assign( 'login_form', $view->fetch( 'logined.tpl' ) );
  14. } else { //gość
  15. $view->assign( 'login_form', $view->fetch( 'login_form.tpl' ) );
  16. }
  17.  
  18. }
  19.  
  20. }
  21.  
  22. ?>

Oczywiście można łatwo dodać kolejne elementy do szablonu i jeśli użytkownik jesst zalogowany wyświetlać.
Klasa widoku:
  1. <?php
  2. class SmartyView {
  3. var $smarty;
  4. var $autoLoadBlocks = array( );
  5.  
  6. function SmartyView( ) {
  7. $reg = &Registry::instance( );
  8. $config = $reg->load( 'viewconfig' ); // ładuję ustawienia z rejestru
  9.  
  10. $this->autoLoadBlocks = $config['autoload']; // domyślne bloki
  11. unset( $config['name'], $config['autoload'] );
  12.  
  13. include_once DOCUMENT_ROOT .'libs/smarty/Smarty.class.php';
  14. $this->smarty = new Smarty; //tworze obiekt
  15.  
  16. foreach( $config as $key => $value ) { // ustawienia konfiguracyjne
  17. $this->smarty->$key = $value;
  18. }
  19. $reg->save( 'view', $this ); //zapisuje widok w rejestrze
  20. }
  21.  
  22. function autoLoad( ) {
  23. foreach( $this->autoLoadBlocks as $id => $name ){
  24. $block = Init::block( $name );
  25. if( $action !== false ){
  26. $block->run( );
  27. }
  28. }
  29. }
  30.  
  31. #inne metody
  32.  
  33. function assign( $key, $value ) {
  34. $this->smarty->assign( $key, $value );
  35. }
  36.  
  37. function fetch( $tpl ) {
  38. return $this->smarty->fetch( $tpl );
  39. }
  40.  
  41. function display( $tpl = null ) {
  42. if( $tpl === null ) {
  43. $this->autoLoad( );
  44. $this->smarty->display( 'home.tpl' );
  45. } else {
  46. if( sizeof( $this->autoLoadBlocks ) > 0 ) { // są jakieś bloki
  47. $this->autoLoad( );
  48. }
  49. $this->smarty->display( $tpl );
  50. }
  51. }
  52. }
  53.  
  54. ?>

Wycinek z pliku config.php
  1. <?php
  2. $viewconfig['smarty']['name'] = &#092;"smarty\";
  3. $viewconfig['smarty']['template_dir'] = &#092;"templates/\";
  4. $viewconfig['smarty']['compile_dir'] = &#092;"compiled/\";
  5. $viewconfig['smarty']['config_dir'] = &#092;"tpl/config\";
  6. $viewconfig['smarty']['debugging'] = false;
  7. $viewconfig['smarty']['compile_check'] = true;
  8.  
  9. //bloki ktore beda automatycznie ladowane
  10. $viewconfig['smarty']['autoload'] = 
  11. 'Menu', 'News', 'Hello', 'auction_box', 'login_form'
  12. );
  13. ?>

W zależności od potrzeb akcja może usuwać lub dodawać nowe elementy blokowe. Jeśli serwis jest pod sporym obciążeniem to wtedy można włączyć buforowanie. Myślę, że takie rozwiązanie jest bardzo elastyczne i umożliwia tworzenie nawet bardzo złożonych bloków.
aleksander
buuuu ;( glowie sie i glowie i nie wiem jak wyglowic.
chce, zeby ustawienia jaki akcje uruchamiac przed i po właściwej akcja byly zapisane w konfiguracji frameworka (czyli plik xml). Chcialbym, zeby to bylo w miare elastycznie, wymyslilem takie sposoby:
  1. <action type="alwaysPre" showId="1"> <!-- showId ustala kolejnosc wyswietlania. wszystko musi byc elastyczne! -->
  2.      <name>main</name>
  3.      <requiredRoles></requireRoles>
  4.      <type>view</type>
  5.      <matches>main</matches>
  6.    </action>
  7.  
  8. <action>
  9.      <name>main</name>
  10.      <requiredRoles></requireRoles>
  11.      <type>view</type>
  12.      <matches>_alwaysPre1</matches> <!-- cyferka oznalcza kolejnosc wyswietlania -->
  13.    </action>
  14.  
  15. <action>
  16.      <name>main</name>
  17.      <requiredRoles></requireRoles>
  18.      <type>view</type>
  19.      <always where="pre" showId="1" />
  20.    </action>
Jednak te wszystkie pomysły wydają się jakieś nie tego teges smile.gif Any suggestions?
bela
może wzorzec Intercepting Filter biggrin.gif
aleksander
taa juz bo mi to cos mowi biggrin.gif

swoja droga przed chwila zdalem sobie sprawe ze popelniam blad. W koncu za wyglad odpowiada warstwa widoku, a ja probuje to rozwiazac w warstwie kontrolera.

Ok zrobilem cos takiego:
  1. <?php
  2. abstract class StronaGlowna
  3. {
  4. protected function runPreActions()
  5. {
  6. //tutaj odpalamy akcje, ktore powinny byc wyswietlone przed wlasciwa akcja (np header, menu, sonda)
  7. }
  8.  
  9. abstract protected function Action(); // akzja bazowa czyli to co wywolujemy
  10.  
  11. protected function runPostActions()
  12. {
  13. // tutaj odpalamy akcje, ktore powinny byc wyswietlone po wlasciwej akcji (typu stopki albo menu z prawej)
  14. }
  15.  
  16. public function Run() // metoda wywolywana przez kontroler
  17. {
  18. $this->runPreActions();
  19. $this->Action();
  20. $this->runPostActions();
  21. }
  22. }
  23.  
  24. class someView extends StronaGlowna implements Action
  25. {
  26. protected function Action()
  27. {
  28. // tutaj zapisujemy wlasciwa akcje
  29. }
  30. }
  31.  
  32. // dorzucam jeszcze interfejs Action (b. prosty)
  33. interface Action
  34. {
  35. public function Run();
  36. }
  37.  
  38. // akcje wywolujemy poprzez
  39. $objAction = new someView();
  40. $objAction->Run();
  41. ?>
DeyVowi chyba o to chodzilo nie?
hawk
Uhm, IMHO nie. DeyV mówił o dziedziczeniu z akcji bazowych, które zawierają kod wyświetlający np. menu. Nie o dziedziczeniu z akcji, które wywołują inne akcje.

Chociaż to drugie też jest możliwe i sensowne. Trzeba jakoś zapanować nad tym, które akcje wymagają wywołania przed/po jakiś innych akcji. To można zrealizować właśnie przez dziedziczenie ze wspólnej akcji bazowej, która zawiera implementację runPreActions i runPostActions.

Oczywiście, to wszystko ma sens przy założeniu, że widok jest typem akcji, i że możemy wywoływać kilka akcji jedna po drugiej. A obie te rzeczy nie są standardowe i nie każdy framework musi to umożliwiać. No ale jak widać są przydatne... tylko w tym momencie to co nazywamy "akcją" staje się coraz mniej akcją w sensie MVC, a raczej, hmm, jednostką kodu/pracy. No problem, tylko można się zakręcić w dyskusji z ortodoksyjnymi znawcami MVC.

PS Ja miałem kiedyś inny sposób, chyba lepszy niż dziedziczenie, bo elastyczny, ale nie pasujący do starej wersji phienda:
W konfiguracji akcji przydałby się dodatkowa informacja: "layout". Taki layout to byłaby klasa, która wyświetlałaby wszystkie stałe elementy, a w środku wynik działania akcji-widoku.
  1. <?php
  2. class View {
  3. public function run() {
  4. //pobierz layout z konfiguracji
  5. $layout->run($this);
  6. }
  7.  
  8. public function display() {
  9. //tutaj wyświetlamy zawartość
  10. }
  11. }
  12.  
  13. class Layout {
  14. public function run($view) {
  15. //wyświetl header
  16. $view->display();
  17. //wyświetl footer
  18. }
  19. }?>

Layouty można zmieniać, można dziedziczyć, itd.
aleksander
no jasne, na pewno te sztuczki z MVC zgodne nie są, tyle że moim TODO jest zrobienie frameworka, który będzie podwalinami do dowolnego portalu.

BTW w tym co mowil DeyV trzeba zauwarzyć, że to menu w akcji bazowej jest po prostu kodem html a u mnie to miejsce ma zająć kolejna akcja.
DeyV
Z tym ostatnim stwierdzeniem nie mogę się zgodzić.
To "menu" to jakiś dowolnie generowany fragment kodu strony.
W takiej sytuacji potrzebny jest jakiś kod, który pozwoli na pobranie tych danych i przesłanie ich do szablonu.
I tu pojawia się konieczność wydzielenia samodzielnego działu w kodzie - zestawu klas o podobnych możliwościach jak akcje (np. korzystanie z modelu) jednak nie odpalane przez router, a przez zainteresowane nimi konkretne akcje.
Pozwala to na uproszczenie kodu tej akcji bazowej, a zarazem na prostą, modularna obsługe pozostałych elementów.
Jarod
Cytat(anas @ 29.12.2004, 16:40:07 ) *
@DeyV:
Tak tez to rozwiazuje - wlasnie sie tym zajalem i all dziala jak nalezy - jak dla mnie najbardziej przejrzysty i sprawdzony sposob smile.gif


Możecie wyjaśnić o co chodzi z tą akcją bazową?

Czy to coś w tylu: tworzę sobie akcje BaseAction, która np. zawiera metodę compose(). Metoda compose() odpowiedzialna jest za wygenerowanie nagłówka, menu i stopki. A pozostałe akcje generujące zawartość contentu (środka) dziedziczą po BaseAction?

Jeśli tak to jak rozwiązać problem kolejności ładowania poszczególnych sekcji: nagłówek, menu, stopka? A co jeśli zawartość menu zależy od uprawnień które posiada zalogowany użytkownik? Nie mogę sobie jakoś tego wyobrazić? Może jakiś kodzik?
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.