Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Klasa generująca tagi html.
Forum PHP.pl > Forum > PHP > Object-oriented programming
windman
Witam wszystki,

zacząłem pisać klasę do generowania tagów/kodu HTML.
Nakreślę najpierw jak działa, a potem napiszę w czym problem.

Jednym z ważniejszych klasy jest właściwość "neighborTags", jest to tablica zawierająca z kolei 4 inne tablice: before, after, outer, inner.
Tak to działa:
  1. //Tworzony jest pierwszy tag
  2. $img = new Tag('img', array('class'=>'logi','style'=>'border:0'));
  3.  
  4. //Tworzymy link to zdjecia, tag 'a' opakowujący tag 'img'
  5. //w obiekcie $img zostanie utworzony nowy obiekt klas Tag a następnie zapisany we właściwości $neighborTags: array_unshift($this->neberTags['inner'], $tag);
  6. $a = $img->outer('a',array('href'=>'http://php.pl'));
  7.  
  8. //za tagiem 'a' wstawiamy 'span'
  9. //w obiekcie $a zostanie utworzony nowy obiekt klasy Tag a następnie zapisany we właściwości $neighborTags: $neighborTags: array_unshift($this->neberTags['inner'], $tag);
  10. $a->after('span',array('class'=>'span1'));


Teraz mój problem.
Napisałem metodę, która generuje kod html na zasadzie łączenia stringów.
Generowałem pierwszy tag i sprawdzałem co ma w 'before' i w 'after' doklejałem co trzeba i dalej...
Niestety bardziej skomplikowany kod jest źle generowany...

Nie mam innego pomysłu jak to rozwiązać...
Nie jestem nawet pewien czy jest to możliwe w przypadku takiej struktury danych.

Ktoś ma jakiś pomysł?
wookieb
Ok zacznijmy od tego Po co ci coś takiego robić. Takie rozwiązanie o którym mówisz jest najgorszym z możliwych rozwiązań w Zend_Form_Decorators. Już niedługo dojdzie do tego, że każda literka będzie obiektem.
windman
Cytat(wookieb @ 11.09.2009, 19:07:46 ) *
Ok zacznijmy od tego Po co ci coś takiego robić. Takie rozwiązanie o którym mówisz jest najgorszym z możliwych rozwiązań w Zend_Form_Decorators. Już niedługo dojdzie do tego, że każda literka będzie obiektem.

czy to ma znaczenie po co?
chodzi o rozwiązanie tego problemu...

Generalnie klasa będzie miała 'cikawsze' zadania niż tylko generowanie htmla ale najpierw muszę rozwiązać ten problem.
wookieb
No to przy generowaniu jednego taga sprawdza, jakie tagi ma w before. Generujesz je i wstawiasz przed tag. Podobine z after. Ale skoro mówisz, że źle ci działa to może byś sypnął kodem?
windman
Cytat(wookieb @ 11.09.2009, 19:14:55 ) *
No to przy generowaniu jednego taga sprawdza, jakie tagi ma w before. Generujesz je i wstawiasz przed tag. Podobine z after. Ale skoro mówisz, że źle ci działa to może byś sypnął kodem?



Problem tkwi w metodzie renderAll()
  1. class Tag
  2. {
  3. public $tagType;
  4. public $tagAttributes;
  5. public $neberTags = array('before'=>array(), 'after'=>array(), 'inner'=>array(), 'outer'=>array());
  6. public $options = false;
  7. private $optionsDefault = array('quotation'=>'"');
  8. private $closedTags = array('img'=>1,'input'=>1);
  9.  
  10.  
  11. public function __construct($tagType, $tagAttributes = array(), $options = array())
  12. {
  13. $this->tagType = $tagType;
  14. $this->tagAttributes = $tagAttributes;
  15. $this->options = array_merge($this->optionsDefault, $options);
  16. }
  17.  
  18. public function after($tagType, $tagAttributes = array(), $options = array())
  19. {
  20. $options = array_merge($this->options, $options);
  21.  
  22. $tag = new Tag($tagType, $tagAttributes, $options);
  23. array_unshift($this->neberTags['after'], $tag);
  24. //$tag = $this->neberTags['after'][] = new Tag($tagType, $tagAttributes, $options);
  25. return $tag;
  26. }
  27.  
  28. public function before($tagType, $tagAttributes = array(), $options = array())
  29. {
  30. $options = array_merge($this->options, $options);
  31.  
  32. $tag = new Tag($tagType, $tagAttributes, $options);
  33. array_unshift($this->neberTags['before'], $tag);
  34. return $tag;
  35. }
  36.  
  37. public function outer($tagType, $tagAttributes = array(), $options = array())
  38. {
  39. $options = array_merge($this->options, $options);
  40.  
  41. $tag = new Tag($tagType, $tagAttributes, $options);
  42. array_unshift($this->neberTags['outer'], $tag);
  43. return $tag;
  44. }
  45.  
  46. public function inner($tagType, $tagAttributes = array(), $options = array())
  47. {
  48. $options = array_merge($this->options, $options);
  49.  
  50. $tag = new Tag($tagType, $tagAttributes, $options);
  51. array_unshift($this->neberTags['inner'], $tag);
  52. return $tag;
  53. }
  54.  
  55. public function link($url, $tagAttributes = array(), $options = array())
  56. {
  57. $options = array_merge($this->options, $options);
  58.  
  59. $defaultAttributes = array('href'=>$url, 'title'=>'');
  60. $tagAttributes = array_merge($defaultAttributes, $tagAttributes);
  61.  
  62. if(!$tagAttributes['href'] || !file_exists($tagAttributes['href'])) return false;
  63.  
  64. $tag = new Tag('a', $tagAttributes, $options);
  65. array_unshift($this->neberTags['outer'], $tag);
  66. return $tag;
  67. }
  68.  
  69. public function render($echo = true)
  70. {
  71. $attributes = $this->attributes($this->tagAttributes);
  72.  
  73. if(!empty($this->closedTags[$this->tagType])){
  74. if($echo === true){
  75. echo "<{$this->tagType}{$attributes} />";
  76. }else{
  77. return "<{$this->tagType}{$attributes} />";
  78. }
  79. }else{
  80. if($echo === true){
  81. echo "<{$this->tagType}{$attributes}></{$this->tagType}>";
  82. }else{
  83. return "<{$this->tagType}{$attributes}></{$this->tagType}>";
  84. }
  85.  
  86. }
  87. }
  88.  
  89. public function renderAll($html = false, $type = false)
  90. {
  91. if($type == false) $html = $this->render(false);
  92.  
  93. if($type == 'after') $html = $html.$this->render(false);
  94. if($type == 'before') $html = $this->render(false).$html;
  95.  
  96. if($type == 'outer'){
  97. $out = explode('><',$this->render(false));
  98. $html = $out[0].'>'.$html.'<'.$out[1];
  99. }
  100.  
  101. if($type == 'inner'){
  102. $out = explode('><',$html);
  103. $html = $out[0].'>'.$this->render(false).'<'.$out[1];
  104. }
  105.  
  106. foreach($this->neberTags as $k=>$types){
  107. foreach($types as $k2=>$v2){
  108. $html = $v2->renderAll($html, $k);
  109. }
  110. }
  111. return $html;
  112. }
  113.  
  114. private function attributes($tagAttributes)
  115. {
  116. if(!is_array($tagAttributes)) return;
  117. foreach($tagAttributes as $attribute=>$value){
  118. $attributes .= ' '.$attribute.'='.$this->options['quotation'].$value.$this->options['quotation'];
  119. }
  120. return $attributes;
  121. }
  122.  
  123. }
jang
linia 108:
  1. $html = $v2->renderAll($html, $k);


co to jest $v2 - obiekt ? , do $this też jakoś niepodobne winksmiley.jpg
windman
Cytat(jang @ 11.09.2009, 19:42:45 ) *
linia 108:
$html = $v2->renderAll($html, $k);
co to jest $v2 - obiekt ? , do $this też jakoś niepodobne winksmiley.jpg


zgadza się obiekt, mało trafna nazwa co?
v od value (element tablicy), 2 to zagnieżdżenie pętli foreach.
generalnie cała ta metoda jest skopana, przepisywałem ją po kilka razy i dlatego takie nazewnictwo...
jang
Cytat(windman @ 11.09.2009, 18:53:07 ) *
zgadza się obiekt, mało trafna nazwa co?
v od value (element tablicy), 2 to zagnieżdżenie pętli foreach.
generalnie cała ta metoda jest skopana, przepisywałem ją po kilka razy i dlatego takie nazewnictwo...


czyli neberTags to jest tablica obiektów ?
jesteś pewien, że odczytujesz je w tej samej kolejności co je zapisałeś ?
zawsze wpisujesz coś do wszystkich 4 metod - 'before' 'after' 'inner' 'outer'
nie sypie się wszystko gdy masz puste np. inner
czyli np.
1. zapisujesz do wszystkich czterech
2. zapisujesz do trzech (bez inner ? outer ?)
3. znowu do czterech
windman
Cytat(jang @ 11.09.2009, 20:23:49 ) *
czyli neberTags to jest tablica obiektów ?
jesteś pewien, że odczytujesz je w tej samej kolejności co je zapisałeś ?
zawsze wpisujesz coś do wszystkich 4 metod - 'before' 'after' 'inner' 'outer'
nie sypie się wszystko gdy masz puste np. inner
czyli np.
1. zapisujesz do wszystkich czterech
2. zapisujesz do trzech (bez inner ? outer ?)
3. znowu do czterech

nie bardzo wiem o co pytasz więc wytłumaczę jak to leci:
  1. //Tworzę pierwszy tag IMG
  2. $img = new Tag('img');
  3. //za IMG wstawiam SPAN. Do tablicy neberTags['after'] obiektu $img dodaje pierwszą pozycją, jest nią obiekt $span
  4. $span = $img->after('span');
  5. //znowu za IMG wstawiam P. Do tablicyneberTags['after'] obiektu $img dodaję drugą pozycję, jest nią obiekt $p
  6. $p = $img->after('p');
  7. /*
  8. W tej chwili mój kod htm powinien wyglądać tak:
  9. <img/><p></p><span></span>
  10.  
  11. zwróć uwagę, że P jest przed SPAN, chociaż dodane zostało po tym jak dodałem SPAN.
  12. Powinno tak być gdyż metodę 'after' wykonałem na obiekcie $img.
  13. Gdybym chciał aby P było po SPAN kod musiał by wyglądać tak:
  14. */
  15. $img = new Tag('img');
  16. $span = $img->after('span');
  17. $p = $span->after('p');


Wydaje mi się, że tablica $neberTags jest prawidłowo uzupełniana.
Problemem jest metoda renderAll(). Nie jestem pewien, czy rekurencja jest poprawna i czy zwracane są odpowiednie wartości.

jang
  1. foreach($this->neberTags as $k=>$types){
  2. echo "<pre>";
  3. echo "k : <br />";
  4. var_dump($k);
  5. echo "<pre>";
  6. foreach($types as $k2=>$v2){
  7. $html = $v2->renderAll($html, $k);
  8. }
  9. }
  10. return $html;
  11. }


uruchomienie:
  1. $img = new Tag('img');
  2. $p = $img->after('p');
  3. $span = $img->after('span');
  4.  
  5. $img->renderAll();


efekt:
  1. k :
  2. string(6) "before"
  3. k :
  4. string(5) "after"
  5. k :
  6. string(6) "before"
  7. k :
  8. string(5) "after"
  9. k :
  10. string(5) "inner"
  11. k :
  12. string(5) "outer"
  13. k :
  14. string(6) "before"
  15. k :
  16. string(5) "after"
  17. k :
  18. string(5) "inner"
  19. k :
  20. string(5) "outer"
  21. k :
  22. string(5) "inner"
  23. k :
  24. string(5) "outer"


musisz dojść do tego skąd tyle tego się nabrało, jak będzie wyglądało tak jak poniżej wtedy powinno działać

  1. k :
  2. string(6) "before"
  3. k :
  4. string(5) "after"
  5. k :
  6. string(5) "inner"
  7. k :
  8. string(5) "outer"
zegarek84
mi to wygląda (przyznaję się, że szczegółowo nie analizowałem skryptu) jak jednopoziomowa tablica asocjacyjna dla tagów (niższe poziomy są tylko dla atrybutów) gdzie jeszcze są zapisane niby zależności - ale zależności zagnieżdżeń tagów powinno też się realizować odpowiednio przez zagnieżdżenie tablicy kolejnej asocjacyjnej...

poza tym nie bardzo wiem do czego ten skrypt ma być tym bardziej, że w samym PHP jest też mechanizm DOM - skoro nie znam końcowego przeznaczenia więc nie mogę też krytykować - ale może jednak dało by się jakoś wykorzystać tutaj logikę DOM'u?? - spróbuj to zaimplementować:
Document Object Model
windman
Cytat(zegarek84 @ 11.09.2009, 22:25:02 ) *
zależności zagnieżdżeń tagów powinno też się realizować odpowiednio przez zagnieżdżenie tablicy kolejnej asocjacyjnej...

No ale dokładnie tak jest...
atrybuty nie mają z tym nic wspólnego. Każdy obiekt posiada oddzielną tablicę to przechowywania atrybutów ($tagAttributes).

Zagnieżdżone tagi to tablica $neberTags - tak naprawdę jest to tablica zawierająca kolejne Obiekty tagów - odpowiednio zagnieżdżone właśnie.
jang
tak w największym skrócie wyobrażam sobie prowadzenie wykopalisk :
  1. <?php
  2.  
  3. class Tag
  4. {
  5. // private $tags = array('br' => '<br />', 'p' => '<p>', 'pc' => '</p>');
  6. private $tags = array('br' => ' br ', 'p' => ' p ', 'pc' => ' pc ', 'img' => ' img '); // to po to aby można w echo coś zobaczyć
  7. private $before = array();
  8. private $inner = array();
  9. private $after = array();
  10.  
  11.  
  12. public function __construct($inner)
  13. {
  14. $this->inner = $inner;
  15. }
  16.  
  17. public function before($before)
  18. {
  19. $this->before[] = $before;
  20. }
  21.  
  22. public function after($after)
  23. {
  24. $this->after[] = $after;
  25. }
  26.  
  27.  
  28. public function renderAll()
  29. {
  30. $html = '';
  31. foreach($this->before as $key => $value)
  32. {
  33. foreach($this->tags as $key1 => $value1)
  34. {
  35. if($value == $key1) $html .= $value1;
  36. }
  37. }
  38.  
  39. $html = $html . $this->inner;
  40.  
  41.  
  42. foreach($this->after as $key => $value)
  43. {
  44. foreach($this->tags as $key2 => $value2)
  45. {
  46. if($value == $key2) $html .= $value2;
  47. }
  48. }
  49. return $html;
  50. }
  51.  
  52. }
  53.  
  54. $img = new Tag('img');
  55. $img->before('br');
  56. $img->before('p');
  57. $img->after('pc');
  58. $img->after('br');
  59. echo $img->renderAll();
Crozin
IMO jeżeli musisz to robić od podstaw samemu to jednak radziłbym wzorować się na DOMie.

  1. $img = new ImgElement('/my/image', 'alt text');
  2.  
  3. $a = new AElement('http://...');
  4. $a->appendChild($img);
  5. $a->appendChild('Click me!');
  6.  
  7. echo $a;
A struktura klas mniej-więcej:
  1. <?php
  2.  
  3. abstract class Element{
  4. protected $tagName;
  5. protected $hasEndTag = true;
  6. protected $availableAttributes = array();
  7.  
  8. protected $childNodes = array();
  9. protected $parent;
  10.  
  11. public function setAttribute($name, $value){
  12. $name = strtolower($name);
  13.  
  14. if(!in_array($name, $this->availableAttributes)){
  15. throw new Exception('...');
  16. }
  17.  
  18. $this->availableAttributes[$name] = $value;
  19. }
  20.  
  21. public function appendChild($node){
  22. if($this->hasEndTag === false){
  23. throw new Exception('....');
  24. }
  25.  
  26. if(is_string($node){
  27. //...
  28. }elseif($node instanceof Element){
  29. $node->setParent($this);
  30. }else{
  31. throw new Exception('blah blah blah');
  32. }
  33.  
  34. $this->childNodes[] = $node;
  35. }
  36.  
  37. public function render(){
  38. if($this->hasEndTag === true){
  39. $tagFormat = '<%s%s>%s</%s>';
  40. return sprintf($tagFormat, $this->tagName, $this->renderAttributes(), $this->renderChildNodes(), $this->tagName);
  41. }else{
  42. $tagFormat = '<%s %s />';
  43. return sprintf($tagFormat, $this->tagName, $this->renderAttributes());
  44. }
  45. }
  46.  
  47. public function __toString(){
  48. return $this->render();
  49. }
  50.  
  51. protected function renderAttributes(){
  52. //..
  53. return ' title="to_tylko_przyklad"';
  54. }
  55.  
  56. protected function renderChildNodes(){
  57. $html = '';
  58. foreach($this->childNodes as $node){
  59. if(is_string($node)){
  60. $html .= $node;
  61. }else{
  62. $html .= $node->render();
  63. }
  64. }
  65.  
  66. return $html;
  67. }
  68. }
  69.  
  70. class AElement extends Element{
  71. protected $availableAttributes = array('href', 'title');
  72.  
  73. public function __construct($hrefAttr = null){
  74. if(!is_null($hrefAttr)){
  75. $this->setAttribute('href', $hrefAttr);
  76. }
  77. }
  78. }
  79.  
  80. class ImgElement extends Element{
  81. protected $hasEndTag = false;
  82. protected $availableAttributes = array('src', 'alt');
  83.  
  84. public function __construct($srcAttr = null, $altAttr = null){
  85. if(!is_null($srcAttr)){
  86. $this->setAttribute('src', $srcAttr);
  87. }
  88.  
  89. if(!is_null($altAttr)){
  90. $this->setAttribute('alt', $altAttr);
  91. }
  92. }
  93. }
  94.  
  95. ?>
windman
Cytat(Crozin @ 12.09.2009, 11:13:11 ) *
IMO jeżeli musisz to robić od podstaw samemu to jednak radziłbym wzorować się na DOMie.


Dzięki wszystkim za pomoc, znalazłem rozwiazanie:

  1. public function before($tagName, $attributes = array(), $options = array())
  2. {
  3. $options = array_merge($this->options, $options);
  4. $tag = new Tag($tagName, $attributes, $options);
  5. if(count($this->before) >= 1){
  6. array_push($this->before[0]->after, $tag);
  7. }else{
  8. array_push($this->before, $tag);
  9. }
  10. return $tag;
  11. }
  12. public function after($tagName, $attributes = array(), $options = array())
  13. {
  14. $options = array_merge($this->options, $options);
  15. $tag = new Tag($tagName, $attributes, $options);
  16. if(count($this->after) >= 1){
  17. array_push($this->after[0]->before, $tag);
  18. }else{
  19. array_push($this->after, $tag);
  20. }
  21. return $tag;
  22. }

Jezeli mam tag SPAN i przed niego chce wstawic tag IMG to:
zanim do tablicy before tagu SPAN dodam nowy obiekt tagu IMG sprawdzam czy w tej tablicy juz sa jakies obiekty, jezeli tak to do odnalezionego obiektu dodaje ten nowy IMG lecz nie do tablicy before tylko after.
Troche to zagmatwane, ale logiczne i tagi generowane sa w odpowiedniej kolejnosci.

Pozdrawiam

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.