Získání proměnné z komponenty do šablony (@layout.latte)

sd
Člen | 87
+
0
-

Ahoj,

mám komponentu vykreslující články. Ten stejný výpis používám např. i u vyhledávání, výpis u autora atd – proto tedy jako samostatná komponenta.
V rámci výpisu mám samozřejmě i stránkování – je nějaká cesta jak dostat aktuální stránku a maximální počet stránek do presenteru ⇒ do šablony abych mohl si mohl latte blokem vložit do hlavičky <meta rel​=„​next​“ ..> a <meta rel​=„​prev“ …>.

Je na takovou situaci nějaké elegantní řešení?

Díky moc!
sd

Kamil Valenta
Člen | 820
+
0
-
  • na jedné stránce může být více komponent, např. „A“ a „B“
  • komponenta „A“ bude v paginatoru na 3. straně
  • komponenta „B“ bude v paginatoru na 4. straně

Jaké next a prev bude v meta?

Jinak ano, komponenta může zavolat public metodu presenteru, ale není to úplně šťastné.

Šaman
Člen | 2663
+
0
-

Presenter se přece může na stránku zeptat té komponenty, pokud na to bude mít veřejné rozhraní (public metodu getPage(), nebo v nejhorším public property).

sd
Člen | 87
+
0
-

Díky moc za reakce.

@kamil_v
Díky, chápu, jen já mám stránkování integrované přímo do té vypisovací komponenty – prostě vypíše všechny článku vč. stránkování – takto bych musel rozdělit výpis a stránkování a pro každou stránku vkládat komponentu s danou stránkou, tak jsi to myslel? Následně pak v default.latte si přepsat blok v @layout.latte

{block meta-link-prev-next}
	<link rel​="​next​" href​="{plink //this "articlesList-page" => $pag_cur+1}" n:if="...">
	<link ​rel​="​prev​" href​="​{plink //this "articlesList-page" => $pag_cur-1}​" n:if="$pag_cur > 1 ...">
{/block}

@Šaman
Díky moc, tvoje řešení mi asi přijde o něco hezčí.

public function renderDefault() {
	$this->template->currentPage = $this['articlesList']->getPage();
}

v ArticlesList mám

	/** @var int @persistent */
    private $page = 1;

    /** @var int */
    private $lastPage;

...
	private function loadArticles (...): void
	{
		// načte články, nastaví
		$this->page = ..
		$this->lastPage = ..
	}

	public function getPage(): int
    {
        return $this->page;
    }

    public function getPageLast(): int
    {
        return $this->lastPage;
    }

    /**
     * CONTROLS ****************************************************************
     */
    protected function createComponentSortByForm(): SortByForm
    {
        $control = $this->sortByFormFactory->create();
        $control->onSort[] = function (string $sortBy) {
            $this->redirect('this', ['sortBy' => $sortBy]);
        };

        return $control;
    }

    /**
     * RENDER ******************************************************************
     */
    /**
     * Render content
     */
    public function render(): void
    {
        $page = (int)$this->getParameter('page');
        if ($page == 0) {
            $this->page = 1;
        } else {
            $this->page = (int)$this->getParameter('page');
        }

        $this->template->sortBy = $this->getParameter('sortBy');
        $this->template->cid = $this->id;

        $this->template->page = $this->page;
        $this->template->lastPage = $this->lastPage;

        $this->loadArticles((int)$this->id, $this->page, $this->sortBy); // Loads articles

        $this->template->setFile(__DIR__ . '/templates/content.latte');
        $this->template->render();
    }

Takto nějak jsi to myslel? Jen jsem narazil na problém, že při volání render metody v presenteru data v komponentě ještě nejsou načtená – vrací mi tedy defaultní hodnoty.

Editoval sd (18. 2. 2020 11:45)

Kamil Valenta
Člen | 820
+
0
-

Ne, já to myslel tak, že na stránce může být více různých komponent a každá z nich si bude nárokovat META. A nerozumím tomu, proč by se presenter měl dotazovat komponenty na page, aby věděl, co má poslat do META.

„Šéf se nechodí ptát podřízeného, co má dělat jiný podřízený. Šéf to ví a všem podřízeným to oznámí“.

Takže bych si spíš představoval, že presenter ví z URL, na které page je a jen to setne do komponenty.

sd
Člen | 87
+
0
-

Díky @kamil_v za reakci. To chápu. Asi je chyba, že komponentě předávám de facto „Zobraz mi všechny články autora XY“ nebo „Najdi a zobraz všechny články s XY v titulku“ – narážím tedy na problém pokud bych diktoval z presenteru jakou stránku zobrazit, tak nevím, jestli ta stránka ještě vůbec existuje (celkový počet stránek zná jen ta komponenta)

Kamil Valenta
Člen | 820
+
0
-

Celkový počet stránek má vědět model. A toho se může zeptat presenter i komponenta :)

sd
Člen | 87
+
0
-

Ok, to je pravda. Jsem pako. Díky moc!

Šaman
Člen | 2663
+
0
-

Ale proč by to presenter dělal? Zodpovědnost za stránky je věc té komponenty. Presenter by si musel zjistit jak je komponenta nastavená (třeba kolik se zobrazuje článků na stránce), navíc pokud se cokoliv v logice stránkování změní, musel by se změnit taky.

Pod komponentou je samozřejmě model, ale s ním se má bavit komponenta. Presenter má za úkol vytvořit zobrazit tu komponentu. Jestli chce něco z její kompetence, ať se ptá jí.

Kamil Valenta
Člen | 820
+
0
-

Presenter má odbavit request. Pokud mu přijde „/articles?page=2“, tak má vytvořit komponentu a říct ji, ať zobrazí 2. stránku, nastavit META a plno dalších souvisejících věcí.
Věcí komponenty už může být jak ta data zobrazí.

Pokud to necháš řídit komponentou, tak stále nevím, jak budeš řešit situaci, kdy na jedné URL bude více komponent (což je běžný stav) a každá si bude nárokovat META po svém.

Šaman
Člen | 2663
+
+2
-

Komponenta si META nebude nárokovat. Ale pokud je stránkovač součástí komponenty, tak i v requestu bude číslo stránky někde v article-control-page=2 a Nette (na úrovni presenteru) to zpracuje a předá hodnotu komponentě. Tedy komponenta číslo stránky bude vědět (a bude znát i další nastavení, právě ten počet příspěvků na stránku a už bude mít vazbu na model který zná počet příspěvků apod.)
Presenter by tyhle rutiny duplikoval (navíc by potřeboval ArticleModel jako závislost pro zjištění počtu článků). Imho – když už na to mám komponentu, tak ji nechám obalit všechnu práci s články a stránkováním.

Meta je ale věc celého HTML výstupu, takže to by měl nastavit presenter. On už by měl vědět které komponenty na stránce jsou a ze které (a jestli vůbec) nastavovat META tag. Ale na číslo stránky a informace, jaká je předchozí a následující – na to by se měl zeptat komponenty, ta tomu rozumí.

Editoval Šaman (20. 2. 2020 11:17)

sd
Člen | 87
+
0
-

Díky moc oběma za příspěvky. Jakoby řešení od @Šaman mi přijde čistější, ale nepodařilo se mi ho zrealizovat.

Koncepčně mám komponentu ArticlesList, které předám jen podle čeho má články zobrazit a ona si je vytáhne z modelu, řeší stránkování a uživatelskou volbu řazení. Jenže se mi nedaří získat poslední stránku getterem z ArticlesList /jak navrhoval Šaman/. Aktuální stránka je v pořádku, ale počet stránek se mi vrací defaultní hodnota (null). Na tom jsem se zasekl a implementoval zatím řešení od Kamila. Neví někdo co dělám špatně?

final class ArticlesList extends BaseControl {
...
/** @var int @persistent */
private $page = 1;

/** @var int */
private $lastPage = null;

...

	public function getPage(): int
    {
        return $this->page;
    }

    public function getPageLast(): int
    {
        return $this->lastPage; // Když v render metodě šabloně si zavolám $this['articlesList']->getPage(), dostanu vždy null.
    }

	private function loadArticles(int $id, int $page, string $sort = 'default')
    {
        $articlesList = // získání z modelu ...

        $articlesCount = $articlesList->count();

        $this->template->articles = $articlesList->page($page, 24, $this->lastPage);

		$this->page = $page;
        $this->template->page = $this->page;
        $this->template->lastPage = $this->lastPage;

        $this->template-> articlesCount = $articlesCount;
    }

	public function handleLoadMore(int $id, int $page, string $sortBy): void
    {
        $this->loadArticles($id, $page, $sortBy);

        $this->redrawControl('articles-more');
        $this->redrawControl('articles-pagination-top');
        $this->redrawControl('articles-pagination-bottom');
    }

	public function render(): void
    {
        $page = $this->getParameter('page');
        $this->page = $page == 0 ? 1 : $page;

        $this->template->sortBy = $this->getParameter('sortBy');
        $this->template->cid = $this->id;

        $this->loadArticles($this->id, $this->page, $this->sortBy); // Načtení článků

        $this->template->setFile(__DIR__ . '/templates/content.latte');
        $this->template->render();
    }
}

EDIT: doplněný kód

Editoval sd (23. 2. 2020 21:03)

Kamil Valenta
Člen | 820
+
0
-

Což je v pořádku, alespoň podle ukázky, kterou jsi poslal, lastPage je null.
Patrně jsi třema tečkama vypustil to nejzajímavější :)

sd
Člen | 87
+
0
-

Doplnil jsem potřebný kus kódu – prostě jen v renderu si zavolám funkci na vytažení článků a nastavím $this->page a $this->lastPage. Už tam chybí jen constructor a pár dalších proměnných třídy, které tam mám.

Kamil Valenta
Člen | 820
+
+1
-

A kdy na to saháš v tom presenteru? IMHO na to saháš v presenteru dřív, než to v renderu kompoenty naplníš. Ale to jen tak odhaduju.

sd
Člen | 87
+
0
-

Sahal jsem na to v render metodě v presenteru a předával do šablony.

Šaman
Člen | 2663
+
0
-

Ta komponenta se nejprve vytvoří, pak připojí k presenteru, ale dokud se nevykreslí, tak se neprovede metoda render(). Takže ani loadArticles(). To načtení (inicializaci) proveď v konstruktoru, nebo (pokud bys k tomu chtěl něco z presenteru), tak v metodě attached(). Ta se zavolá ve chvíli, kdy se připojí k presenteru.

P.S. A cokoliv posílat do šablony ($this->template->page = $this->page;) bys měl dělat jen v render metodě.

Editoval Šaman (24. 2. 2020 2:17)