Začátečník: sestavování stránky

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Peetee
Člen | 75
+
0
-

Ahoj,

snažím se proniknout do Nette, ale moc mi to nejde. Mám podobný problém jako Flipajs, ale zatímco on už prozřel, já se v tom stále plácám, myslím, že jednotlivosti chápu, ale mám problém jak funguje celek, proto jsem vás chtěl poprosit o pomoc.

Učím se příkladem, zkoumám příklad CD-collection z distribuce a chtěl bych, aby se na každé stránce zobrazil nějaký další obsah, nějaký další presenter (pokud dobře rozumím terminologii) nebo další instance současného – třeba, aby na každé stránce byly vypsány všechny alba (na stránce s výpisem alb můžou být klidně dvakrát).

ještě doplním, že seriál na zdrojáku jsem četl, četl jsem Quick Start, několik vláken tady na foru, a viděl videa…

Ondřej Mirtes
Člen | 1536
+
0
-

Ahoj,
v tomto případě patří výpis všech těch alb do layoutu a naplnění té proměnné do metody beforeRender v BasePresenteru.

Např.

protected function beforeRender() {
	$model = new AlbumModel;
	$this->template->allAlbums = $model->getAllAlbums();
}

A @layout.phtml:

<ul>
{foreach $allAlbums as $album}
	<li>{$album->name}</li>
{/foreach}
</ul>
Peetee
Člen | 75
+
0
-

Moc děkuju za radu, opravdu mi to pomohlo. Při řešení, jsem přišel ještě na několik otázek:

  1. Dalo by se z toho odvodit, že pokud bych si chtěl na stránku přidat spoustu bloků (např. poslední komentáře, RSS čtečku, poslední články… (to přece není podstatné)), takže je dobrý nápad, vytvořit si všechny „moduly“ do BasePressentr do beforeRender?
  2. V uvedeném postupu se formátování provádí přímo v šabloně @layout.phtml, co kdybych chtěl použít šablonu daného pressenteru? Asi nejlepší je demonstrovat to na příkladu, zde je co jsem upravil podle Ondry – je to úprava příkladu z CD-collection:

úprava DashboardPresenter.php, přidal jsem následující funkci:

<?php
     public function Pokus2()
     {
         $album = new Albums;
         return  $album->findAll()->orderBy('artist')->orderBy('title');
     }
?>

v BasePressenteru v beforeRender jsem přidal:

<?php
        $m2 = new DashboardPresenter;
        $this->template->test2 = $mode2->Pokus2();
?>

a konečně v @layout.phtml

<h1>Pokus č.2</h1>
<ul>
{foreach $test2 as $album}
    <li>{$album->title} - {$album->artist}</li>
{/foreach}
</ul>

Ale co kdybych chtěl, aby se to naformátovalo pomocí šablony? – v tomto konkrétním případě, pomocí Templates/Dashboard/Default.phtml

Jaktože, když vlastně zavolám fci renderDefault tak se to formátuje pomocí této šablony?

Ondřej Mirtes
Člen | 1536
+
0
-

K tomuhle, co píšeš, by měly sloužit komponenty – umožňují ti použít samostatné soubory jako šablony. Do BasePresenteru pak už umístíš jen továrničku dané komponenty…

class LastComments extends Control {

	public function render() {
		$template = $this->createTemplate();
		$template->setFile(dirname(__FILE__) . '/template.phtml');

		$model = new CommentsModel;
		$template->comments = $model->getLastComments();

		$template->render();
	}

}
{* template.phtml *}

{foreach $comments as $comment}
	{$comment}
{/foreach}

Aha, teď jsem si všim, že možná myslíš něco trochu jiného – jestli Dashboard/default.phtml má být cesta k šabloně nějaké action Presenteru (konkrétně asi Dashboard:default). K tomu slouží dědičnost bloků šablon. Ty si můžeš v „nadšabloně“ (= layoutu) definovat vzhled nějakého bloku (např. {block #lastComments}), ale pokud se dostaneš na nějakou stránku, která daný blok předefinovává (tzn. v šabloně action – tj. Dashboard/default.phtml je uveden {block #lastComments}), tak se přepíše touhle definicí ten nadřazený.

Editoval Ondřej Mirtes (5. 2. 2010 14:32)

Peetee
Člen | 75
+
0
-

Připadám si dost hloupě, ale pořád to nechápu, celý den jsem věnoval studiu dokumentace, video o nových šablonách jsem si pustil pro jistotu dvakrát :-) ale nějak nechápu praktické použití, omlouvám se nejspíš je to strašně triviální, ale prostě to tam nevidím… mám k tomu jednu konkrétní otázku:

  • v příkladu CD-collection je v @layout.phtml
{include #content}
  • kde je nastaveno co bude v bloku Content resp. blok content se objevuje v několika šablonách – jak systém pozná kterou má vykreslit ?
  • jak si „zaregistrovat“ svůj vlastní blok? co když chci do @layout.phtml vložit :
{include #pokus}

Ondro: děkuju za pomoc, v minulém příspěvku píšeš, že jsem tím možná myslel něco jiného – vlastně ani nejde o to co jsem tím myslel, hlavním cílem mých otázek je, abych pochopil, jak vystavět stránky na Nette Framework™, jak složit stránku z jednotlivých „bloků“/„částí“ – třeba v uvedeném příkladu s CD-collection je fakt pěkně vidět jak vystavět jednu část, ale není mi jasné, jak na stránku dát takových částí víc

Ondřej Mirtes
Člen | 1536
+
0
-

Jaká šablona se použije záleží na tom, na jakou dvojici Presenter:action jdeš. Pokud jdeš na Dashboard:default, vykreslí se Dashboard/default.phtml, atd…

A vlastní blok zaregistruješ tak, že ho definuješ (v té šabloně):

{block #pokus}
text…
{/block}

A v takovéto podobě se vykreslí tam, kde v nadřazené šabloně máš {include #pokus}.

Peetee
Člen | 75
+
0
-

No, vložím sem co jsem vyplodil – a rovnou řeknu výsledek: Call to undefined block ‚pokus‘. :-(

v @layout.phtml jsem přidal jen:

{include #pokus}

v app/Templates jsem vytvořil složku pokus a do ní vytvořil soubor default.phtml – tj. výsledná cesta je: app/Templates/Pokus/default.phtml a do něj jsem vložil:

{block #pokus} <h2>tento text je šabloně!!</h2> {/block}

a v neposlední řadě jsem udělal vlastní prezenter v složce presenters soubor pokus.php:

<?php
class Pokus extends BasePresenter
{
    public function renderDefault()
    {
	//nic nevrátím, je to přece jen pokus...
    }
    public function renderTest()
    {
        return "toto je testovací text";
    }
}
?>

Mám takový divný pocit, že se snažím udělat z komára velblouda, přitom zároveň objevit Ameriku…

  • Proč mi to píše, že to nemá nadefinovaný blok pokus, vždyť ten blok existuje?
  • musím někde vytvářet instanci třídy Pokus? (kde?)
Ondřej Mirtes
Člen | 1536
+
0
-
  1. Třída se má jmenovat PokusPresenter a být umistěna v PokusPresenter.php. V tom, jak to máš teď, ses na action Pokus:default dostat nemohl, nenašlo by to ten presenter.
  2. Tím, že uvedeš {include #pokus}, tak vynucuješ po každé šabloně každého Presenteru, aby block #pokus definovala (a nejspíš v šabloně Default.default.phtml ten block definovaný nemáš). Pokud chceš tuto definici dát jako volitelnou, uveď v layoutu {block #pokus}{/block}.
  3. Instanci Presenter nikde sám vytvářet nemusíš.

Ke studiu doporučuji seriál na Zdrojáku.

Editoval Ondřej Mirtes (3. 2. 2010 20:42)

Ola
Člen | 385
+
0
-

Důležitý je, že obsah @layout.phtml se vykoná vždy. Tedy pro všechny presentery (pokud jeho renderování nevypneš samozřejmě). Ty, při přístupu na stránku spustíš HomepagePresenter, tedy presenter, který opravdu blok pokus nemá. K tvému kódu mám jednu výhradu – je třeba jméno třídy uvádět jako {Název}Presenter – v tvém případě PokusPresenter. Také je potom potřeba přistupovat ke stránce /pokus/ – v případě výchozích rout.

Dále si dej pozor na to, že jména souborů a složek v linuxu jsou case-sensitive – tedy přejmenuj si app/Templates na app/templates.

Peetee
Člen | 75
+
0
-

Díky za info, opravil jsem název preznteru na PokusPresenter a přejmenoval jsem, název souboru… blok „pokus“ to stále nenašlo…

tak jsem bootstrap.php změnil výchozí rout (když není v URL nic) z:

$router[] = new SimpleRouter('Dashboard:Default');

na:

$router[] = new SimpleRouter('Pokus:Default');  ;

a tentokrát to nenašlo blok „content“ – to zní logicky, protože pokud použiju můj pokusný prezenter, tak se tak mi chybí blok content… pokud jsem {include #content} odstranil z @layout.phtml, tak to fungovalo dobře – vypsalo to „tento text je šabloně!!“ (viz. můj minulý příspěvek)

Což mě přivádí k otázce na začátku – jak na jedné stránce zobrazit „hlavní obsah“ – to je to co tam je normálně v příkladu CD-collection (proto si pořád hraju s tímto příkladem) – tedy „hlavním obsahem“ rozumím #content (výpis alb, editace alba, vytvoření nového alba) a zároveň na každé stránce (proto mám tendenci to psát do #layout) zobrazit další bloky (třeba pro začátek text „tento text je všabloně!!“, v budoucnosti další možnosti (třeba poslední komentáře, RSS čtečku, poslední články)

Ještě doplním, že seriál na zdrojáku jsem opravdu pozorně četl, pochopil jsem z něj spoustu jednotlivostí, ale stále nerozumím tomu, jak složit stránku z více „částí“ – ještě jednou děkuju za rady a trpělivost.

Ondřej Mirtes
Člen | 1536
+
0
-

A přecházíš z toho Dashboardu vůbec na Pokus:default?

BTW: Názvy actionů mají být malým písmenem! Dashboard:default, Pokus:default!

Moc nerozumím otázce – co ti brání tam ty bloky vypisovat? Chceš tam mít prázdné připravené bloky #rss, #lastComments apod., a nechávat na podřazených šablonách, jestli je něčím naplní?

22
Člen | 1478
+
0
-
Peetee
Člen | 75
+
0
-

Ahoj, děkuju za rady, asi jsem se do toho strašně zamotal, pokusím se to vysvětlit jasněji, omlouvám se, možná to bude trošku delší, ale hledám pouze nějakou best practice, jak na stránce zobrazit hlavní obsah (tady je ten blok #content) a víc „vedlejšího obsahu“.

Na konkrétním příkladu zas tak nezáleží, ale myslím, že tak se domluvíme nejlépe, proto mým cílem je upravit příkald CD-collection, aby se na každé stránce byl zobrazen hlavní obsah (jako je to ve výchozím: např seznam alb, vytvoření alba, editace alba, smazání alba) a jeden nebo víc „výstupů“ z odalších „objektů“, nějak předpokládám, že ty „výstupy“ by měly být formátované přes spec šablonu, z čehož tuším, že by, pro přehlednost měly být zapsány do samostatné šablony…

Tak, teď nevím, jestli jsem se do toho nezamotal ještě víc, ale Jak na jedné stránce zobrazit prezenter alb a následujíc text z PokusPrezenteru, z šablony: Pokus/defalut.phtml:

{block #pokus} <h2>tento text je šabloně!!</h2> {/block}

Ondřej Mirtes:

A přecházíš z toho Dashboardu vůbec na Pokus:default?

tomuhle přesně nerozumím, můžeš to prosím upřesnit? (předpokládám, že ne, protože mé jediné úpravy jsem zde důsledně zobrazil)

BTW: Názvy actionů mají být malým písmenem! Dashboard:default, Pokus:default!

omlouvám se, tohle ještě musím dostat do krve, ale na svoji obhajobu musím říct, že tohle je vyloženě testovací projekt, který prostě jen zkouším

Moc nerozumím otázce – co ti brání tam ty bloky vypisovat? Chceš tam mít prázdné připravené bloky #rss, #lastComments apod., a nechávat na podřazených šablonách, jestli je něčím naplní?

Teďka přesně nerozumím já… budu předpokládát, že do @layout vložím bloky #rss, #lastComments apod. ale mám k tomu následující otázky:

  • jak tyto bloky naplnit?
  • má je naplnit výchozí pressenter (v tomto případě DashboardPresenter)? (ale vždyť se třeba zobrazení rss vůbec netýká prezenteru, který se stará o práci s alby
  • nebo je má naplnit nějaký jiný objekt? kde ho zavolat? v BasePresenteru? (ale tam jsme se už bavili o tom, že k tomu má sloužit něco jiného)

pro 22: tento tutoriál jsem zkoušel a prolezl skrz naskrz, myslím, že je to jasný, ale důvod proč mám pocit, že to „není to co hledám“ je, že sestavení menu je napsáno v BasePresenter.php, ale co kdyby sestavení toho menu bylo složitější? nebo co kdybych chtěl na každou stránku zobrazit víc „postraních bloků“? to by mi BasePresenter.php strašně nakynul – navíc ve svém třetím příspěvku jsem se ptal, jestli je dobré tuto logiku vkládat do BasePresenter.php a pokud jsem to dobře pochopil, tak to není dobrý nápad. Takže v tutorialu je tento text v BasePresenter v funkci beforeRender():

<?php
        $this->template->menuItems = array(
            'HomePage:' => 'Domů',
            'Products:' => 'Produkty',
            'Contact:' => 'Kontakty',
}
?>

Ale nějak cítím, že složitější logiku není vhodné psát sem, ale je vhodné ji vložit do samostatného souboru – a mám nutkavou potřebu si z toho udělat samostatný prezenter – který prostě zavolám a nemusí mě zajímat, jak se to menu vlastně vytvoří.

Další bod je soubor @layout.phtml – kde se menu zobrazí kódem:

<ul>
{foreach $menuItems as $id => $item}
    <li>{$item}</li>
{/foreach}
</ul>

Tak opět cítím, že zobrazit zde víc „vedlejšího“ obsahu sem nepatří, zbytečně by to natáhlo soubor @layout.phtml a ztratil by na přehlednosti, proto mám opět potřebu tuto vykreslení menu přesunout do samostatného souboru, proto mám tendence vkládat nějaké {include #pokus}.

Ondřej Mirtes
Člen | 1536
+
0
-

Na konkrétním příkladu zas tak nezáleží, ale myslím, že tak se domluvíme nejlépe, proto mým cílem je upravit příkald CD-collection, aby se na každé stránce byl zobrazen hlavní obsah (jako je to ve výchozím: např seznam alb, vytvoření alba, editace alba, smazání alba) a jeden nebo víc „výstupů“ z odalších „objektů“, nějak předpokládám, že ty „výstupy“ by měly být formátované přes spec šablonu, z čehož tuším, že by, pro přehlednost měly být zapsány do samostatné šablony…

Těmi jinými objekty myslíš asi komponenty a na to jsem ti odpovídal hned nahoře, viz 4. příspěvek. Jestli máš nějaký dotaz, jak si napsat a rozchodit komponentu, sem s ním :)

Přechod na Pokus:default – no jestli jsi tam přešel odkazem (který jsi zapsal do šablony jako {link Pokus:default}), jestli máš v adresním řádku (v případě SimpleRouteru nastaveného na Dashboard:default) něco jako localhost/?presenter=Pokus.

Teďka přesně nerozumím já… budu předpokládát, že do @layout vložím bloky #rss, #lastComments apod. ale mám k tomu následující otázky:

jak tyto bloky naplnit?

V šabloně presenteru (např. Pokus/default.phtml) napíšeš {block #rss}obsah{/block}. V layoutu se na něj odkážeš přes {include #rss} (čímž vyžaduješ po každé šabloně presenteru, aby tento blok definovala) anebo přes {block #rss}{/block} (čímž definuješ prázdný blok a umožníš, aby ho nějaká šablona presenteru přepsala svým vlastním obsahem – ale nevyžaduješ po každé šabloně presenteru, aby ho definovala). Ale to jsem tady už taky popisoval.

má je naplnit výchozí pressenter (v tomto případě DashboardPresenter)? (ale vždyť se třeba zobrazení rss vůbec netýká prezenteru, který se stará o práci s alby

Proměnné pro bloky naplňuješ v tom presenteru, kterého je ta šablona. Je nesmysl, abys ty proměnné naplňoval v nějakém jiném presenteru :o)

Pokud stále nerozumíš, uveď konkrétní případ, jak chceš, aby se stránka chovala a vypadala, klidně i něco nakresli :o) Protože mi přijde, že omýláme to samé stále dokola, ale nemůžeme se dobrat výsledku.

a mám nutkavou potřebu si z toho udělat samostatný prezenter

Takhle se v Nette neuvažuje. Presenter je samostatná jednotka pro vykreslení kompletní sekce nějakého webu – např. Aktuality, Články, Registrace, Fórum. Pokud chceš mít na každé stránce vykresleno menu, rozhodně se k tomu účelu nevytváří MenuPresenter, to je nesmysl. Pokud nechceš mít tím kódem menu zaplácaný layout, vytvoříš si komponentu, do BasePresenteru dáš její továrničku (metoda createComponentMenu) a v layoutu jen umístíš {widget menu}, čímž tu komponentu vykreslíš.

vykreslení menu přesunout do samostatného souboru, proto mám tendence vkládat nějaké {include #pokus}.

Bloky slouží k něčemu trochu jinému. Ty kdybys v layoutu napsal nějaké {include #menu}, tak vlastně chceš, aby každá sekce webu, každá akce, definovala svoje vlastní menu – protože bys pak musel v každé šabloně (každičkém .phtml souboru v app/templates) nějak definovat {block #menu} a něco do něj napsat. Jenže ty chceš mít na každé stránce to samé menu… a k tomu slouží komponenty.

Editoval Ondřej Mirtes (4. 2. 2010 11:27)

Peetee
Člen | 75
+
0
-

Takhle se v Nette neuvažuje. Presenter je samostatná jednotka pro vykreslení kompletní sekce nějakého webu – např. Aktuality, Články, Registrace, Fórum. Pokud chceš mít na každé stránce vykresleno menu, rozhodně se k tomu účelu nevytváří MenuPresenter (…)

Ve světle těchto informací většina mých dotazů buď přestává mít smysl nebo je odpovězena. Děkuju! Nevím, jak jsem mohl přehlédnout příspěvek ve kterém jsi se zmínil o komponentách, resp. jsem zmínce o nich nevěnoval pozornost a věnoval jsem blokům – omlouvám se.

Stále mi ještě není jasná jedna otázka k tomuto témat: Vztah prezenteru a komenenty.

Dám příklad třeba komponenty, která vykreslí poslední komentáře, jak jsi navrhl v 4. příspěvku, kde by se dalo předpokládat, že o vykreslování komentářů už se normálně stará prezenter, pak se ptám, jestli není „nečisté“ stejnou věc dělat na dvou různých místech? resp. spojit komponentu s konkrétním prezenterem?

Ondřej Mirtes
Člen | 1536
+
0
-

Vztah komponenty a Presenteru – komponentu můžeš k Presenteru „připojit“, on se postará o vytvoření jejího objektu a např. o zavolání metody render() na té komponentě – čímž se vykreslí. Komponenta je jinak na Presenteru nezávislá třída – jeden Presenter se může starat o více komponent, stejně tak jako jedna komponenta může existovat ve více Presenterech.

K žádné duplikaci kódu nedochází. Všechnu potřebnou logiku můžeš umístit do komponenty, viz můj příspěvek nahoře. Pokud vezmeš tenhle kód, tak do Presenteru, který chceš, aby vykresloval ty poslední komentáře, umístíš továrničku:

protected function createComponentLastComments() {
	return new LastComments;
}

A do šablony Presenteru (např. Pokus.default.phtml) umístíš tento řádek s widget makrem (který se přeloží na $presenter['lastComments']->render()):

{widget lastComments}

A to je všechno. Vidíš, žádný kód z komponenty v Presenteru neopakuji :)

Peetee
Člen | 75
+
0
-

Myslím, že už mi je to začíná být jasnější, ikdyž bude třeba spousta praxe. Zrovna si připadám jak dítě, které dostalo novou hračku :-).

Ještě jednou děkuju za vysvětlení a trpělivost. Ať se daří.