Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: multiwidoki
Forum PHP.pl > Forum > PHP > Pro > Archiwum Pro
chmolu
Witam,

Jest jedna rzecz, z którą jakoś nie mogę dać sobie rady. Wbrew pozorom problem nie jest taki znów banalny.
Chodzi o to, jak w ładny sposób rozwiązać kwestię kilku widoków na stronie, tzn: nagłówek, menu, treść, reklama, stopka. Do tej pory robiłem to w następujący sposób:

  1. // plik header.php
  2. <?php $this->executeHelper('MenuViewHelper'); ?>
  3. <head><title><?php echo $title ?></title></head>
  4.  
  5. ---------------------
  6.  
  7. //plik article.php
  8. inlude('header.php');
  9.  
  10. tutaj jakas tresc
  11.  
  12. include('footer.php');


Szablon article.php jest wywoływany przez główną akcję. W includowanych plikach istnieje możliwość wywołania klasy typu helper, które wyciągają dane z bazy i robią wszystkie assigny. Do tej pory ten sposób mi odpowiadał, jednak teraz chcę, by można było cachować poszczególne części strony. Nie mówię tu o serializacji obiektów, czy tablic, ale o cachowaniu gotowego htmla.

Powiedzmy, że mam jedną akcję wywoływaną w urlu i stronę, która składa się z: nagłówka, menu, treści artykułu, listy najnowszych tematów na forum oraz ze stopki. Przy czym, menu i treść artykułu są cachowane. Reszta nie.

Jak to ładnie rozwiązać? Zapraszam szanowne grono do dyskusji smile.gif
bela
Ja robię tak.
Określam requesty, które są dopasowywane do URLa.
W requeście może siedzieć kilka akcji.
Po wywołaniu wszystkich akcji, wynik ich jest przekazywany do widoku i zależnie od widoku i jego parametrów jest to wszystko wyświetlane.
chmolu
Można bardziej szczegółowo?
Z przykładami smile.gif
bela
Fragment konfiga
  1. <request match="*">
  2.        <action>
  3.            <name>TestAction</name>
  4.        </action>
  5.  
  6.        <action>
  7.            <name>Dupa</name>
  8.        </action>
  9.        <view>
  10.            <name>smarty</name>
  11.            <params>
  12.                <template>index.tpl</template>
  13.            </params>
  14.        </view>
  15.    </request>


To umieszczam na ostatnim miejscu, a więc jeśli nie zostanie dopasowany do innych, to jest uruchamiany ten. A w nim znajdują się 2 akcję: Test i Dupa. Widokiem jest smarty - wszystkie widoki to pluginy - i zostaje uruchomiony z parametrem template równym index.tpl.
Sam plugin jest konfigurowany przy 1. próbie dostępu do niego.
chmolu
Hm... sam nie wiem. Pomyślę jeszcze nad tym.

A może zna ktoś lżejszy sposób?
chmolu
Wyskrobałem coś takiego:

  1. <?php
  2.  
  3. interface IView {
  4. public function render();
  5. }
  6.  
  7. abstract class CachedView implements IView {
  8. // oczywiscie to wszystko powinno byc zaimplementowane;)
  9. protected function isCached() {}
  10. protected function loadCache() {}
  11. protected function clearCache() {}
  12. }
  13.  
  14. /** 
  15.  * Prosty system szablonow w postaci skryptow php
  16.  *
  17.  */
  18. abstract class PHPView extends CachedView {
  19. private $vars = array();
  20.  
  21. protected function assign($var, $value = false) {
  22. if (is_array($var)) {
  23. $this->vars = array_merge($this->vars, $var);
  24. } else {
  25. $this->vars[$var] = $value;
  26. }
  27. }
  28.  
  29. protected function fetch($_tpl_file) {
  30. if (!is_readable($_tpl_file)) {
  31. throw new TemplateException('TPL file ' . $_tpl_file .' does not exists');
  32. }
  33.  
  34. extract($this->vars, EXTR_PREFIX_SAME, '_');
  35. include($_tpl_file);
  36. }
  37. }
  38.  
  39. class CompositeView implements IView {
  40. private $components = array();
  41.  
  42. public function addView(IView $view) {
  43. $this->components[] = $view;
  44. }
  45.  
  46. public function render() {
  47. foreach ($this->components as $view) {
  48. $view->render();
  49. }
  50. }
  51. }
  52.  
  53. class ShowArticle extends PHPView {
  54. public function render() {
  55. $this->assign('content', $art->loadArticle());
  56.  
  57. return $this->fetch('show_article.php');
  58. }
  59. }
  60.  
  61.  
  62. $composite = new CompositeView();
  63. $composite->add(new ShowHeader());
  64. $composite->add(new ShowMenu());
  65. $composite->add(new ShowArticle());
  66. $composite->add(new ShowFooter());
  67.  
  68. ?>

Można to teraz łatwo do konfiguracji wywalić.

Niby działa ładnie, ale jest jedna wada. Jako, że szablony są inicjalizowane lokalnie nie ma możliwości, żeby np. z widoku ShowArticle assignować zmienną dla ShowHeader(), czyli ustawić tytuł, słowa kluczowe etc.

Muszę jeszcze trochę nad tym posiedziec :/
Vengeance
W szablonie głównym dodawaj pozostałe szablony (np. menu) poprzez rozszerzoną metodę Include() która będzie potrafiła sprawdzić czy istnieje Cache (w formie czystego HTMLa wygenrowanego wcześniej) dla danego szablonu.

Jeżeli istnieje to go zwróci w czystej formie, a jeżeli nie to przystąp do generowania szablonu tak jak to robisz teraz. I dopiero w menu.tpl użyj odpowiednich ViewHelperów wykonujących odpowiednie zapytania do DAO i wykonujących wymagane Assigny().

Potem zwracasz HTML, a ww metoda Include() zapisuje go do Cache oraz zwraca do głównego szablonu.
NuLL
@chmolu - fajowski pomysł z kompozytowym widokiem cool.gif

A może by tak dodać assign'y do całościowego widoku. Najpierw przetworzyc akcję, dodać w niej assigny a potem wypluć całość :?:
chmolu
Cytat
W szablonie głównym dodawaj pozostałe szablony (np. menu) poprzez rozszerzoną metodę Include() która będzie potrafiła sprawdzić czy istnieje Cache (w formie czystego HTMLa wygenrowanego wcześniej) dla danego szablonu.

Jeżeli istnieje to go zwróci w czystej formie, a jeżeli nie to przystąp do generowania szablonu tak jak to robisz teraz. I dopiero w menu.tpl użyj odpowiednich ViewHelperów wykonujących odpowiednie zapytania do DAO i wykonujących wymagane Assigny().

Potem zwracasz HTML, a ww metoda Include() zapisuje go do Cache oraz zwraca do głównego szablonu.


Myślałem trochę nad tym. No dobrze, mogę zrobić $this->include() i to sprawdzi mi, czy jest cache. Powiedzmy, że wywołuję szablon show_article.tpl.php. Wewnątrz includuję header.tpl.php. Metoda $this->include sprawdza, czy header posiada cache. Jeśli posiada, to zostaje wczytany. Ale... co jeśli w pliku header.tpl.php jest dołączany jeszcze menu.tpl.php??

Taka metoda nie nadaje się do większej hierarchii widoków.

Naprawdę już nikt nie ma pomysłu, jak to zrealizować??
NuLL
Pomysł mam smile.gif Tylko zapomniałem o nim tu napisać. Takie coś można by uzyskać tworząc system buforów wyjściowych. Można by napisać jakiś mini manager buforów który będzie je trzymał razem w kupie. Każdy widok który ma zostać obrobiony miałby swój bufor - można to zrobić przez dziedziczenie. Bufory dzięki managerowi moglby się komunikować lub podczas jakieś wstępnej pętli bo buforach w managerze dodać 'swoje' możliwe globalne assigny. I potem dopiero render. Tak ja to widze.
chmolu
Moglbys rozwinąć tę myśl? Wygląda ciekawie. Jakiś pseudokod by się przydał :]
NuLL
Spróbuje coś jutro skodzić. Sam walcze z buforami w swoim systemie. Z tym że u mnie nie będzie managera buforów bo bufory będą w strkturze drezwiastej biggrin.gif Jutro zedytuje posta i postram się coś napisać smile.gif
chmolu
Spróbujmy jeszcze bardziej skomplikować sytuację :]

Powiedzmy, że strona składa się z następujących części/bloków:
- nagłówek,
- menu,
- reklama,
- treść artykułu,
- komentarze,
- stopka

Nagłówek jest statyczny, menu jest cachowane. Reklama nie jest.
Treść artykułu jest pobierana z bazy danych w postaci XML'a (DocBook, OpenOffice), przekształcana za pomocą XSL do HTMLa i cachowana. Komentarze też są cachowane, dopóki nie dojdą nowe wpisy. Stopka jest statyczna.

Oj, czarno to widzę biggrin.gif
NuLL
  1. <?php
  2.  
  3. $buffer=new outputBuffer();
  4.  
  5. $buffer->addChild(new outputBuffer($tresc_menu));
  6.  
  7. $buffer->addChild(new outputBuffer($tresc_artykulu)->addChild(new outputBuffer($tresc_komentarzy)));
  8.  
  9. $buffer->collectAssigns();
  10.  
  11. $buffer->render();
  12.  
  13.  
  14. ?>

Z palca pisane - sama idea.
Vengeance
chmolu: Wydaje mi się że powoli zaczynasz przesadzać i pisać nad wyrost... Najpierw zastanowił bym się czy kiedykolwiek przyda ci sie az taki system cache... i czy w ogole on ma sens.

Ja rozumiem... optymalizacja itd... Ale jak dla mnie lepiej skupic sie nad zapytaniami sql i samym kodem, zoptymalizowac go... niz sleczyc 3 miesiace nad pisaniem krowy do cache... ktorej nigdy nie wykorzystasz :]

Ja wiem ze cache to modne teraz slowo... ale bez przesady chlopaki :/
NuLL
@vee - to jest przepis na multiwidok wg. mnie a nie na cache jak coś tongue.gif
Vengeance
pisalem juz wiele rzeczy, i zwykly glowny TPL z includem headera/footera ew. innych rzeczy zawsze mi starczal. Dodanie do tego Cache poszczegolnych includowanych elementow tez wg mnie nie jest trudne...

No a jak chcesz miec Cache Headera ale bez miejsca gdzie sa bannery... no coz :/ Wywal bannery albo tak rozdziel pliki TPL, by pozbyc sie problemu.
chmolu
Cytat
chmolu: Wydaje mi się że powoli zaczynasz przesadzać


Nie wiem dlaczego, ale zawsze lubię sobie życie kompilować. Zanim napisałeś tego posta uświadomiłem sobie, że rzeczywiście przeginam smile.gif

Postanowiłem, że będę cachował tylko częściowo dane. Nie będę mordował się z tym co tutaj omawiamy, bo raczej już nic sensownego nie wymyślę.
Vengeance
"życie kompilować" hmm google ani wiki nie wyjaśniły mi tego pojęcia, wiec może ty to zrób snitch.gif
matid
A co do multiwidoków, to czy najlepszym wyjściem nie będzie Composite View?
chmolu
Cytat
"życie kompilować" hmm google ani wiki nie wyjaśniły mi tego pojęcia, wiec może ty to zrób


LOL. Mam chyba ten sam syndrom co Zyx biggrin.gif
Powinno być komplikować, oczywiście smile.gif

Cytat
A co do multiwidoków, to czy najlepszym wyjściem nie będzie Composite View?

Jeśli chodzi ci o ten Composite, który podałem wyżej, to niestety w moim przypadku się on nie sprawdzi. Nie znaczy to oczywiście, że jest on bezużyteczny :]
chmolu
Cytat
Sprawdzone rozwiązanie stosowane w Smalltalku. Jednym słowem jestem za

Jasne, że to dobre rozwiązanie. Tylko dobrej implementacji brak biggrin.gif
emilio
A to Composite View ?
chmolu
A masz jakąś propozycję, jak to sensownie przenieść do php?
M.in. na tym dokumencie się wzorowałem pisząc kod, który podałem wcześniej, ale moje rozwiązanie mi nie odpowiada smile.gif
emilio
To jest implementacja wzorca Composite (skorzystałem z Composite).
  1. <?php
  2.  
  3. abstract class Component {
  4. protected $sName;
  5.  
  6. public function __construct($sName) {
  7. $this->sName = $sName;
  8. }
  9.  
  10. abstract public function Add(Component $o);
  11. abstract public function Remove(Component $o);
  12. abstract public function Display($iDepth);
  13.  
  14. public function __toString() {
  15. return $this->sName;
  16. }
  17. }
  18.  
  19.  
  20. class Composite extends Component {
  21. private $aChildren = array();
  22.  
  23. public function Add(Component $oComponent) {
  24. $this->aChildren[$oComponent->__toString()] = $oComponent;
  25. }
  26.  
  27. public function Remove(Component $oComponent) {
  28. unset($this->aChildren[$oComponent->__toString()]);
  29. }
  30.  
  31. public function Display($iDepth) {
  32. echo str_pad('-', $iDepth, '-').$this->sName.'<br />';
  33.  
  34. foreach($this->aChildren as $oValue) {
  35. echo $oValue->Display($iDepth + 2);
  36. }
  37. }
  38. }
  39.  
  40. class Leaf extends Component {
  41. public function Add(Component $o) {
  42. echo 'Cannot add to a leaf';
  43. }
  44.  
  45. public function Remove(Component $o) {
  46. echo 'Cannot remove from a leaf';
  47. }
  48.  
  49. public function Display($iDepth) {
  50. echo str_pad('-', $iDepth, '-').$this->sName.'<br />';
  51. }
  52. }
  53.  
  54. $root = new Composite('root');
  55. $root->Add(new Leaf('Leaf A'));
  56. $root->Add(new Leaf('Leaf B'));
  57.  
  58. $comp = new Composite('Composite X');
  59. $comp->Add(new Leaf('Leaf XA'));
  60. $comp->Add(new Leaf('Leaf XB'));
  61.  
  62. $root->Add($comp);
  63. $root->Add(new Leaf('Leaf C'));
  64.  
  65. $l = new Leaf('Leaf D');
  66. $root->Add($l);
  67. $root->Remove($l);
  68.  
  69. $root->Display(1);
  70.  
  71. ?>

Mam nadzieję że to Ci pomoże winksmiley.jpg
chmolu
Hmm.. ciekawe, jakby to się sprawdziło w praktyce. Przydałoby się coś takiego, jak getParent(), żeby dany liść mógł assignować np. tytuł dla strony lub inną globalną zmienną :]

Póki co pozostaję przy starym sposobie smile.gif
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-2024 Invision Power Services, Inc.