Abstrakce nad poli a objekty v Latte
- Honza Marek
- Člen | 1664
O víkendu jsem si hrál se Symfony, kde je šablonovátko Twig. Líbí se mi tam, že mi může v šabloně jedno, jestli datový objekt je stdClass, pole nebo objekty s gettery, ke všemu se přistupuje stejně přes tečkový operátor.
Některé zápisy by šly pěkně zestručnit.
{$control['form']['input']->getLabel()}
{* případně díky Nette\Object ->label *}
by šlo napsat v Twigu asi takto:
{{ control.form.input.label }}
V případě pole je funkčnost jasná, v případě objektu se postupuje cca takto:
- kouknu, jestli existuje property
- kouknu, jestli existuje metoda getProperty (není potřeba kvůli šablonám dědit od Nette\Object)
- kouknu na ArrayAccess
Šlo by něco takového implementovat i do Latte? Bylo by asi potřeba najít jinou syntaxi, protože tečky kolidují se spojováním řetězců (možná šipky?).
- Mikulas Dite
- Člen | 756
Dobré zjednodušení, ale jako další filter, asi ne do rovnou latte.
Latte se snadněji překládá do php. Ale protože Object umí převádět
getteru na „property“, teoreticky by stačilo ještě před latte přidat
filter, který by nahradil ty tečky za ->
. Akorát čistý
array
by se musel ošetřit, nebo podobné, které nemají jako
předka Object
.
- Filip Procházka
- Moderator | 4668
Stačilo by přidat nějakou utilitku na „cestování objektem“ a předpřeložit filtrem
{$neco.tralala.tralalay}
popř.
{$neco->tralala->tralalay}
na
{=Nette\ObjectTravel::in($neco)->using('tralala', 'tralalay')}
Dobrý nápad, fandím :)
- Petr Motejlek
- Člen | 293
O něčem podobném jsem přemýšlel už minulý týden. Hrál jsem si zrovna s Tapestry a tam se mi líbilo, že při tom traverzování to i samo hlídá, aby někde nevyskočila NullPointerException, kdybych náhodou volal něco nad ničím ;).
Ten nápad s tím automatickým voláním getterů/setterů, příp. přístupu k poli, je docela zajímavý, ale nemlžu se zbavit dojmu, že to přináší hodně velký WTF faktor. Pokud má objekt proměnnou, tak použiju tu. Pokud nemá proměnnou, ale má getter, použiju getter. Když nemá ani jedno, je otázka, jestli by to mělo umřít s chybou, nebo jen vrátit prázdný řetězec. Když do toho ještě přidáš kontrolu, jestli jméno tý proměnný náhodou není index pole, bude to divné.
Chtěl jsem něco, co by hlídalo, aby nedošlo k volání metody/přístupu k proměnné nad null, udělat sám. Rozšířit to o automatické volání getterů by bylo cool. Pokud chceš, můžeme se domluvit na nějaké koordinované spolupráci, abychom zbytečně oba nedělali to samé ;).
- Vyki
- Člen | 388
Osobně bych byl pro spojování tečkou. Každému pak dojde, že se jedná o něco magického, protože to v PHP jinak nelze. Určitě bych zavrhl šipky, které v PHP mají své pevné místo a pro tento účel by se to chovalo naprosto neočekávaně, každý přeci předpokládá, že zápis
{$neco->tralala->tralalay}
udělá i v šabloně přesně to co by udělal v čistém PHP. Byla by to magie na druhou.
- newPOPE
- Člen | 648
No ale ked si uvedomis ze si v sablone resp. v Latte tak by to malo byt jasne.
Co sa tyka magie, tak ta je tam (myslim najvacia) ze budem pisat
aaa.bbb.ccc
a je mi jedno ci je to pole, objekt etc.. (A ako
predpokladam, tak opat u nas v praci tomu nebude nik rozumiet :-D)
Ale keby sa to tykalo napr. vykreslovania formov tam je to velka pomoc.
- PJK
- Člen | 70
Udělal jsem si malý prototyp, separarátor si můžete změnit.
https://gist.github.com/919240
Použití:
{~var->bla->bla}
//totozne s
{~$var->bla->bla}
(Technické náležitosti jsem obšlehl z https://github.com/…rmMacros.php, rady a připomínky jsou vítány)
Předělal jsem si pro to pár templatů a jsem spíš pro klasickou šipku ->, protože mi to připadá víc čitelné.
Samozřejmě by se pak přidělaly všechny standardní funkce pro výpis proměnných (escapování atd).
- Filip Procházka
- Moderator | 4668
Ó můj bože, to chceš ten kód kopírovat do každé šablony na každý výskyt? :) Zkus to dát do té třídy a nahrazovat macro pouze statickým voláním
- Mikulas Dite
- Člen | 756
Object může implementovat array access, asi by bylo lepší dat první podmínku
if ($current implements ArrayAccess || is_array($current)) {
// ...
} else if (is_object($current) {
// ...
}
- PJK
- Člen | 70
HosipLan napsal(a):
Ó můj bože, to chceš ten kód kopírovat do každé šablony na každý výskyt? :) Zkus to dát do té třídy a nahrazovat macro pouze statickým voláním
Nojo, já bych rád, nejsem na tohle nijak hrdej, jenže Latte rozumím na úrovni uživatele. Chtěl jsem to tak samozřejmě udělat, jenže jsem narazil na takový problém: Když jsem tam dal jen callback do té třídy, tak jsem se zaboha nedokázal dostat k hodnotě té proměnné. Pokud víš, jak na to, tak by mě to dost zajímalo :)
- Mikulas Dite
- Člen | 756
Ještě jsem to forknul, malinko přiupravil a přidal kontrolu, jestli to
existuje, ať to vždy raději háže tu InvalidStateException
než
notice. https://gist.github.com/919433
- Filip Procházka
- Moderator | 4668
To je aspoň spolupráce panečku! :) Kdyby se takhle nadšeně psala dokumentace, kde bysme dneska byli :)
- David Grudl
- Nette Core | 8218
Kdysi jsem nad tím přemýšlel, ale nikoliv z důvodu úspory syntaxe (srovnejte:
{$control[form][input]->label}
{$control[form-input]->label}
{{ control.form.input.label }}
ale kvůli výkonu. Tedy při výskytu
$a->b->c->d->X
by se uložilo do pomocné proměnné
$a->b->c->d
a další
$a->b->c->d->Y
by se zpracovalo řádově rychleji.
Obecně jsem tedy pro (byť z jiného důvodu), ale chce to dobře promyslet syntax.
- Pavel Kouřil
- Člen | 128
Co se týče syntax, tak buď {{ control.form.input.label }}
nebo klidně třeba {.control.form.input.label}
; ale nevím, zda by
ta tečka na začátku nekolidovala nějak?
- na1k
- Člen | 288
Pajka, myslím, že ta tečka na začátku by se velice snadno přehlédla/zapomněla
…
A jak by fungovalo odlišení v případě, že bych chtěl například label nějakého form. prvku (čistě teoreticky) použít jako parametr odkazu? Teď tuším funguje to, že pokud jde o řetězec, nemusí kolem něj být uvozovky. Ta tečková notace by pak lehce zaváněla konkatenací.
Tím neříkám, že se mi tečková notace nelíbí, ale neměla by být matoucí při použití v současné syntaxi. A přiznávám se, že ani nevím co všechno si můžu v Latte dovolit a tak často zkouším a většinou mi to projde :) Tak jen aby s touhle benevolencí nebyl nakonec ještě problém při přidávání nových věcí :-p
- PJK
- Člen | 70
Upravil jsem https://gist.github.com/919240
- Obsahuje všechna vylepšení a opravy, opravuje chybu v https://gist.github.com/919433, l65
- Upřednostňuje getter před přímým přístupem k property (měl by? zajímal by mě váš názor)
- Přidal jsem obdobu !$ makra. (!~$ nefunguje, vyžadovalo by úpravy v parseru (https://api.nette.org/…ser.php.html#422), aby rozeznával i delší speciální jména, nicméně pro zachování konzistence jsem ho přidal).
- Zajímalo mě, jestli je cachování referencí opravu o tolik rychleší, hrubé testy ukazují, že rozdíl je opravdu velmi výrazný: https://gist.github.com/944108
A nyní… FLAMETIME!
Syntaxe:
Pokusím se shrnout všechny možné přístupy
- přidání nového macra {~$var->prop->bla|helper} (vězte, že tildu jsem zvolil naprosto náhodně a může to být cokoliv jiného)
- zavedení standardního univerzálního operátoru přímo do celého latte: {$a.b.c}
- navrhovaná syntaxe dvojitých závorek {{$a.b.c|helper}}
- nějaký další filtr a úplně nová syntaxe. Napadá mě třeba
{
`$a.b.c
`|helper}, což se přeloží třeba na {$a->b->getC()|helper} a dál zpracuje standardně latte
U druhé možnosti mluvím o „celém latte“. Kdybychom chtěli zavést jednotý operátor, pak by nejspíš bylo žádoucí využívat ho skuečně všude – cykly, podmínky, linky, atd… Podobná možnost je i u dvojitých závorek, čímž by se zachovali obě možnosti zápisu:
//klasicka
{if $a->b['c']}
bla
{/if}
//nova
{{if $a.b.c}}
bla
{{/if}}
Stejné vlastnosti má i poslední nápad, navíc by asi výrazně zjednodušil a zrychlil zpracování.
Co je potřeba rozhodnout:
- Chceme tenhle vylepšovák zavést jako (jedninou možnou syntaxy v celém latte | volitelnou syntaxi v celém latte | nové makro, tedy jen pro výpis)?
- Jaký operátor použít
- Detail zvoleného přístupu
Myslím, že by bylo rozumné shodnout se na první otázce a pak teprve pokračovat dál.
Můj osobní názor je takový, že současná syntaxe není nijak špatná, tudíž bych to integroval hlouběji do latte jen společně s cachováním těch referencí, jinak bych si to nechal jako šikovné rozšiřující makro. Upřímně, nejsem nijak nadšený z vyhlídky dalších větších změn, ale tohle by mohlo stát za to. (Ono by to dost slušně šlo i teď, málokdo v šablonách střídá různé typy zápisu (getter/property, []/getOffset, atd.))
Editoval PJK (27. 4. 2011 16:18)
- Nox
- Člen | 378
Dal bych asi do celého, jen pro výpisy by to nepomohlo až tolik a navíc
takto by měl zbytečně člověk všude různé zápisy – narozdíl od {} vs
n: vynuceně pokud by chtěl tuto abstrakci používat
(podobně ~, {{}} atd.)
Předpokládám že nevýhoda varianty „univerzálně a volitelně“ je jen výkonnostní – jedná se ale jen o překlad do šablony, takže defakto jednorázově a neměl by to IMHO být problém? V takovém případě bych byl pro tuto variantu – asi nejelegantnější
Akorát mě napadá jak to udělat aby se to nepletlo s PHP tečkou..aktuálně {if $a.„1“ == „abc1“} je možné, že?
Editoval Nox (27. 4. 2011 16:08)
- Honza Marek
- Člen | 1664
Rozhodně bych byl pro zabudování přímo do latte, aby to šlo použít i v podmínkách a podobně, nejen pro výpis.
- veena
- Člen | 98
Pro inspiraci: tečková notace se používá i v templatovacím jazyku v djangu. Viz http://docs.djangoproject.com/…s/templates/#…
Hledá to postupně jestli ta proměnná je:
- index v asociativním poli (slovník v pythonu)
- atribut objektu
- metoda (volání metody bez parametrů)
- index číslovaného pole (list v pythonu)
Je to intuitivní a zapamatovatelný.