Opakovanie kodu – pomoc s komponentou

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

Ahojte, pokusim sa vam nacrtnut moj problem.
V roznych templateoch (na homepage, pri vyhladavani, pri prezerani urcitej kategorie) sa mi stale opakuje kus kodu.
Zjednodusena forma:

{foreach $books as $book}
	<img src="/image/{$book->id}">
	<h2>{$book->title}</h2>
	<p>{$book->description}</p>
{/foreach}

V presentery

 public function renderDefault(){
..
// zapis je iba symbolicky, v kazdom presentery su rozne podmienky pre vyber knih
$this->template->books = $this->db->find($by);
..
}

funguje mi to dobre, len pri uprave sablony musim nasledne upravovat ten isty kod aj v ostatnych sablonach .. predpokladam ze by sa to dalo riesit cez komponenty, cize by napr. pri vyhladavani vytvorilo tolko komponent kolko by bolo vysledkov vyhladavania, a tie by sa vypisali potom na stranku .. teoreticky v hlave mi to ide, ale nemozem to dostat do fungujuceho kodu .. potreboval by som vediet ako by mala vyzerat komponenta, ako upravit presenter v ktorom to vypisujem a aj sablonu.
Dufam ze som to popisal ako-tak zrozumitelne. Viem ze je tu ohladom komponent vela tem, ale po prestudovani par z nich sa mi to stale nedari.

thm
Člen | 147
+
0
-

Zdar, jsem sice také začátečník, ale pokusím se ti to vysvětlit. Opravte mě někdo, jestli šířím bludy.

Komponenta:

class BookList extends Nette\Application\UI\Control{

    /** @var Nette\Database\Connection */
    private $connetion;

    /** @var Array */
    private $findBy;

    public function __construct(Nette\Database\Connection $connetion) {
        $this->connetion= $connetion;
    }

    public function setFindBy($findBy){
        $this->findBy = $findBy;
    }

    public function render(){
        $this->template->setFile(__DIR__ . '/bookList.latte');

        $books= $this->connection->table('books');
        if($this->findBy) $books->where($this->findBy);
        $this->template->books= $books;

        $this->template->render();
    }
}

Komponenta má kontruktor, tam posílám Connection, protože ho potřebuje využívat.
Komponenta má metodu render(), ve které nastavíme soubor se šablonou a do ní pak pošleme nějakou proměnnou, nakonec vyrenderujeme.
Komponentě jsem udělal metodu na oříznutí výsledků z databáze.
(komponentu lze udělat i lépe, ale pro názornost stačí)

Šablonu máš hotovou.

Nyní někde v presenteru (podle toho kde všude jí chceš zobrazovat, tak použij nadřazenější presenter) napíšeme továrničku na tuto komponentu. Ta má předepsaný název createComponent<NazevKomponenty>() a bude vracet instanci komponenty.

public function createComponentBookList(){
        return new BookList($this->connection);
    }

Komponenta hotova, vytvoří se až když je potřeba. Někdy je to až v šabloně. Když Nette při renderování narazí na makro

{control bookList}

jukne jestli už tuto komponentu má připojenou k presenteru, pokud ne, zavolá metodu createComponent<NazevKomponenty>() a vyrenderuje.

Někdy je ale potřeba komponentu doladit než se vyrenderuje a to je i náš případ. Někde v presenteru je potřeba filtrovat výsledky. Komponentu můžeme v presenteru zavolat tímto:

$this['bookList']->setFindBy(array('author' => 'Grundl'));

Když se narazí v presenteru na tuto řádku a komponenta neexistuje, Nette zavolá createComponent<NazevKomponenty>() a vyrobí ji. Pak už máme odkaz na instanci komponenty. Když dojde na {control bookList}, tak ji jenom vyredneruje (už ji má vytvořenou).

Snad ti to trochu pomůže.

Šaman
Člen | 2666
+
0
-

V tomhle případě, protože parametr je potřeba jen k vykreslení, je možné předat parametr až render metodě.

<?php
    public function render($findBy = NULL){
        $this->template->setFile(__DIR__ . '/bookList.latte');

        $books= $this->connection->table('books');
        if(isset($findBy)) $books->where($findBy);
        $this->template->books= $books;

        $this->template->render();
    }
?>
Seznam všech knih:
{control bookList}

Seznam knih od Davida:
{control bookList, 'author' => 'Grudl'}

Je to stále stejná komponenta, pokaždé jinak nastavené vykreslování. Pravá komponenta mívá i vnitřní stavy (pak se ale musí nastavit už v továrničče, parametr render metody nestačí). A u takové komponenty s vnitřními stavy se samozřejmě používá vždy nová instance, tady je to ale zbytečné.
Když si ji dáš její továrničku do BasePresenteru (není to ideální z pohledu čistoty, ale ušetří to psaní), tak tu komponentu budeš moci používat ve všech šablonách presenterů.

// Edit: upravil jsem pár překlepů v kódu a Davidovo příjmení. :)

Editoval Šaman (18. 3. 2014 22:53)

thm
Člen | 147
+
0
-

Tak tohle jsem ani nevěděl, že lze poslat parametr do render metody komponenty, to se hodí. V tomto případě je to nepochybně ideální.