Vkládání a výpis dat – presenter nebo komponenta?
- kloban
- Člen | 123
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
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
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
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
@kloban skus si pozriet preco su komponenty dobre :) https://www.youtube.com/watch?…
- kloban
- Člen | 123
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
https://doc.nette.org/…n/presenters#…
Jinak @inject property defaultne fungujou jen v komponentach, pouzij radeji konstruktor injection
- kloban
- Člen | 123
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
- 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
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.
- kloban
- Člen | 123
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
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
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)