Nem titkoltan »azonnal beleszerettem« az ASP.net nyújtotta lehetőségekbe. Leginkább a code behind modell az, ami rögtön megfogott. Ezzel kapcsolatban jönne most néhány gondolat, a címből sejthető témával.
Az elején mondom, hogy akiket a kísérőszöveg nem érdekel, a kódot ide kattintva böngészhetik, nem bonyolult, nem nagy és UTF-8-as, a böngészőben színezve jelenik meg.
Klakkolás után tömény kódokkal és súlyos gondolatokkal folytatom.
PHP-ben amikor egy-két HTML elemet (leginkább legördülő listát) kicsit macerás kezelni. Tudom, hogy léteznek frameworkök, illetve kismillió megoldás, de én általában szeretek a dolgok mögé látni, megérteni azok működését, így valamelyik nap kíváncsiságból összeraktam néhány osztályt, ami akár jó alap is lehet. Vagy nem, majd elválik.
Na, de miről is van szó. Tegyük fel hogy szeretnénk egy listát, amiben a blog kategóriái jelennek meg, de úgy, hogy ha éppen egy bejegyzést módosítunk, akkor a listában az aktuális kategória legyen kiválasztva. Ehhez valószínűleg mindenki írna valami olyan kódot, amiben lenne egy adag SQL, egy adag PHP és egy adag HTML. Mondjuk így:
function getCategories($selectedID = null) { $q = query('SELECT id, name FROM cat;'); if($isResult) { echo '<select name="select_cat_id">'; while($r) { $selected = ($selectedID == $r[0]) ? ' selected="selected' : null; echo '<option value="' . $result[0] . '"' . $selected. '>' . $r[1] . '</option>'; } echo '</select>'; } else { return false; } }
Nem a legszebb megoldás, és ismételten: biztos van erre kismillió jobb út, de ez az egyik legalapvetőbb.
Oké, ezt tegyük is félre. Gondolkodjunk egy kicsit teljesen máshogy. Egyrészt objektumorientált világban, másrészt ezzel elmondtam mindent. A HTML struktúrájára tökéletesen illeszthető egy PHP-kód, mely annak OOP nyújtotta lehetőségeit kihasználva próbálja voltaképpen szimulálni egy HTML elem létrehozását. Ráadásul ne felejtsük el, hogy általában van tíz-tizenöt elem, amivel dolgozunk, ezeknek is javarészt ugyanazon tulajdonságaival. Persze a megoldás, amit most szeretnék bemutatni, inkább igyekszik univerzális lenni. Fontos azért azt is megjegyezni, hogy ezek a kódok gyakorlatilag egy agymenés után néhány perc gépelés eredményeként születtek, lenne még mit átgondolni és megtervezni rajtuk, de úgy vélem jó alap lehet.
Lépcső a Mennyországba
Legeslegelőször a CSS az, amivel foglalkozni kell, ezt ugyanis később majdnem minden használni fogja. Első körben gondolkodjunk nagyon minimális dolgokban: minden elemnek van egy style
tulajdonsága, amelybe tetszőleges CSS-t írhatunk a CSS-pedig tulajdonság-érték párokból áll. Ezek alapján akkor már csak annyi a dolgunk, hogy lefordítjuk ezt PHP-re. Íme:
class HTMLElementCSS{ private $properties = array(); private $values = array(); public function addProperty($property, $value) { if(strlen($property) > 0 && strlen($value) > 0) { array_push($this->properties, $property); array_push($this->values, $value); } } public function removeProperty($property) { $key = array_search($property, $this->properties); if($key !== false) { $this->properties[$key] = null; $this->values[$key] = null; } return false; } public function getStyle($onlyStyle = true) { if(count($this->properties) > 0) { $style = null; $i = 0; foreach($this->properties as $p) { if($p !== null) { $v = $this->values[$i]; $style .= $p . ':' . $v . ';'; } $i++; } return ($onlyStyle) ? $style : ' style="' . $style . '"'; } else { return false; } }
Mint mondtam, semmi komoly, csak a legszükségesebbek. A használata eléggé egyértelmű, ezért arra most külön nem térnék ki, talán csak annyiban, hogy ugyan az osztály példányosítható, de haszna csak valamelyik HTMLElement osztálynál lesz
És mondá az úr: hát, É emel!!
Itt is van a lényeg. Elsőre úgy gondoltam, hogy egy interfésszel kellene próbálkozni, de ez több szempontból sem bizonyult megfelelőnek, hiszen van egy rakatnyi tulajdonság (id, class, style, name
stb.), amiket jó lenne valahol először leírni, aztán később csak hivatkozni rá. Így egy absztrakt HTMLElement
osztály született, amelyet később minden elem örökölni fog.
abstract class HTMLElement { public $ID; public $style; public $class; private $name; private $value; private $innerHTML; private $selected; function setInnerHTML() { } function render($echo = false) { } }
A dolog még mindig roppant egyszerű. Persze lenne kismilliárdtrillió tulajdonság még, de elsőre csak néhányat vettem fel.
Kezdjünk bele
A következő kód az egyik legegyszerűbb és legtöbbet használt elem lesz, a bekezdés. Egy bekezdésnek van valamilyen tartalma, lehet esetleg stílusa, osztálya és azonosítója. Ezeket akár első körben rögtön meg is adhatjuk, de persze az is lehet, hogy először csak megcsináljuk azt a bekezdést, és később adunk neki mondjuk színt. (Például egy bejelentkezés űrlapjánál a hibás adatokra vonatkozó felhívást először nem jelenítjük meg, csak ha már volt egy hibás próbálkozás.) A kód:
class HTMLParagraph extends HTMLElement { public function setInnerHTML($content) { $this->innerHTML = $content; } public function render($echo = false) { $ID = ($this->ID !== null) ? ' id="' . $this->ID . '"' : null; $class = ($this->class !== null) ? ' class="'. $this->class . '"' :null; $style = $this->style->getStyle(false); $element = sprintf('
%s
', $ID, $class, $style, $this->innerHTML); if($echo) { echo $element; } else { return $element; } } public function __construct ($innerHTML = null, $ID = null, $class = null, $render = false, $echo = false) { $this->setInnerHTML($innerHTML); $this->ID = $ID; $this->class = $class; $this->style = new HTMLElementCSS(); if($render) { $this->render($echo); } } }
Első körben ha valaki ránéz, lehet hogy fejbe akar lőni. Minek ennyi sor egy árva bekezdés kiírásához? Valahol tejesen jogos, de ennek az igazi erejét akkor fogjuk látni, amikor megcsináljuk a korábban említett legördülő listát, ami pedig így néz ki:
class HTMLSelectOption extends HTMLElement { public function setInnerHTML($innerHTML) { $this->innerHTML = $innerHTML; } public function render() { $value = ($this->value !== null) ? ' value="' . $this->value . '"' : null; $selected = ($this->selected !== false) ? ' selected="selected"' : null; $element = sprintf('', $value, $selected, $this->innerHTML); return $element; } public function __construct ($value = null, $innerHTML = null, $selected = false) { $this->value = $value; $this->setInnerHTML($innerHTML); $this->selected = $selected; } } class HTMLSelect extends HTMLElement { public function addOption($value, $innerHTML, $selected = false) { if(strlen($value) > 0 && strlen($innerHTML) > 0) { $option = new HTMLSelectOption($value, $innerHTML, $selected); array_push($this->options, $option); return true; } else { return false; } } public function getOptions() { $optionValues = null; foreach($this->options as $option) { $optionValues .= $option->render(); } return $optionValues; } public function render($echo = false) { $ID = ($this->ID !== null) ? ' id="' . $this->ID . '"' : null; $name = ($this->name !== null) ? ' name="' . $this->name . '"' : null; $class = ($this->class !== null) ? ' class="'. $this->class . '"' :null; $style = $this->style->getStyle(false); $options = $this->getOptions(); $element = sprintf('', $ID, $name, $class, $style, $options); if($echo) { echo $element; } else { return $element; } } public function __construct ($ID = null, $name = null, $class = null) { $this->ID = $ID; $this->name = $name; $this->class = $class; $this->style = new HTMLElementCSS(); $this->options = array(); } }
És hoppá, a fentebb említett kód rögtön gusztusosabban néz ki, sőt, egy lépéssel közelebb vagyunk a code behind modellhez, de akár még az MVC-hez is (persze attól ez nagyon messze van, mint maga a PHP):
function getCategories($selectedID = null) { $q = query('SELECT id, name FROM cat;'); if($isResult) { $selectElement = new HTMLSelect(); while($r) { $selected = ($selectedID == $r[0]) ? true : false; $selectElement->addOption($r[0], $r[1], $selected); } return $selectElement->render(); } else { return false; } }
És így tovább.
Még mindig szeretném hangsúlyozni, hogy ezeket leginkább agymenésnek szántam, semmi komoly szándék nincs mögötte. Talán annyi, hogy esetleg egy kis fórumot
beindítana az ezzel kapcsolatos ötletekkel, gondolatokkal. Ilyen módon tehát nyugodtan jöhet bármilyen nemű hozzászólás, amíg nem megyünk el olyan irányba, hogy ez mennyire felesleges és mennyire szar, mert annak nem lenne sok értelme.
A fájl letölthető, ide kattintva is.
Lev
2010. február 20. — 21:08:39
Strike of a genius, my old chump
erenon
2010. február 20. — 21:31:24
Hát ha legyen fórum, akkor én megmondom.
Szerintem alapvetően aláássa az OOP szemléletet, hogy a HTMLElement oszálynak van id, style, class, name, satöbbi mezője. Én a következőt képzelném el:
HtmlElement{
public:
__construct($tag, $selfclose = false);
addParameter(HtmlElementParameter $parameter);
removeParameter(HtmlElementParameter $parameter);
render();
}
HtmlElementParameter{
public:
__construct($name, $value);
render();
}
(Kicsit csaltam a szintaxissal, nem akartam nagyon terjengős lenni.) Ezektől az osztályoktól pedig születhetnek HtmlElementOption, stb osztályok.
erenon
2010. február 20. — 21:41:33
Bocsánat, két lényeges dolgot kihagytam:
Egyrészről a fenti példa HtmlElement osztálya rendelkezik a következő két public methoddal:
addChild(HtmlElement $parameter);
removeChild(HtmlElement $parameter);
Valamint magyarázatként a két osztály a render metóduson keresztül megvalósítják a Composition (Összetétel) mintát.
Mefi
2010. február 20. — 21:43:48
[re=6059575]erenon[/re]: vissza lehetne ennyire bontani, jogosan, ez tipikusan tervezés kérdése. Meg pl. a meghívás is lehetne virtuális, és négy-öt sort lehetne spórolni renderelésenként.
Split
2010. február 20. — 21:44:11
Ncore-on, mint eredetiséget igazoló link jelentél meg: http://www.imgupload.org/images/658_clipboard01.jpg
Mefi
2010. február 20. — 21:49:02
[re=6059576]erenon[/re]: amúgy ilyenkor érezni kicsit a php gyengeségeit
[re=6059578]Split[/re]: lol, kösz.
erenon
2010. február 20. — 21:51:18
[re=6059577]Mefi[/re]: Szerintem részben ez az OOP lényege, hogy az entitások közös elemeit egyre absztraktabb osztályokba gyűjtjük, amikből szükség szerint konkrétakat származtatunk. Bár te is sokszor említetted, én is megerősíteném, hogy HTML generálásra valószínűleg nem használnék ilyen aprólékos PHP kódot, a megvalósítás számos hátránya miatt (nagyon lassú, a legtöbb elemnél nincs szükség rá).
Ezt a virtuális meghívást nem értem. Elmagyaráznád?
Kokan
2010. február 20. — 21:52:28
Régebben elkezdtem tervezni valami hasonlót, aztán a lekódolásnál valahogy félbemaradt :S
Mefi
2010. február 20. — 22:01:14
[re=6059580]erenon[/re]: arra gondolok hogy az ismétlődő kódokat, amik a renderelésénél biztosan előjönnek (ugye nem minden elemet kell ugyanúgy renderelni, erre jó példa lehet pont az option), az egyik ősosztályban ki lehetne fejteni, és itt lehetne kihasználni a virtualizációt. Máskülönben lassú és körülményes, de ezt megfelelően kiépítve végső soron szerintem gyorsabb, mert ha megnézed az ASP.net-et, ott az is hasonlóképpen lesz . Persze a PHP nyelvi adottságai nagyon sok mindent megnehezítenek vagy -akadályoznak, ahogy picit mélyebben beleástam magam a Java és a C#-.net világba, pontosan ezért kezdtem el megkedvelni, pedig a Javával nagyon nehéz kör volt.
HunTeR
2010. február 20. — 22:02:22
Talán nem szeretik bonyolítani a dolgokat. :]
Egyébként érdekes megoldás. Beindítja az ember fantáziáját.
erenon
2010. február 20. — 22:07:40
[re=6059582]Mefi[/re]: Nem igazán értem, miről beszélsz. A selectet és az option gyerekeit pont úgy kell generálni, mint az összes többi HTML elemet. Ha lesz néhány pillanatnyi időm, feldobok valahova néhány sort, és megvitatjuk ha gondolod, mert tényleg érdekel, hogy a PHP milyen hiányosságaira gondolsz.
(Kétségtelenül van neki, nem is kevés, de én ezt nem érzem itt olyan durván)
Maerlyn
2010. február 20. — 22:26:05
Szép dolog az `echo $object->render()` hívás, de mennyivel szebb az `echo $object` – csinálj __toString() függvényt, és meg is vagy. Elég annyi a törzsbe, hogy `return $this->render();`.
Mefi
2010. február 20. — 22:39:07
[re=6059583]HunTeR[/re]: szerintem az ilyen nagyon összegyúrt dolgok sokszor inkább bonyolítják. Én jobban élvezném ha pl. az SQL-kód leírása kimerülne a tárolt eljárás meghívásában, a kiírás meg a sablonkezelőnek való átadásban. Persze ez nem lehetetlen. 🙂
[re=6059584]erenon[/re]: nem feltétlenül. Egy p-t meghívhatsz úgy, hogy a konstruktor már rögtön ki is írja, míg egy optionnél ez fölösleges, mert ugye egyrészt a selectbe mehet csak, másrészt pedig mi értelme rögtön kiírni. A hátrányait itt még annyira nem, de azért ennél összetettebb feladatoknál erősen érezni; korábban nem értette mikor valami ilyet mondott, hogy miért lehet kevés a PHP, de mostanában egyre jobban kezdem. Gondolok például arra, hogy PHP-ben egy collection már nem vicces annyira.
[re=6059585]Maerlyn[/re]: $this->render(true); 🙂
Maerlyn
2010. február 20. — 23:49:14
Meg fogod unni a -> render hivogatást, és rájössz, milyen jó a __toString() 🙂
erenon
2010. február 21. — 00:04:52
Ezt dobtam össze 20 perc alatt:
http://pastie.org/834729
És ajánlanám még ezt:
http://hu.php.net/domelement
Mefi
2010. február 21. — 00:33:28
[re=6059590]Maerlyn[/re]: annyira nem vészes szerintem, áttekinthetőbb, meg én az egyetlen echo híve vagyok, ha lehet. Viszont köszi a tippet. 🙂
[re=6059593]erenon[/re]: zsír, köszi. Mondjuk megnézném hogy mennyire gyors ezzel, mennyire lassú, mennyire kényelmes és így tovább.
erenon
2010. február 21. — 01:15:26
Felraktam gitHubra is, így talán könnyebben megy a belenyúlkálás, hibajavítás, ötletek hozzáadása.
http://gist.github.com/309999
sajt
2010. február 21. — 09:48:10
Szerintem ez hülyeség, a forráskód meg hiába UTF-8, ha iso-8859-2-vel akarja megjeleníteni.
Kicsit bővebben: Szerintem ezt az egész html-t valamilyen template-engine-nek (ami nálam mostanában php) kell megjeleníteni. Az ilyen kódok bár szépek, egy idő után visszakövethetetlenek.
thgab
2010. február 21. — 11:18:10
[re=6059595]erenon[/re] megoldása azért közelebb áll az OOP szemlélethez. Engem leginkább a natív javascript, html elem generálására emlékeztet. OOP példának nem rossz. De
osztom [re=6059597]sajt[/re] véleményét. A template generálására vannak kész, jól használható megoldások. Némelyik még selectet is képes generálni a selected optionnel együtt. 🙂
Eléggé nehézkes a html tartalom, ilyetént történő generálása. Mi van akkor ha változik az oldal megjelenése? Hány helyen kell új tageket generálni vagy a meglévő attribútumait módosítani? Átláthatatlan a megjelenítés.
A lényeg az, hogy ha csak a megjelenítés változik, ne kelljen a php kódot átírni.
thgab
2010. február 21. — 11:19:52
Ja! És hacsak nincs valami html cachelés. Gyanítom, hogy a html generálása is elég lassú lesz.
Mefi
2010. február 21. — 13:53:22
[re=6059597]sajt[/re]: ha normalisan generalsz hozza htmlt, UTF-8 lesz az, meg ha view -> character encoding -> UTF-8 akkor is jo.
[re=6059598]thgab[/re]: a PHP-t (ugy ertem hogy nem a megjelenitesert felelos PHP-t) nem kell modositani, pontosan ez a lenyeg. Valami kod adja az adatot, egy masik meg abbol a HTML-t.
sajt
2010. február 21. — 14:24:27
[re=6059603]Mefi[/re]: Arra gondoltam, hogy a forrásfile-nál rosszak az ékezetek (aheaderben rossz a charset.) Egyébként honnan tudod, hogy milyen böngészőt, használok?
Mefi
2010. február 21. — 15:07:39
[re=6059604]sajt[/re]: én mindent tudok 🙂
sajt
2010. február 21. — 23:54:32
[re=6059605]Mefi[/re]: Akkor már ketten vagyunk.
Mefi
2010. február 22. — 01:04:56
[re=6059611]sajt[/re]: 😀
thgab
2010. február 22. — 10:39:45
[re=6059603]Mefi[/re]: szerintem tutira, mivel PHP-vel generálod a tageket.