Będe tu zamieszczał pytania odnoscie działania FW, oraz róznych kruczków
Pierwsze z nich to pobieranie aktualnej akcji i modułu. Moge to zrobić pobierając z adresu w Kontrolerze module oraz action. Jesli wartosci sa puste podstawiamy domyslne. Bułka z masłem. Jednak co jesli rozszerzymy funkcjonalnosc o znaną funkcje forward()? Przekierowywuje ona do innego modułu i/lub akcji bez zmiany parametrów w adresie. I co teraz? W adresie mamy dane dotyczącego poprzedniej akcji, wiec pobieranie z adresu nie ma sensu. W tym wypadku odpada pobieranie z adresu. Jak to najlepiej rozwiązać? Jak wy to widzicie
EDIT: Zmieniłem nazwe tematu, na bardziej adekwatny
W moim frameworku mam to tak rozwiązane, że każdy widok/akcja może być odpalony z parametrami. ..zatem dla przykladu w akcji, ktora utworzyla nowy news:
<?php function Execute() { [...] // kod wewnatrz akcji // $rek['ID'] - id ostatniego newsa $this->setNext('NewsPlus', 'NewsPlusShow', http://www.php.net/array('newsPlusShow'=>$rek['ID'], 'saved'=>true)); return true; } ?>
<?php function Display() { $idNews = $this->getParam('newsPlusShow', http://www.php.net/array('get','action'), 'Integer'); // pobieramy ID $saved = $this->getParam('saved', 'action', 'Integer'); // dzieki temu moge potem to .tpl przekazac informacje, ze trzeba wyswietlic komun
ikat o nowoutworzonym news } ?>
A co gdyby przenieść sterowanie z kodu do... pliku XML? Spotkałem przykład w książce i zaimportowałem sobie ideę.
Wg. mnie łańcuchy akcji to jedna z bardziej skomplikowanych rzeczy w FW. Przywykłem już patrzeć na akcje jako na uczestników pewnego bardzo rozproszonego systemu.
Moja definicja akcji.
Akcja to podstawowa jednostka w systemie odpowiadająca za wykonanie określonego zadania (model) przy użyciu dostępnych jej bibliotek i funkcji. Wyrózniam dwa podstawowe typy akcji (chyba nie tylko ja):
- prosta
- złożona (kompozyt)
Akcja złożona może składać się z innych akcji wywoływanych w łańcuchu i korzystać z wyników pracy tych akcji. Jej ciało może zostać wykonane na początku łańcucha, na jego końcu (częstszy przypadek) lub po dowolnej z subakcji. Subakcje są wywoływane w pętli przez kontroler i powinny być tak poskładane, żeby w żaden sposób nie mogły doprowadzić systemu do upadku.
Akcja może też przekazać działanie do innej akcji (forward), ale nie sama, a za pośrednictwem kontrolera. Sama ustawia jedynie swój status, który odczytany przez kontroler umożliwia uruchomienie (np. przez header()) kolejnej akcji. Akcja wywołana funkcją forward nie jest częścią łańcucha akcji i stanowi niejako jego przerwanie.
Celem istnienia akcji złożonych jest ponowne wykorzystanie istniejącego kodu poprzez użycie go w niezmienionej formie w innym kontekście. Uff : P
Sorry, że jest tak mądrze napisane, ale może komuś się przyda. Wg. mnie, właśnie to jest esencja wszystkich frameworków - organizacja pracy z kodem i styl programowania. Jako, że aktualnie piszę takie cóś (idzie jak krew z nosa), pozwoliłem sobie na podsumowanie dotychczasowych przemyśleń.
Pozdrawiam.
@Cysiaczek: Zgadzam sie z Toba, ale chyba nie do konca. Nie wiem czy dobrze zrozumiałem twoją definicje forward() Wydaje mi sie ze pomyliłeś z redirect() (głownie przez tenheader()), aczkolwiek moge sie mylić A wracając do mojego pierwszego rozwiązania. Wyznaje zasade uproszczenia sobie działań, nawet jesli nie wygląda to profesjonalnie, ważne ze skutecznie i prosto. A o to w koncu w programowaniu chodzi. Do pobierania służą funkcje wbudowane w kontroler. W najczestrzym wypadku tak jak opisałem pobierają one dane na podstawie Routera. Jesli jednak uzyjemy funkcji forward() to w jej ciele ustawiamy recznie akcje. Moze przyklad to lepiej opisze niz moje słowa
<?php public http://www.php.net/static function getModule( $sModule = null ) { if( $sModule == null ) { $aUrl = Router::decodeUrl(); if( !http://www.php.net/isset( $aUrl['module'] ) ) { return _DEF_MODULE; } else { return $aUrl['module']; } } else { return $sModule; } } ?>
<?php public function forward( $sAction, $sModule = null ) { Controller::getAction( $sAction ); Controller::getModule( $sModule ); } ?>
Przez forward rozumiem uruchomienie innego łańcucha akcji. Ok. za mnie niech przemówi XML : )
<index> <status> <ok> <view>index</view> </ok> <error> <forward>error</forward> </error> </status> <subactions> <news /> <menu /> </subactions> </index> <news> <status> <ok> <view>news</view> </ok> <error> <view>error</view> </error> </status> </news> <menu> <status> <ok> <view>listmenu</view> </ok> <error> <view>error</view> </error> </status> </menu> <error> <status> <ok> <view>ErrorPage</view> </ok> <error> <exit>Blad krytyczny</exit> </error> </status> <subactions> <logError /> <sendNotify /> </subactions> </error>
<?php class Index extends Action { public function Perform(){ $test=1; // tak poglądowo $this->aVars['tekst']="jakiś tekst"; $this->aVars['lista']=$array; if ($test==1){ $this->setStatus("ok"); } else { $this->setStatus("error"); } } } ?>
Kod który przedstawiłem dla funkcjiforward jest szczątkowy. Chodzi tylko o ukazanie głownego problemu jakim jest "dobieranie" sie do informacji jaki jest aktualny moduł i akcja Forward własciwie jest tylko funkcją dostępową do zmiennych prywatnych Ustawia on odpowiednie zmienne w klasie i metoda executeAction wywołuje akcje i nastepnie widok dla tej akcji. Całośc znajduje sie w klasie AppController. Tak więc mamy: Controller -> AppController->View && Action. Tak to wygląda w uproszczeniu.
Ciekawie prezentuje sie to drzewo xml. Mogłbyś napisać coś wiecej
Zastanawia mnie jeszcze jedno pytanie. Jak najskuteczniej wywołać kilka akcji wraz z widokami w jednej akcji? Troche namieszałem. Dajmy przyklad:
Najspierw wywołujemy akcje Index() w module Default. W tej akcji wywołujemy 2 nowe akcje. Czyli wywołując jedną akcje otrzymujemy ich aż 3. Kiedyś sie nad tym męczyłem jak tworzyłem CMSa, ale nic ciekawego nie wymysliłem. Teraz po przejsciu na OOP chce to już rozwiązać. Przydatne jest to w sytuacji kiedy mamy moduł newsy oraz moduł komentarze. Wywołujemy akcje showNews i w tejże akcji wywołujemy akcje ShowComments z Modułu komentarze. Oczywiscie mozna przekazywać parametry do akcji ShowComments z wnetrza ShowNews takie jak id komentarza itp. Na przeszkodzie stoi mi tylko połączenie wyświetlania szablonów. Bo jest to zrobione, że mamy głowny szablon strony z całym szkieletem w którym jest $this->LoadView(). LoadView załaduje nam podszablon dla danej akcji i modułu. Wszystko działa bardzo ładnie dla pojedynczego wywołania akcji bądź dla forward. Natomiast nie wiem jak to rozwiązać dla większej ilości.
<?php class DefaultAction extends View { public function Index() { $this->newModel( 'Index', 'Test'); $this->newModel( 'Info', 'Modul'); } } ?>
<?php $o = new Obiekt1 $o->funkcja(); $o = new Obiekt2; //Zeby Obiekt2 "dołączył" sie do Obiekt1 $o->Innafunkcja(); ?>
Najlepszy moim zdaniem (ale do średnich i dużych) projektów jest wzorzec http://www.martinfowler.com/eaaCatalog/twoStepView.html, który zakłada najpierw rendering mniejszych widoków, a potem ich połączenie w całość.
Jeśli spojrzysz na XML, który dałem wyżej, to zobaczysz znaczniki <view>nazwa</view>. To są właśnie widoki, które będą renderowane. W tym drzewie pominąłem część znaczników, więc nie ma np, znacznika <viewFrame>nazwa</viewFrame>, który jeśli akcja jest wywoływana żądaniem (nie jest subakcją), stanowi schemat łączenia poszczególnych mniejszych widoków. W prezentowanym przykładzie, jeśli wszystko pójdzie dobrze, to akcja Index będzie miała do dyspozycji aż 3 widoki
news.tpl
menulist.tpl
index.tpl
oraz schemat łaczenia index.frame.tpl
news.tpl (pseudo-kod)
<? http://www.php.net/print $news['title']; ?> <br> <? http://www.php.net/print $news['body']; ?>
<html> <head></head> <body> <div> <? AHelper->get("menu"); ?> </div> <div> <? AHelper->get("news"); ?> </div> </body> </html>
Nie wiem czy sie rozumiemy do konca.
Widok jest podzielony na szablon głowny:
oraz Szablon akcji. Metoda loadModuleView() to poprostu include('szablonAkcji.php); I to nad czym sie aktualnie męcze to jak zrobić aby przy wywołaniu Akcji ShowNews jednoczesnie wywołać ShowComments, bez ingerencji w głowny szablon. Kawałek kodu z AppController:
Witamy na stronie Sparkle Works :)<hr><br> <?php $this->loadModuleView(); ?> <br><br><br><br><br><br> <hr> Powered by Sparkle
Nie przejmujcie sie bałaganem, gdyż to tylko testy I wszystko jest prawie pwisane na sztywno. Plik Action wyglada tak:
<?php public function executeAction( $sAction, $sModule, $a = '' ) { //echo 'Action'. $sModule; $sTempModule = $sModule.'Action'; $this->oModule = new $sTempModule; $this->oModule->$sAction(); $t = $this->s($this->oModule, $sAction, $sModule); if(http://www.php.net/is_array(self::$om)) { include(_SPARKLE_PATH.'/app/'._SPARKLE_APP.'/modules/'.self::$om['mod'].'/action/Actions.class.php'); $this->oModules = new TestAction; $this->oModules->Index(); } $this->oModule->loadAppView($this->oModule); //echo self::$om; } public function newModel( $sAction, $sModule = null ) { include(_SPARKLE_PATH.'/app/'._SPARKLE_APP.'/modules/Test/action/Actions.class.php'); $o = new TestAction; $o->Index(); //$this->s($o, $sAction, $sModule); } ?>
I to własnie on jest wywołany w przegladarce. Wenatrz akcji index wywołujemy moduł Test, z akcją Index. To jakoś działa. Tylko niestety wszystko sie pipeprzy w
<?php class DefaultAction extends View { public function Index() { $this->d = 'sssssdszzzzzzzzzzzzzzz<br>'; //$this->test = 'duuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuupa'; //echo '<br>ddddddddddddddddddddddddddddddddddddd'; $this->newModel( 'Index', 'Test'); } } ?>
Przez takie wywołanie gubie referencje w szablonie do Obiektu $this->oModules. I wszelkie zmienne zadeklarowane w Module test w akcji index, nie zostaną wyswietlone w szablonie. Jak temu zaradzić?
<?php $this->oModule->loadAppView($this->oModule); ?>
Wprowadź obiekt kolekcji uruchamianych modułów. Przy próbie inicjalizacji nowego modułu, referencję wywołującego umieść najpierw w kolekcji, a dopiero potem wywołaj nowy.
<?php $collection->registerModule($this); $this->newModel( 'Index', 'Test'); ?>
<?php $module=$collection->getModule($moduleName); // albo konkretnie $this->oModule->loadAppView($collection->getModule($this->oModule)); ?>
Nie za bardzo rozumiem. Obiekt kolekcji? Co to
Obiekt, który coś przechowuje i pozwala na przeglądanie tego oraz zarządzanie itp. W najprostszym przypadku może to być tablica, ale obiekt daje więcej możliwości.
Pozdrawiam.
Czyli na polskie tłumacząc napisz sobie rejestr ;]
Co do widoków używam ten wzorzec co podał Cysiaczek proste i wygodne ;] .
Ufff....
Już sie uporałem, bez rejestru Troche pokombinowałem z referencjami do obiektów i działa
Narazie to tylko światełko w tunelu, bo większość rzeczy wpisałem na sztywno, ale to już poprawki kosmetyczne Aby nie myliły sie zmienne w szablonach, tzn aby nie została wykorzystana przez przypadek zmienna z akcji pierwszej w szablonie drugim zrobiłem małe myku Tzn. Zamiast w szablonach odwolywac sie do zmiennych przez
robimy tak:
<?php $this->zmienna ?>
W ten sposób mamy wszystko odzzielone w szablonach. Natomiast w akcjach uzywamy normalnie $this->zmienna Jednak mój sukces nie kończy dyskusji
<?php $NazwaModulu->zmienna ?>
Tzn. tak ;]
Np. mam newsy i podpinam tam obiekt view. Renderuje sobie do zmiennej po kolei każdego. Podaje je szablonowi newses, który je listuje i znowu do zmiennej. Potem ta zmienna dopiero wędruje do szablonu index i tam jest wyświetlana razem z nim.
Czyli zawartość szablonu news wczytujesz do zmiennej zamiast includowac szablon? Zgadza sie?
Tak ;] To jest wygodne i szybkie Tak jest np. w CI
Ja z kolei includuję : )
menic: Bez różnicy ;] Sprawa gustu przedewszystkim
W praktyce, podejrzewam jest niewielka róznica jesli chodzi o wydajność. Ale jeśli chodzi o wygode to jak najbardziej. Gdybym u siebie tak zrobił to nie męczyłbym sie tyle z ładowaniem akcji itp. Ale no wlasnie... Kwestia gustu
A co do moich akcji i widoków to już chyba skonczyłem skończyłem Właściwie zrobiony jest już cały Controller, AppController, oraz View. Sporo sie męczyłem z forward'em i załadowaniem akcji w akcji, ale w koncu sie udało. Było warto poświęcic tyle czasu. Mam tylko nadzieje, że nie odbije sie to negatywnie na zaliczeniach, które mam w tym tygodniu
Witam, zaprezentuje moje rozwiązanie tego problemu
posiadam plik konfiguracyjny bloków np:
<block> <default> <layout>index.tpl<layout> <blocks> <main controller="index" action="index" /> <info controller="index" action="info" /> </blocks> </default> <login> <layout>login.tpl<layout> <blocks> <main controller="login" action="index" /> </blocks> </login> </block>
<alias> <login controller="logowanie" action="main" block="login"/> <alias>
<?php class indexAction extends AController { public function index(){ $view = $this->getView(); $this->setBlock('main', $view->parse('index/main.tpl'); } public function info(){ $view = $this->getView(); $view->infoMSG = 'jakaś informacja'; $this->setBlock('info', $view->parse('index/info.tpl'); } } class logowanieAction extends AController { public function main(){ $view = $this->getView(); $this->setBlock('main', $view->parse('logowanie/main.tpl'); } } ?>
<?php http://www.php.net/print $this->block->main?>
<?php http://www.php.net/print $this->block->run('index', 'main');?>
Szczerze? Nie rozumiem o czym piszesz Co masz pod pojęciem bloki. Bo dla mnie to kawałek kodu php i html umieszczane w glownym layoucie strony, raczej nie mający powiązań z akcjami.
u mnie bloki są wynikiem akcji ..
Po sparsowaniu xml'a (z poprzedniego postu) mam tablice, która zawiera nazwy bloków z kontrolerem i akcją do odpalenia. Wrzucam to do ActionChain i ruszam z tym w FrontControllerze razem z wynikiem rutera.
Każda z odpalonych akcji umieszcza swój wynik w odpowiednim bloku poprzez
<?php $this->setBlock('mojBlok', 'trescBloku'); ?>
<?php http://www.php.net/print $this->block->mojBlock; ?>
Smieszne masz nazewnictwo wszystkiego. U mnie sa tylko dwa kontrolery: Controller oraz AppController. Ten pierwszy znany jest jako FrontController. Dalej jest moduł i akcja z widokiem. U ciebie kazdy moj moduł to jest nowy kontroler :roll2: Ale wracajac do tematu...
Ja nie musze nic edytować, dopisywac do szablonu. Nic z tych rzeczy. Kazdy kazda akcja modułu ma swoj tpl które są ładowane do głownego tpl
Kazdy wynik akcji bedzie w loadModuleView().
<h1>Witamy na stronie Sparkle Works :)</h1><hr><br> <pre> </pre> <?php $this->loadModuleView(); ?> <br><br><br><br><br><br> <hr> Powered by Sparkle
Natomiast jesli forward() damy na koncu to zostaną wykonane wczesniejsze załadowane akcje, ale bez ładowania widoków Tak wiec mozliwosci kombinowania jest dużo I jak zechce to moge odpalic nawet kilka razy ta samą akcje (tylko nie wiem po )
<?php class DefaultAction extends View { public function Index() { //$this->redirect( 'Show', 'News', Array( 'type' => 'get', 'id' => 77 ) ); //echo 'forward<br>'; $this->forward('Show', 'Default'); //Przekieruje do modułu Default do akcji Show nie wyświetlając wyniku aktualnej akcji $this->newModel( 'Show', 'News' ); //Załaduje akcje Show z modułu News wraz z widokiem $this->newModel( 'Show', 'Test', http://www.php.net/array('id' => 77, 'type' => 'get') ); //Załaduje akcje Show z modułu test wraz z parametrami oraz widokiem } } ?>
( takie rozwiązanie jest chyba w symfony .. nie jestem 100% pewien .. )
<?php $wynik = $this->runAction('controller', 'action', 'params'); ?>
<?php $view->set('main', $wynik); ?>
<?php http://www.php.net/print $main; ?>
Przyglądam się Waszej i snuje wnioski i pomysły . Wnioski takie mam, że albo lubicie sobie komplikować sprawę albo po prostu 'tak Wam wygodnie'. Oczywiście każdy ma to co lubi i pisze FW dla siebie i pod siebie. Z resztą sam też się w to bawię i więcej mam przyjemności z budowy FW niż z samego korzystania z niego .
Co do wywoływań akcji to akurat ja sobie to zupełnie inaczej obmyśliłem. Wydaje mi się że sporo prościej ale po kolei wytłumaczę. Generalnie jest sobie kontroler. Ma on funkcję statyczną runEvent() z parametrami : jaką klasę i metodę wywołać oraz jakie parametry przesłać do niej. Zwraca to co dana metoda wypluje. Oczywiście sam kontroler ma w sobie mechanizmy sprawdzające czy się wywołania nie zapętlą itp. Ma również obiekty odpowiadające za cache'owanie obiektów akcji (czy jak ja to nazywam 'eventów'), za dane sesji no i za widok.
Metodę runEvent można wywołać z każdego miejsca FW tak więc odpada nam konieczność robienia forward() i martwienia się o to czy można i jak. Dodatkowo daje nam możliwość kontrolowania kazdej akcji z poziomu kontrolera (chyba zamieszałem:) ). Każda klasa obsługująca zdarzenia musi dziedziczyć po bazowej klasie do zdarzeń. Dzięki temu każda z akcji ma możliwość dodania zmiennych wyświetlanych w widoku czy modyfikacji zmiennych sesyjnych. Dopiero później kontroler przekazuje do widoku sterowanie z zebranymi danymi. Jedyne co akcja musi zrobić to zdefiniować nazwę szablony do wyświetlenia i tylko ta akcja która została wywołana z router'a. Dodatkowo mam możliwość zdefiniowania czy daną akcję mogę wywołać przez URL'e czy tylko i wyłącznie przez metodę runEvent.
Jedyne co mnie natchnęliście to muszę się zastanowić czy chcę aby klasy obsługujące zdarzenia miały możliwość definiowania własnych części widoku a nie tylko danych do wyświetlenia.
View dziediczy po AppControlerze, stąd dostepnośc forward. W Klasie View trzymane są metody jak nazwa wskazuje do widoku, takie jak loadModuleView(), loadAppView(), setView() itp. Narazie jeszcze jest nie rozubudowana, gdyz meczyłem sie z kontrolerami
CZy to komplikowanie? W pewnym sensie tak, ale zalezy jak na to pparztec Pisze o głownie dla pożniejszej wygody. Wkurzyło mnie jak pisałem w Symfony, ze w module newsów musze pisac tez moduł komentarzy (wiec po co cała modułowosc?)
U mnie Controller odpowiada tylko za załadowanie odpowiednich plikow i tyle. Reszte przejmuje appController z funkcjiami do ładowania akcji itp. Metoda NewModel() to jest tak jak by nakladka na metode executeAction() z appControllera. Musiałem rozdzielic, bo raczej rzadko sie uzywa newModel wiec bez sensu byłoby to pakowanie do głownej metody wywoławczej
A działanie forward jest takie, ze wykonuje wszystko w danej akcji, lecz nie wyswietla jej wyniku tylko przenosi do innej akcji i tam juz wyswietla wynik tej przeniesionej
@menic - i już wszystko jasne ( ciekawe podejście ) ..
Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)