Komponenta a její správné napojení na model

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

Dobrý den,
udělal jsem si jednoduchou komponentu boxAkce, která má na starosti pouze, že se vypíšou akce z databáze. Data do komponenty nalévám v presenteru (profiltruji si databázi a vypíšu jen ty akce, které chci). Nikde, ale nemám nadefinované, co komponenta potřebuje ke svému „životu“. Jaký je správný postup? Sice si mohu dát do komentáře u komponenty, něco jako, pro chod komponenty je nutné dodat \Nette\Database\Table\Selection, kde jsou hodnoty: nazev, obsah. Ale tento postup se mi nějak nezamlouvá…

Též bych se chtěl dopracovat k tomu, abych místo:

{control boxAkce}

mohl zadat:

{control boxAkce, 8}

kde by číslo zobrazilo, kolik se zobrazí maximálně záznamů, jak tohoto docílit jednoduše? (s tím, že pokud se nezadá hodnota, vypíše to například max 10 záznamů.

Zkoušel jsem to pomocí:

class HomepagePresenter extends BasePresenter
{
    private $akce;

    public function actionDefault()
    {
        $this->akce = $this->model->getAkce()->where('zobrazit', 1)->where("od <= ?",

Date("Y-m-d"))->where('do >= ? OR do IS NULL', Date("Y-m-d"))->order('od, id');

    }

protected function createComponentBoxAkce($limit=10)
    {
	// zde si ale nedokážu nastavit limit ze šablony
        $akce = $this->akce->limit($limit);
        return new BoxAkce($akce);
    }
}

komponenta:

<?php

use Nette\Application\UI;
use Nette\Database\Table\Selection;

class BoxAkce extends UI\Control
{
    /** @var \Nette\Database\Table\Selection */
    private $akce;


    public function __construct(Selection $akce)
    {
        parent::__construct(); # vždy je potřeba volat rodičovský konstruktor
        $this->akce = $akce;
    }

    public function render()
    {
        $this->template->setFile(__DIR__ . '/BoxAkce.latte');
        $this->template->akce = $this->akce;
        $this->template->render();
    }

}
Ot@s
Backer | 476
+
0
-

Parametr u komponenty …

{control boxAkce, 8}

… se přenáši jako parametr do render() komponenty. Pokud toho využiješ, donutí tě to upravit výše uvedený kód tak, aby byl více košer.

public function render($limit=10)
{
	$this->template->setFile(__DIR__ . '/BoxAkce.latte');
	$this->template->akce = $this->presenter->model->getAkce()->where('zobrazit', 1)->where("od <= ?", Date("Y-m-d"))->where('do >= ? OR do IS NULL', Date("Y-m-d"))->order('od, id')->limit($limit);
	$this->template->render();
}

V HomepagePresenter pak stačí uvést:

protected function createComponentBoxAkce()
{
	return new BoxAkce;
}
tatyalien
Člen | 239
+
0
-

Tak jsem to musel jen trochu upravit, na:

Komponenta:

<?php

use Nette\Application\UI;
use Nette\Database\Table\Selection;

class BoxAkce extends UI\Control
{
    /** @var \ShopModel */
    private $model;

    public function __construct(\ShopModel $model)
    {
        parent::__construct(); # vždy je potřeba volat rodičovský konstruktor
        $this->model = $model;
    }

    public function render($limit=10)
    {
        $this->template->setFile(__DIR__ . '/BoxAkce.latte');
        $this->template->akce = $this->model->getAkce()->where('zobrazit', 1)->where("od <= ?", Date("Y-m-d"))->where('do >= ? OR do IS NULL', Date("Y-m-d"))->order('od, id')->limit($limit);
        $this->template->render();
    }

}

Presenter:

protected function createComponentBoxAkce()
{

    return new BoxAkce($this->model);
}

Podle tvého kódu mě totiž házelo laděnku: Cannot read an undeclared property HomepagePresenter::$model, i když mám:

abstract class BasePresenter extends Nette\Application\UI\Presenter
{
    /**
     *  * @var Model */
    protected $model;

    public function startup()
    {
        parent::startup();
        $this->model = $this->getService('model');
    }

a v homepage jsem zkusil i:

public function startup()
{
    parent::startup();
}

Takto ale mám nyní přesunutou veškerou logiku do komponenty, s tím, že ví co přesně potřebuje (jaké hodnoty z databáze atd…) má to takhle v normálu být, nebo se dělá nějaký mezikrok, který určí co která komponenta potřebuje pro svůj chod?

Ot@s
Backer | 476
+
0
-

Chodilo by to, kdybys protected $model změnils na public $model (v BasePresenter).

Obecně logika je v komponentě je správný přístup. To je princip DI, kdy komponentám (obecně objektům), vkládáš na vstup to, co potřebují k „samostatnému“ životu (konstuktorem, setterem, atributem). Typickým případem je právě tvůj problém – tj. přenesení objektu modelu (nebo DB connection) do komponenty. Opět máš více způsobů, jak to udělat. Můžeš to udělat způsobem, který jsi použil. Dále můžeš využít možností nového konfigurátoru, kdy si tyto závislosti mezi objekty definuješ v config.neon.

Kolem DI už toho bylo hodně napsáno. Výsledky, resp. best practices jsou v dokumentaci a videích z PS.

tatyalien
Člen | 239
+
0
-

Ok, děkuji ;)

duke
Člen | 650
+
0
-

Totéž téma se nedávno diskutovalo v tomto vlákně.