Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [php/MVC] Lancuszek akcji, akcja w akcji.
Forum PHP.pl > Forum > PHP > Object-oriented programming
c3zi
Witam,

Jak rozwiazujecie uruchamianie akcji w implementacji innej akcji?

Dodam, iz u mnie kazda akcja posiada swoj widok. Zatem w akcji1, w ktorej musze uruchomic inna akcje2 musialbym jakos przypisac do zmiennej - widok akcji2, a potem przeslac go w zmiennej akcji1 (juz plus akcja2) to widoku akcji1 ? Nie wiem jak do tego podejsc.

Chodzi o to, ze mam dynamicznie tworzona liste danych w akcji2, a akcja1 tez chce z nich korzystac +dodac odp. swoje funkcjonalnosci.

Schemat:
Akcja_showItems->Uruchom akcje main->akcja main wykonuje swoj kod->uruchom szablon akcji_main->przekaz go w zmiennej do akcj showItems->uruchom szablon akcji_showItems przekazuja do niego rowniez dane z akcji main..

Jak do tego podejsc ?
Cysiaczek
Powiem Ci coś... To jest bardziej skomplikowane niż przypuszczasz.
Takie luźne wskazówki i obserwacje.

Powinieneś stworzyć pojęcie (obiekt) łańcucha akcji. Każdy jego element musi być opisany jakimiś parametrami umożliwiającymi jego zidentyfikowanie i ustalenie relacji do innych akcji.
Przekazywanie danych pomiędzy akcjami w łańcuchu wytwarza ścisłe powiązania pomiędzy nimi.
Łańcuch akcji nie powinien mieć nic do czynienia z warstwą prezentacji.
Zrezygnuj z przekazywania danych pomiędzy akcjami - pozwól im pobierac dane ze wspólnego miejsca (np. rejestr) lub z innej akcji w sposób jawny.

Teraz odrobinkę o moim rozwiązaniu,
Każda akcja posiada definicję w formacie XML, która może zawierać listę subakcji, czyli akcji wykonywanych przed lub po niej samej. Jeśli te inne akcje również zawierają subakcje, to są one w procesie parsowania doklejane do łańcucha. Gdy łańcuch jest gotowy,następuje jego wykonanie w pętli. Po każdym wywołaniu akcji może nastąpić modyfikacja łańcucha, jeśli akcja sobie tego zażyczyła. Łańcuch może zostac przerwany, zastąpiony innym, lub też można do niego dokleić inny łańcuch.
W teorii można tworzyć niemal niograniczone struktury, ale w praktyce łatwo jest "wyłożyć" aplikację.

Może Ci się do czegoś przyda to co napisałem : )
Pozdrawiam.
c3zi
Witam,

Cysiaczek:


Niewatpliwie pomogles, aczkolwiek mam jeszcze pare pytan.

1. Jak w takim razi przekazujesz dane do widoku, jesli potrzebuje dane z 2 akcji. Korzystasz wtedy z rejestru i tam przechowujesz wszystkie dane?

2. Masz osobna klase do lanuszkow akcji?

3. Zatem ogolnie polega to u Ciebie na sparsowaniu pliku xml i uruchomieniu kolejnej akcji. Istotne jest dla mnie w jakis sposob przekazujesz te dane do widokow, z roznych akcji ( powtarzam sie [-; ).


Pozdrawiam.
Cysiaczek
Każdy obiekt akcji zawiera obiekt typu actionDataObject.
  1. <?php
  2. $this->data->tekst='Hello World!'; // tak dodajesz dane
  3.  
  4. //W innej akcji...
  5. // pomocnik  akcje konkretna akcja obiekt danych
  6. $dane=$this->AHelper->getActions()->getAction('name', 'module')->getDataObject(); //pobierasz ten obiekt
  7. $this->data->dane=$dane->tekst;
  8. ?>


Przekazane do widoku, to przekazanie obiektu actionDataObject do obiektu odpowiedzialnego za warstwę prezentacji

W praktyce:
  1. <?php
  2.  
  3. /**
  4.  * Pobiera wskazany parametrem ID rekord i przechowuje obiekt News
  5.  */
  6. class news_get extends Action
  7. {
  8.  
  9. public function Perform()
  10. {
  11. $req=$this->AHelper->getReqRegistry();
  12. $id=(integer)$req->get('id');
  13. $db=DB::getInstance();
  14. $db->setQuery("SELECT * FROM news WHERE id='$id'");
  15. $result=$db->fetchAll();
  16. if(!count($result)>0)
  17. {
  18. $this->setStatus('ERROR');
  19. return;
  20. }
  21. $this->data->news=new News($result[0]);
  22.  
  23. }
  24. }
  25. ?>


  1. <?php
  2.  
  3. /**
  4.  * Operuje na pojedyńczym newsie - przekazuje go jako tablicę do widoku
  5.  * W razie niepowodzenia ustawia widok błędu
  6.  *
  7.  */
  8. class news_show extends Action
  9. {
  10.  
  11. public function Perform()
  12. {
  13. $get=$this->AHelper->getActions()->getAction('get', 'news')->getDataObject();
  14. if($get->has('news'))
  15. {
  16. $this->data->news=$get->news->getAsArray();
  17. return;
  18. }
  19. $this->setStatus('ERROR');
  20. }
  21. }
  22. ?>


Templatka
  1. <?php
  2. print $news['title'].'<br />';
  3. print $news['body'].'<br />';
  4. ?>


Pozdrawiam.


--edit
Wywołanie:
http://host.pl/news/show/id_32

Co do parsowania; łańcuszek akcji jest znany przed uruchominiem pierwszej akcji. Jedynie w trakcie żądania może się odrobinę zmienić - jedne akcje mogą wypaść, inne zostać dodane. Jest to więc proces dynamiczny.

--eidt kolejny : P
  1. <show>
  2. <slot>show</slot>
  3. <subactions>
  4.   <get module="news" />
  5. </subactions>
  6. </show>


akcja show musi dostarczyc widok główny (show.slot.tpl), który jest stroną www.
NoiseMc
Mi sie podoba jak to jest rozwiazane w ZF. W akcji wywolujesz sobie
  1. <?php
  2. $this->_forward($action, $controller);
  3. ?>

W nastepnej akcji mozesz sobie forwardowac do innej i tak sobie robic lancuchy. Co do widoku, ja w tej chwili mam jeden obiekt widoku w klasie bazowej kontrolera i jest dostepny dla wszystkich akcji ktore dziedzicza z BaseController.
menic
U mnie troche jest to inaczej. Myslalem nad zdefiniowaniem wszystkiego za pomocą xml'a jak to jest u Cysiaczka, ale odszedlem od tego pomysłu. Glownyu kontroler odpala akcje z adresu a co dzieje sie dalej to ustalamy juz w samej akcji. Oczywiscie pomijam tutaj sytuacje typu nieistniejaca akcja, czy brak dostepu. W takich przypadkach zostaje uruchomiona akcja "awaryjna" bez udziału własciwej akcji biggrin.gif (troche chyba namieszałem winksmiley.jpg ) Ale wrocmy do sedna. Tak wiec w trakcie wykonywania akcji mamy juz wiele mozliwosci. Mozemy dokonac przekierowania na inna akcje, lub np. uruchomic dodatkową, tak wiec przy jednym rzadaniu mozemy miec np. wykonanych 5 roznych akcji.
A co do widokow do tych akcji... To tez jest roznie winksmiley.jpg Ale podstawowa zasada, ze trzymam sobie "gdzieś" informacje do jakiej akcji przynalezy dany widok i pozniej w ładnej petelce sie wyswietla :]

BTW. Sorki za pismo ale pisze nie ze swojego kompa i jest tu do d... klawiatura :|
Cysiaczek
@NoiseMc - Można rzeczywiście w ciele akcji wywołać forward(), ale zauważ, jak mocna zależność się pojawia w kodzie.
Pomyśl, co się stanie, jeśli będziesz chciał użyć kodu tej akcji w innej aplikacji i przekierować na inna akcję. Musisz wówczas zmienić kod, a to spowoduje powstanie dwóch plików, bądz dwóch wersji tej samej metody.

Pozdrawiam.
NoiseMc
Mozna myslec tez w ten sposob, akcje jako komponenty wymieniane pomiedzy aplikacjami. Wtedy faktycznie lepiej miec plik konfiguracyjny ze zdefiniowanymi lancuchami akcji. Do tej pory jeszcze nie trafilem na taki problem dlatego _forward () na razie mi starczy.
c3zi
Cysiaczek:

W ktorym momencie definiujesz obiekt ActionDataObject? Nie widze jego definicji, za to widze, iz nie jest on statyczny, wiec nie wiem na jakiejs zasadzie odpowiednie akcjebeda wrzucaly do niego zmienne, obiekty.. ?

Bo wiadomo, jesli bylby static to tylko jeden egzemplarz mielibysmy, a ty jak to rozwiazales ?
Cysiaczek
Ten obiekt jest tworzony przy tworzenieu egzemplarza obiektu akcji - dokładniej w konstruktorze klasy bazowej (Action) i prztpisywany do składowej chronionej Action::$data
Fragment kodu:
  1. <?php
  2. /**
  3. * Inicjuje pewne pierdoły, które równiedobrze klasa sama może zrobić
  4. * Konstruktor jest finalny - dziedziczące nie mogę mieć konstruktora
  5. */
  6. public final function __construct()
  7. {
  8. $this->AHelper=application_Helper::getInstance();
  9. $this->data=new actionData();
  10. $this->name=substr(strstr(get_class($this), "_"), 1);
  11. $this->data->actionName=$this->name;
  12. }
  13. ?>


Każda akcja ma zatem własny obiekt danych, do którego wkłada dane, którymi chce się podzielić z widokiem, albo innymi akcjami. To takie publiczne zmienne akcji. Obiekt ten pobierasz tak jak Ci pokazałem wcznieśniej. Współny rejestr to już zupełnie inna sprawa (choć i jego zaimplementowałem).

W wersji minimalistycznej to np.
  1. <?php
  2.  
  3. /**
  4.  * Obiekt reprezentujący dane (zmienne) akcji
  5.  */
  6. class actionData{
  7.  
  8. /**
  9. * Tablic zmiennych
  10. * @var array
  11. */
  12. private $vars=array();
  13.  
  14. /**
  15. * Prawie jak u Harry'ego Pottera tongue.gif
  16. *
  17. * @param string
  18. * @param mixed
  19. * @return void
  20. */
  21. public function __set($key, $value)
  22. {
  23. $this->vars[$key]=$value;
  24. }
  25.  
  26. /**
  27. * Prawie jak u Harry'ego Pottera tongue.gif
  28. *
  29. * @param string
  30. * @return mixed
  31. */
  32. public function __get($key)
  33. {
  34. return $this->vars[$key];
  35. }
  36.  
  37. /**
  38. * Sprawdza czy kluicz istnieje
  39. *
  40. * @param string
  41. * @return bool
  42. */
  43. public function has($key)
  44. {
  45. if($this->vars[$key])
  46. {
  47. return true;
  48. }
  49. return false;
  50. }
  51.  
  52. /**
  53. * Zwraca refernencję tablicy danych
  54. *
  55. * @return &array
  56. */
  57. public function & getArray()
  58. {
  59. return $this->vars;
  60. }
  61.  
  62. /**
  63. * Zwraca tablicę danych
  64. *
  65. * @return array
  66. */
  67. public function getArrayCopy()
  68. {
  69. return $this->vars;
  70. }
  71. }
  72. ?>
c3zi
No tak, kazda klasa ma wlasny obiekt z danymi. Jak przekazywac te dane miedzy akcjami ? akcja2 nie widzie przeciez rejestru do ktorego sa wsadzane dane z akcji1 ? A moze myle sie? Probuje wlasnie takie cos napisac.. Jesli zaincluduje w akcji2 plik z akcja1 to oczywiscie bede mial w rejestrze dane z akcji1, jesli tego nie zrobie - nie widze ich.. Co robie nie tak ?

Teraz jeszcze raz przejrzalem wczesniejszego Twojego posta i widze, ze chyba za pomoca Helpera przekazujesz te dane ? Czy jestem w bledzie? Jaka role Helpery u Ciebie pelnia ? Narazie tyle [;
Cysiaczek
Helper dostarcza mi w tym przypadku interfejs dostępu do danych.
Posiada metodę getActions(), która zwraca obiekt kolekcji - zawierający obiekty wykonanych wcześniej akcji. Ten obiekt kolekcji ma z kolei metodę getAction($action. $module), która przyjmuje 2 argumenty - nazwę akcji i nazwę modułu do którego akcja należy. Metoda ta zwraca obiekt akcji, a jak napisałem w poście powyżej - obiekt akcji zawiera metodę zwracającą dane getDataObject()

To może być nieco męczące, więc można sobie stworzyć w helperze metodę, która to uprości. Np.

// klasa helpera
  1. <?php
  2. function getDataOf($action, $module)
  3. {
  4.  return $this->getActions()->getAction($action, $module)->getDataObeject();
  5. }
  6. ?>


Teraz w każdej akcji możesz pozyskać obiekt danych dowolnej innej akcji (takiej, która wcześniej została wykonana).
Specjalnie pogrubiam słowo pozyskać, bo nie przekazujesz tego obiektu, tylko prosisz inny obiekt o jego udostępnienie

  1. <?php
  2. class news_show extends Action
  3. {
  4.  
  5. public function Perform()
  6. {
  7. //$get=$this->AHelper->getActions()->getAction('get', 'news')->getDataObject();
  8. // te sposoby sa różnoważne
  9. $obiekt_danych_akcji_get=$this->AHelper->getDataOf('get', 'news'));
  10. }
  11. }
  12. ?>


Co do Twojej implementacji... nie wiem, co robisz źle, bo Ja jedynie pokazałem Ci luźny kawałek kodu - to jest część dużo większej aplikacji - mojego frameworka. Aby usiągnąć zadowalający efekt, trzeba napisać masę kodu, ktry będzie tym zarządzał i kontrolował rozwój wypadków. Moja główna klasa do obsługi łańcucha akcji ma 800 linijek kodu (z komentarzami) i ciągle ją poprawiam. Nie includuje w żadnej akcji innej akcji, bo to bez sensu. Po prostu dodaje nowe moduły i akcje i ustalam ich wzajemne relacje. Szczegóły techniczne to już sprawa frameworka - Ja po prostu uzywam jego API.
Jeśli nie potrzebujesz bardzo zaawansowanych mozliwości kontroli przebiegu żądania, to tak jak wspomniał NoiseMc - możesz zobaczyć jak to wygląda w ZF albo innym frameworku.

Takie testowe filmiki : P
http://cysiak.110mb.com/kvktest
Router parametrów żądania (pozwala dynmicznie zmienić miejsce, z którego pobierane są parametry, a akcja nawet się ine dowie, że coś takeigo się stało ; ]) http://cysiak.110mb.com/paramrouter
obsługa JavaScript http://cysiak.110mb.com/jssupport
NoiseMc
Cytat
$this->data->actionName=$this->name;

Jezeli 'data' nalezy do warstwy modelu to podawanie mu nazwy akcji powoduje sprzeganie sie warstw kontrolera i modelu. Wedlug mnie model nie powinien wogole wiedziec ze korzysta z niego jakis kontroler, o to wlasnie chodzi w MVC. Jezeli pisalbys w Javie to moglbys napisac taka warstwe modelu, ktora moglbys wykorzystac w aplikacji www, desktopowej albo w komorce bez zmiany kodu modelu. Tak mi sie zdaje smile.gif
Cysiaczek
Ten obiekt nie należy do warstwy modelu. To po prostu obiekt kolekcjonujący dane, którymi akcja chce się podzielić z warstwą widoku lub z inną akcją. Predefinowana zmienna actionName służy raczej do oznaczenia przynależności danego obiektu i przydaje się, gdy chę go wyszukać wśród kilku innych. np. kontroler widoków układa referencje do tych obiektów w tablicy asocjacyjnej używając właśnie nazw akcji jako kluczy.
Generalnie możesz też sobie zawsze w templatce wypisać nazwę akcji, co może być przydatne.
  1. <?php
  2. print $actionName;
  3. ?>


Co do modelu. Ciężko jest mówić o modelu w oderwaniu od implementacji. Kilka dni temu wprowdziłem kolejną funkcjonalność i bardziej można ją nazwac modelem, choć ja uważam, że to po prostu kontroler DAO, współpracujący obecnie w Propelem. Tutaj faktycznie każda akcja może mieć 100% pewności, że odwołując się tak:
  1. <?php
  2. $news=$this->dao->News;
  3. $news->setTitle('ble ble');
  4. $news->save();
  5. ?>


Otrzyma obiekt News, który jest klasą Propela i może sobie dowolnie z niego korzystać.
Jeśli zechce, może udostępnić cały ten obiekt, lub tylko jego fragmenty warstwie widoku poprzez:
  1. <?php
  2. //kontynuacja powyższego kodu
  3. $this->data->title=$news->getTitle();
  4. ?>


Nadmienie jeszcze tylko, że kontroler DAO na zasięg modułu - inne moduły i ich akcje nie widzą go, bo mają własne.
Jak widzisz. Tutaj akcja pełni rolę pośrednika pomiędzy warstwą danych, a widokiem. Sama z kolei jest uczestnikiem większej całości - modułu. Moduł z kolei jest częscią aplikacji lub małą aplikacją. Zauważ, że nie ma kodu odpowiedzialnego za prezentację, co oznacza, że moge sobie wyprodukować dowolne wyjście w postaci html, xml, pdf, pda, plain text, czy_co_tam_chcesz używając jednej i tej samej akcji.

Sorki za lekkie przynudzanie, ale chciałem pokazać, że tradycyjne pojęcie modelu, czy kontrolera
jest zbyt abstrakcyjne - trzeba przyglądać się konkretnym imlementacjom i zorientować się, czy jest sprzeganie, czy go nie ma. Ja w swoim FW chyba nawet przesadziełem z separacją, bo nie ma sensu odpalać go do napisania prostej strony-wizytówki, :|

Pozdrawiam.
menic
Z wszystkich mozliwosci zaprojektowania z jakimi sie spotkałem, najbardziej przypadła mi ta z Symfony. Zreszta podobnego schematu używałem od dawna, tylko teraz troche udoskonaliłem i dopisałem to czego mi w sf brakowało. IMHO podział własnie na moduły jest najefektywniejszy i najwygodniejszy smile.gif Mimo że jeszcze moj fw nie jest skonczony, to już sie na nim bardzo przyjemnie pisze smile.gif Symfony takiej szybkosci mi nie dawało. Przydałoby sie tylko dokonczyć wszystko z TODO i zrobić porządek w kodzie, bo w niektorych miejscach takich jak np. obsługa baza danych to az sie dziwie jak to działa winksmiley.jpg
Troche zboczyłem z tematu, ale co tam smile.gif
kaniagandzowski
Cysiaczek:

Bardzo mi się spodobał twój pomysł i chce wdrożyć do mojego programiku, który korzysta z Frameworka CodeIgniter wraz biblioteką XAJAX.

Jestem w fazie sklejania wszystkich bibliotek i budowania interfejsu dla użytkownika. Brakuje mi sposobu na przekazywanie różnych danych pomiędzy operacjami wykonywanymi przez użytkownika.

Zrozumiałem w ten sposób, że każda akcja (w CI jest to klasa typu kontroler) tworzy obiekt typu actionData w swoim konstruktorze.
  1. <?php
  2. $this->data=new actionData();
  3. ?>

Później dowolna metoda w akcji może dodać swoje jakieś dane np. dane do widoku , jakieś innej akcji lub swoją instancje obiektu, która to będzie się mieścić w obiekcie typu actionData pod nazwa $akcja, $metoda.

Następna metoda chcąc pobrać dane wystarczy że wpisze nazwę akcji i jej metodę ta zwróci wynik albo tekst lub obiekt to co akcja poprzednio wrzuciła.
Problemem będzie, że akcja pobierająca dane będzie musiała wiedzieć jakie typu dane są pobierane oraz jak się nazywa (w sumie to nie problem).

Ja nie bardzo kapuję jak może działać rozwiązanie sington w twoim kodzie (domyślam, że stosujesz ten wzorzec)
  1. <?php
  2. $this->AHelper=application_Helper::getInstance();
  3. ?>

Co on robi.
Ja wiem że wzorzec singleton zapewnia wszędzie ten sam obiekt zapobiegając tworzenia drugiego obiektu tej samej klasy (jestem laikiem więc mogę się mylić).

Może tak. Nie mogę sobie wyobrazić żeby przy następnych wywołaniach stron dawał ten sam obiekt (referencje obiektu, który na poprzedniej stronie został stworzony). Bo zapewniony będzie jeden obiekt do końca wykonania kodu. Tak więc jeśli zostanie przeładowana strona (wybrana jakaś akcja przez użytkownika) to wzorzec singleton stworzy nowy obiekt. Ponieważ nie będzie pamiętać obiektu.

Pisze dlatego o tym wzorcu bo myślę, że w twoim kodzie jest coś zaimplementowane
  1. <?php
  2. $this->AHelper=application_Helper::getInstance();
  3. ?>

Że on pobiera sobie instancje obiektu z sesji lub bazy danych albo Cook lub jest przekazywane przez javaScript.
Bardzo chcę poznać jak sklejacie w całość wszystko. Ja robiłem dotychczas że przekazywałem przez zmienne do konkretnej funkcji czy metody. To tworzyło straszne problemy i strasznie wiązało pomiędzy sobą te funkcje. Zmiana czegoś w jednej metodzie prowadziło do zmian wszędzie tam gdzie były powiązane z tą metodą. Co strasznie komplikowało kod.
menic
Ja u siebie postawiłem na elastycznośc. Może i ten sposób nie jest do końca poprawny, ale IMO najlepszy. Chodzi o przekazywanie danych między akcjami. Jedni używają w tym celu jakiegoś kontenera, a ja $_GET winksmiley.jpg
Sprawa wyglada tak. Wszelkie dane $_GET, $_POST sa na stracie wrzucane do singletona. W akcji robimy $this->request->Get('nazwa') lub $this->request->Post('nazwa'). W podobny sposób mozemy cos dorzucic od siebie winksmiley.jpg $this->request->setGet('nazwa', 'wartosc') itp.
Eleganckie zbytnio to nie jest, przyznaje. Za to użyteczne jak najbardziej snitch.gif Chodzi o to aby poszczegolne akcje były jak najbardziej samodzielne. Jedne akcje sa wywoływane bezposrednio, a inne z forwardu. I zeby zachowac spojnosc to w akcji z ktorej jest forward() robimy $this->request->setGet('nazwa', 'wartosc'). I w akcji przekierowanej mamy swobodny dostep do potrzebnych parametrow smile.gif
kaniagandzowski
Na pewno będę stosować GET np. do wywołania wybranego formularza http://program.pl/nrIdFormularza wiadomo tam gdzie jest to mile widziane. Ale w innych przypadkach nie będzie dobrym rozwiązaniem stosować GET lub POST (brzydki kod i nie połapię się).

Mój program ma umożliwiać dodawanie elementów, warunki, akcje, filtry do formularza (tego konkretnego formularza). Dodawane różne elementy do wybranego formularza np. akcje będą generować dane czy zostało dodane element lub inne informacje jak chociażby HTML (nie ważne. Ważne jest aby nie ograniczać się jakimiś ograniczeniami tylko tworzyć luźne powiązania, które po zmianach w jakieś akcji nie będzie wymagała zmian w innych miejscach kodu itd).

Tak więc mój DUŻY PROBLEM to jest zebranie, łączenie tych wszystkich danych i przekazywaniu do różnych akcji (W CodeIgniter są to klasy typu kontroler). która to później przekazuje dane do różnych klas i one obrabiają dane.
Nieraz dane mogą być pomiędzy sobą powiązane np. jak zrobić że mam jakieś zakładki, a w jednej z nich dane użytkownika, a w drugiej chcę zobaczyć dane szczegółowe z innej tabeli powiązane z tym danym użytkownikiem. Trzeba pamiętać żeby nie pogubić danych co użytkownik wybrał, żeby później mógł wrócić do poprzedniej zakładki z tymi danymi co użytkownik wybrał.

Tak więc bardzo interesuje się rozwiązaniami, jakie stosujecie. A jak na razie, spodobało mi się rozwiązanie Pana Cysiaczek.
Tylko nie wiem jak przekazuje te różne dane pomiędzy różnymi wywołaniami stron. Czy stosuje sesje do której serializuje obiekt typu actionData. Jak się ma do tego wzorzec sigleton (czy on tylko ma zapewnić jeden obiekt w wywołaniu kodu - wykonanie akcji, która coś wykona i skorzysta z innych klas, a na końcu skończy żywot obiekt i będzie czekać na następną reakcje użytkownika).
Ludvik
Ja skłaniam się do wzorca obiektu kontekstu. Wszystkie dane żądania mapujemy na odpowiednie parametry, które zapisujemy w kontekście żądania. Mapy tworzymy dla każdej akcji i protokołu, gdyż zależą od obydwu czynników. Dane wytworzone przez akcje zapisujemy w kontekście odpowiedzi, który jest przekazywany do wywołania każdej z nich. Ostatecznie trafia do obiektu, który na jego podstawie generuje widok. Może to być szablon XHTML, RSS czy inne cuda. Pisałem dokładniej o tym u siebie na blogu...

Zapisywanie danych pomiędzy żądaniami to inna sprawa. Ja bym utworzył obiekt stanu użytkownika, w którym można zapisywać dowolne dane, w tym obiekty. Wszystko to by trafiało do sesji... To już Twoja decyzja, co Ci będzie potrzebne później. Podglądnij symfony, tam jest to nieźle rozwiązane...
kaniagandzowski
Nie znam tego wzorca Context Object (wzorca obiektu kontekstu). Szukałem opis tego wzorca lecz nie mogę znaleźć. Na forum był poruszany temat wzorców lecz podane tam linki też nie znalazłem.

Wzorce projektowe, co to, poco, dlaczego

Czy mógł bym prosić o link lub opis jak wygląda z przykładami.
Ludvik
Google wyrzuca pierwszy wynik - Core J2EE Patterns. Przykład wykorzystania masz u mnie na blogu. Warto przeczytać właśnie Core J2EE Wzorce Projektowe i Patterns of Enterprise Application Architecture Fowlera (chociaż w tej drugiej nie ma tego wzorca, ale jest sporo innych, mniej znanych...).
kaniagandzowski
Dzięki za szybką odpowiedz.

Przeczytałem klika krotnie twój artykuł Elastyczny kontroler (http://ludvik.pl/2007/08/23/elastyczny-kontroler/) i nie do końca rozumiem jak uruchomić to i jak operować tym wzorcem.

Zrozumiałem w ten sposób (tyczy się przykładu kodu umieszczonego na stronie ludvik.pl art. „elastyczny kontroler”)
Klasa RequestContext (interfejs) to do tej klasy w sumie obiekty typu RequestContext ,są wrzucane dane, przekazane przez użytkownika. A obiekt posiada informacje do jakiej metody są przeznaczone te parametry.

interface ResponseContext (Interfejs kontekstu odpowiedzi ) obiekty tego typu zawierają dane wrzucane przez daną akcje (metodę – wyniki ich wykonania z którymi się chcą podzielić z widokami lub akcjami)

interface HttpRequestDispatcher (Interfejs request dispatchera dla protokołu http) – tego nie wiem co robi
oraz
  1. <?php
  2. class HttpFrontController {
  3. /**
  4.  * Uruchamia kontroler.
  5.  */
  6. function run(HttpRequest $req, HttpResponse $res) {
  7. $dispatcher = new BaseHttpRequestDispatcher();
  8. $controller = $dispatcher->getApplicationController($req);
  9. $reqContext = $dispatcher->getRequestContext($req);
  10. $resContext = $dispatcher->getResponseContext();
  11.  
  12. $controller->forward($reqContext->getActionName());
  13.  
  14. while($controller->hasNextCommand()) {
  15. $command = $controller->getNextCommand();
  16. $command->execute($reqContext, $resContext);
  17. }
  18.  
  19. $view = new BaseHttpView($req, $res);
  20. $view->render($controler->getView(), $resContext);
  21. }
  22. ?>

Do metody są wrzucane dwa parametry $reg typu HttpRequest – nigdzie nie ma pokazanej tej klasy ! oraz drugi parametr $res typu HttpResponse jego klasy tez nie widzę!
Tak domyślam że klasa HttpFrontController tworzy swój obiekt z danymi przekazanymi do akcji i dane przeznaczone do widoku. Które później ten obiekt powstały ma wrzucane dane przekazane dla tej akcji parametr.

Szukałem innych przykładów tego wzorca Context Object lecz nie znalazłem dla php.
Ludvik
Klasy HttpRequest i HttpResponse to pseudo-klasy, które zawierają szczegółowe żądanie i odpowiedź związaną z protokołem HTTP. Pierwsza z nich zawiera zmienne GET, POST, ciastka itp. Obiekt odpowiedzi zawiera dane typowe dla odpowiedzi HTTP, czyli nagłówki (w tym ciastka) i treść odpowiedzi. Obiekty kontekstu nie zawierają natomiast danych związanych z protokołem. Obiekty implementujące interfejs request dispatchera mają za zadanie:
  • Wywnioskować na podstawie szczegółowego żądania nazwy akcji i kontrolera aplikacji
  • Przetłumaczyć parametry związane z protokołem (dla HTTP są to GET, POST itp...) na parametry związane z akcją
Na podanych wcześniej przeze mnie stronach drugą z tych ról nazwano fabryką kontekstu. Jak to działa? Powiedzmy, że dostajesz żądanie POST z URL /news/666/comment. W tablicy POST znajdują się dane z formularza komentowania, czyli posiadające klucze autor, mail, strona, tresc. Obiekt HttpRequest będzie posiadał zarówno PATHINFO jak i wszystkie dane przesłane metodą POST. Teraz pałeczkę przejmuje RequestDispatcher dla protokołu Http. Najpierw odczytujemy URL i na podstawie jakiejś mapy/wyrażeń regularnych dopasowujemy go do akcji - w tym przypadku powiedzmy addNewsComment. Z URL możemy też odczytać identyfikator newsa. Zapisujemy go w kontekście żądania z kluczem id. Wybrana przez nas akcja doskonale wie, do czego on się odnosi. Następnie zapisujemy w obiekcie kontekstu dane przesłane POST-em. Klucze będą takie same, bo się nie powtarzają. Teraz w akcji możemy spokojnie odnosić się do obiektu kontekstu, zapominając o implementacji protokołu. Akcji nie obchodzi to, czy żądanie jest wysłane protokołem HTTP, czy jest to żądanie SOAP, AJAX czy inne... Odebraniem komunikatu zajmie się RequestDispatcher, który przetłumaczy go do postaci zrozumiałej dla akcji. Akcje zapiszą rezultaty swojego działania w obiekcie kontekstu odpowiedzi. Uformowaniem odpowiedzi dla konkretnego protokołu zajmie się widok.

Trudno znaleźć przykłady, bo rzadko używa się tego wzorca w PHP. Główną jego zaletą jest zlikwidowanie zależności od protokołu, a programiści PHP rzadko kiedy wykraczają poza HTTP GET i POST... Dla mnie ten wzorzec ma jeszcze jedną zaletę - nieco zwiększa przejrzystość kodu akcji.
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.