Vkládání a výpis dat – presenter nebo komponenta?

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

Ahoj,
vytvořil jsem si dva presentery. Jeden pro vkládání položek do databáze a druhý pro výpis těchto položek.

Nyní bych potřeboval zobrazit v šabloně tyto dvě věci pod sebou. To znamená, že chci mít pod vkládáním rovnou výpis vložených položek.

Napadá mě k tomu především použití komponent (které jsem v nette ještě nepoužil). Je to správná cesta? No a v případě, že budu mít tyto dvě komponenty, tak pak budu mít jeden presenter který je spojí a provede metodu render, nebo je můžu rovnou vkládat do šablony?

Díky za nasměrování, Dan

Lukeluha
Člen | 130
+
0
-

Je nějaký důvod, proč máš logiku načtění položek a vložení do db v rozdílných presenterech? Pokud ano, pak by určitě byl dobrý nápad využití komponenty. Pokud ne, stačí tyto 2 presentery sloučit ← tato varianta je dle mého spíš správná (samozřejmě neznám přesně tvůj případ), ale nevidím důvod nemít jeden presenter ItemsPresenter, kde budou 2 action.

kloban
Člen | 123
+
0
-

Jde mi o to, že nevím, jak budu chtít aby aplikace vypadala za 2 měsíce, repektive nevím, jak ji bude chtít upravit potencionální budoucí zákazník. Proto bch ji chtěl rozčlenit na co nejmenší části a ty pak spojovat dohromady podle potřeby. Například když budu aplikaci nabízet, tak každý zákazník bude chtít mít poskládaný dashboard z jiných prvků.

Pavel Janda
Člen | 977
+
+3
-

Komponenty jsou určitě správná cesta, doporučuji se je naučit. Časem se ti pak weby nebudou skládat z ničeho jiného, než z komponent..

K tomuto účelu bych použil komponentu, která by měla další dvě komponenty – jednu na výpis a druhou na vkládání. Modulárnost tak dostane další úroveň.

Editoval Beton (7. 5. 2015 14:23)

newPOPE
Člen | 648
+
0
-

@kloban skus si pozriet preco su komponenty dobre :) https://www.youtube.com/watch?…

kloban
Člen | 123
+
0
-

Dobře, díky za navedení.
Momentálně mám hotovou jednoduchou komponentu a přemýšlím jak ji zasadit do zbytku aplikace.

Komponentá je následující:

CtestControl.php

<?php

namespace App\AdministrationModule\Components\Ctest;

use Nette\Application\UI\Control;
use Nette\Application\UI\Form;
use Nette\Security\AuthenticationException;
use Nette\Security\User;
use Nette\Templating\FileTemplate;


class CtestControl extends Control
{
        /**
         * @inject
         * @var \Kdyby\Doctrine\EntityManager
         */
        public $EntityManager;

	public function render()
	{
                $dao = $this->EntityManager->getRepository(\App\entities\Product::getClassName());
                $this->template->products = $dao->findAll();

		/** @var FileTemplate $template */
		$template = $this->template;
		$template->setFile(__DIR__ . '/templates/default.latte');
		$template->render();
	}

}

CtestControlFactory.php

<?php

namespace App\AdministrationModule\Components\Ctest;


interface CtestControlFactory
{

	/**
	 * @return CtestControl
	 */
	function create();

}

default.latte:

<h1>Moje komponenta</h1>

{foreach $products as $product}

    {$product->title} - {$product->price}<br>

{/foreach}

Rád bych ji teď vložil do šablony. Jak na to? Musím ji nějak posílat přes presenter? Nebo ji mohu rovnou vložit do těch šablon do kterých potřebuji?

David Matějka
Moderator | 6445
+
0
-

https://doc.nette.org/…n/presenters#…

Jinak @inject property defaultne fungujou jen v komponentach, pouzij radeji konstruktor injection

kloban
Člen | 123
+
0
-

Ten inject se mi nedaří.

Do třídy CtestControl jsem přidal tento kód:

private $service1;

public function __construct(Service1 $service)
{
    parent::__construct();
    $this->service1 = $service;
}

A do presenteru tento:

 /** @inject @var \Kdyby\Doctrine\EntityManager */
 private $service1;

/**
 * Ctest control factory.
 * @return CtestControl
 */
 protected function createComponentCtest()
 {
     // vytvoříme a nakonfigurujeme komponentu
     $ctest = new \App\AdministrationModule\Components\Ctest\CtestControl($this->service1);
     //$ctest->items = $this->item;
     // a vrátíme ji
     return $ctest;
     //return $ctest->render();
 }

Laděnka ale píše

Argument 1 passed to App\AdministrationModule\Components\Ctest\CtestControl::__construct() must be an instance of App\AdministrationModule\Components\Ctest\Service1, null given, called in /var/www/web-project/app/AdministrationModule/presenters/AddProductPresenter.php on line 76 and defined

Ještě jsem potom tedy zkusil upravit config.neon:

services:
        service1: Kdyby\Doctrine\EntityManager
        router: App\RouterFactory::createRouter
        - App\AdministrationModule\Components\SignIn\SignInControlFactory

To zas laděnka hlásí:
Class Kdyby\Doctrine\EntityManager used in service 'service1' not found or is not instantiable.

Postupoval jsem dle dokumentace https://doc.nette.org/…dependencies
Zkoušel jsem i hledat, nebo se dívat na GIT do ukázek, ale nepodařilo se mi najít kde mám problém. Díky za rady.

Zax
Člen | 370
+
0
-
  • Injektovat jde jen do public proměnných – přepiš private na public
  • Vymaž si cache – Nette si toho injectu jinak nemusí všimnout a bude dál hlásit „null given“.
  • Nauč se používat generované továrničky, usnadní hromadu práce s předáváním závislostí, postup je triviální:

1. vytvoření továrničky

namespace App\AdministrationModule\Components\Ctest;

interface ICtestControlFactory {

	/** @return CtestControl */
	public function create();

}

//EDIT: tip: osobně jsem si zvykl psát kód továrničky do stejného souboru, jako komponentu. Má to tu výhodu, že když někde v IDE kliknu na továrničku, tak se prokliknu rovnou ke kódu komponenty (kód továrny mě beztak v 99,9% případů vůbec nezajímá ;-))
//EDIT2: teď mi došlo, že továrnu vlastně nikdy nepíšu, používám file templates :-D ale to je fuk

2. Registrace továrničky v configu

services:
	- App\AdministrationModule\Components\Ctest\ICtestControlFactory

3. injektnutí továrničky do presenteru

/**
 * @var \App\AdministrationModule\Components\Ctest\ICtestControlFactory
 * @inject
 */
public $ctestControlFactory;

4. úprava metody createComponentCtest

protected function createComponentCtest() {
	$control = $this->ctestControlFactory->create();
	// do something if needed...
	return $control;
}

A je to. Cos tím získal? Vůbec neřešíš závislosti komponenty, ty ti vyřeší Nette – prozkoumá interface továrny, podle anotace @return zjistí, jakou komponentu má vracet, prozkoumá konstruktor komponenty a vygeneruje továrnu, která automaticky dosadí závislosti. Když komponentě změníš konstruktor, Nette se přizpůsobí, vygeneruje novou továrnu a ty nemusíš nikde v presenterech upravovat ani řádek kódu.

Pro-tip:
Generované továrničky nejsou omezeny jen na komponenty ;-)

Klidně jde udělat tohle:

interface IFormFactory {

	/** @return Form */
	public function create();

}

Všude, kde chceš tvořit formuláře, si akorát injektneš IFormFactory, místo new Form budeš používat $this->formFactory->create() a máš okamžitě „standardizované“ formuláře v celém projektu. Řekněme, že chceš pak za půl roku globálně změnit vzhled všech formulářů, stačí si napsat vlastní implementaci:

class MyCustomFormFactory implements IFormFactory {

	public function create() {
		$form = new Form;
		$form->setRenderer(new MyCustomRenderer);
		return $form;
	}

}

V configu akorát přepíšeš IFormFactory na MyCustomFormFactory a bam! projeví se to všude, kde funguješ přes továrnu.

Hustý, ne? :-)

Editoval Zax (8. 5. 2015 12:35)

kloban
Člen | 123
+
0
-

Zax: Díky, udělal jsem to tak. :)

Ještě mám ale další zásek. V třídě resp. komponentě CtestControl mám následující metodu render:

public function render()
{
	/** @var FileTemplate $template */
	$template = $this->template;
        $template->setFile(__DIR__ . '/templates/default.latte');
	$template->render();
}

Laděnka vypisuje Component '' is not attached to 'Nette\Application\UI\Presenter'.
V dokumentaci jsem našel (https://doc.nette.org/…n/components#…), že existuje metoda monitor. Proto jsem ji do konstruktoru komponenty přidal:

$this->monitor('Nette\Application\UI\Presenter');

Žádna změna ale nenastala.
Díky za případné nasměrování. D.

Unlink
Člen | 298
+
0
-

Problém bude podľa mňa inde, ukáž čo máš v konšruktore.

kloban
Člen | 123
+
0
-

Unlink: Takto vypadá celá třída CtestControl

<?php

namespace App\AdministrationModule\Components\Ctest;

use Nette\Application\UI\Control;
use Nette\Security\AuthenticationException;
use Nette\Security\User;
use Nette\Templating\FileTemplate;
use App\entities;

class CtestControl extends Control
{

        public function __construct(){
            parent::__construct();
            $this->monitor('Nette\Application\UI\Presenter');
        }

	public function render()
	{
		/** @var FileTemplate $template */
		$template = $this->template;
                $template->setFile(__DIR__ . '/templates/default.latte');
		$template->render();
	}

}
kloban
Člen | 123
+
0
-

Tak problém byl v presenteru.

Měl jsem v něm metodu createComponentCtest takto:

protected function createComponentCtest()
{
   $control = $this->ctestControlFactory->create();
   return $control->render();
}

Díky videu https://www.youtube.com/watch?… jsem pochopil, že v mětodě nemá být odkaz na render a má se rendrovat v šabloně presenteru pomocí {control ctest}.
Metoda má tedy vypadat takto:

protected function createComponentCtest()
{
   $control = $this->ctestControlFactory->create();
   return $control;
}

Úplně jsem ještě nepochopil proč jsem render nemohl mít v presenteru, ale zatím jsem rád, že mi to funguje. :-)

Azathoth
Člen | 495
+
0
-

Protože createComponent, jak název napovídá, slouží pouze k vytvoření komponenty, jak tovární funkce. Formulář taky nerenderuješ v createComponent. A render metodu už si zavolá frameowrk (pokud se to má vyrenderovat defaultně). Například u formulářů, což jsou komponenty, je vidět, že někdy (když ho renderuješ manuálně) se render na celý formulář nezavolá.

Editoval Azathoth (14. 5. 2015 21:05)