Začiatočnícke otázky (Tovarnička vs. Presenter)
- SontoEremo
- Člen | 341
Zdravím ľudia,
Rád by som sa spýtal na pár veci ohľadom továrničiek a presenterov.
Niekde som sa dočítal, že je lepšie používať formulár ala továrnička
napr. registrácia či login ako v presentery ako to teda je? prečo to je
lepšie? aké to má výhody?
Momentálne mám Vytvorený SignPresenter kde riešim Registráciu,
Prihlasovanie, Obnovenie hesla, Preposlanie aktivácie účtu a
odhlasovanie dá sa to používať aj v továrničke spolu alebo pri
tovarníčkach to musím rozkúskovať?
Ospravedlňujem sa za hlúpe dotazy no potrebujem si ujasniť čo je lepšie
hneď na začiatok.
Za odpoveďe vopred Ďakujem.
Editoval SontoEremo (22. 6. 2014 18:35)
- echo
- Člen | 134
Zdravím,
obecně se doporučuje single-responsibility princip. Proto by bylo nejlepší
kdyby jsi rozdělil SignPresenter na více presenterů: AuthenticationPresenter
(login, logout), ResetPasswordPresenter, RegistrationPresenter (registrace,
aktivace) Toto má svá omezení.
Pokud potřebuješ vykreslit přihlašovací komponentu ve všech presenterech, pak AuthenticationPresenter ztrácí význam. Uděláš si AuthenticationLoginControl a logiku (chápej jako logiku, která komunikuje s modelem!) nacpeš tam. V BasePresenteru vytvoříš pomocí createComponentAuthenticationControl()… komponentu a vykreslíš v layoutu. Podobně AuthenticationLogoutControl atd.
Ber to jako nástin možností. Není totiž možné říct, abys to dělal tak nebo jinak. Záleží na potřebách aplikace.
- David Matějka
- Moderator | 6445
@SontoEremo precti si: https://doc.nette.org/…s/form-reuse je tam jak o tovarnach, tak i o formularich jako komponentach, jak radi @echo
- SontoEremo
- Člen | 341
@echo & @matej21 Vďaka za reakcie :)
Trocha upresním prečo som sa na to pítal.
Všetky spomenuté formuláre chcem použiť iba na jednom mieste nikde inde ich
nechcem umiestnovať 2×.
Preto som sa pýtal, či je lepšie továrnička s formulárom alebo formulár
v presenter-u,
keďže som chcel mať fajn url v tvare http://www.domena.xx/sign/in atď… tým, že ich
rozstrúsim budem mať aj inú tvar url nie? čiže už to bude http://www.domena.xx/
authentication/in atď…
Editoval SontoEremo (22. 6. 2014 19:19)
- David Matějka
- Moderator | 6445
@SontoEremo v pripade tovarnicek pouze presunes vytvareni onoho formulare pryc z presenteru, aby formular nebobtnal – url tedy zustane naprosto stejna. je to jako bys mel nejakou funkci
function vytvor_prihlasovaci_formular()
{
$form = new Form();
$form->addText('email');
...
return $form;
}
a tuto funkci pak v createComponent metode zavolal. Pomoci tech zminovanych tovarnicek, ktere jsou jako trida a reigstrovane v DI containeru, je to sofistikovanejsi a cistci.
Komponenty jsou o krapet slozitejsi, ale mnohem flexibilnejsi.
Editoval matej21 (22. 6. 2014 19:35)
- SontoEremo
- Člen | 341
@matej21 Takže si vytvorím dajme tomu Továrničku s napr. UserRegister.php a vňom
use Nette\Application\UI\Form;
class UserRegister extends Nette\Object
{
private $database;
public function __construct(Nette\Database\Connection $database)
{
$this->database = $database;
}
public function createComponentSignUpForm()
{
$form = new Form;
....
return $form;
}
public function signUpFormSuccessSubmited($form)
{
...
}
}
atď … a to potom predať SignPresenter-u a nakoniec vykresliť v Šablone napr. in.latte ?
- echo
- Člen | 134
@SontoEremo To děláš špatně. createComponent ve třídě, která nedědí od Nette\ComponentModel\Container (čili Presenter nebo Control atd. viz Nette.ComponentModel.Container) je k ničemu. Já bych na to ve tvém případě šel takto:
Presenter:
class AuthenticationPresenter extends Presenter
{
/** @var UserModel @inject */
public $user;
public function actionOut()
{
$this->user->logout();
}
protected function createComponentLoginForm($name)
{
$form = new Form($this, $name);
$form->add...
if($form->isSuccess()) {
$this->login($form->values->username, $form->values->password);
}
return $form;
}
protected function login($username, $password)
{
try {
$this->user->login($username, $password);
} catch (AuthenticationException $e) {
$this->flashMessage('Invalid credentials');
}
}
}
UserModel by byla fasáda, která by se starala o běžné věci s uživatelem. Měla by přístup do databáze, session, atp. Její metody bych viděl na
login($username, $pass);
logout();
register($username, $pass, array $data);
markAsActive($username);
...
Případně lépe (rozuměj více) objektově:
login(User $user);
logout();
register(User $user);
markAsActive(User $user);
...
Šablonu pak řešit přímočaře:
{control loginForm}
- David Matějka
- Moderator | 6445
@echo:
- do formu nemusis predavat this a name.
- nepouzivej zadny
->isSuccess()
… ale onSuccess event - tohle myslim @SontoEremo uz umi a ma. tahle diskuze je o refaktoringu toho kodu pryc z presenteru
Editoval matej21 (22. 6. 2014 21:36)
- echo
- Člen | 134
matej21 napsal(a):
@echo:
- do formu nemusis predavat this a name.
- nepouzivej zadny
->isSuccess()
… ale onSuccess event- tohle myslim @SontoEremo uz umi a ma. tahle diskuze je o refaktoringu toho kodu pryc z presenteru
- jednou jsem měl problém s metodou attached(), tak to tam raději uvádím
- když definuju formulář v createComponent..() a používám ho jenom na jednom místě, tak mi to umožní mít obslužnou metodu presenteru jako protected, což se mi líbí; pokud na formulář mám vlastní továrničku, pak s tebou naprosto souhlasím
- souhlas
- SontoEremo
- Člen | 341
@echo & @matej21
Tak po poriadku chalani :) pretože som sa dosť pomýlil takže normálne si
vytvorím
AuthenticationPresenter kde budem riešiť logout + login a zas
napr. RegistrationPresenter pre registráciu atď…
Šablonu pak řešit přímočaře:
{control loginForm}
Ale kde potom budem definovať Css+Html štruktúru? ako mám teraz napr. login
<div id="signup-page-content">
{form signInForm class=>"form"}
<h1 class="block-heading">Prihlásenie</h1>
<div n:foreach="$form->errors as $error" id="errors" class="alert alert-danger">
<strong >{$error}</strong>
</div>
<div n:foreach="$flashes as $flash" id="flashes" class="alert alert-danger flash {$flash->type}">
<strong >{$flash->message}</strong>
</div>
<div class="input-icon mbl">
<i class="fa fa-user"></i>
{input username}
</div>
<div class="input-icon mbl">
<i class="fa fa-key"></i>
{input password}
</div>
{input signIn class=>"btn btn-lg btn-red btn-block"}
{/form}
<div class="sign-footer">
<ul class="list-unstyled">
<li><a n:href="Sign:reset">Zabudli ste heslo?</a></li>
<li>Ešte nemáte účet? <a n:href="Sign:up">Vytvorte si ho</a></li>
<li>Nedostali ste aktivačný email? <a n:href="Sign:resend">Pošlite si ho znova</a></li>
</ul>
</div>
</div>
je o refaktoringu toho kodu pryc z presenteru Tak v presenteru alebo preč z presenteru :)
Editoval SontoEremo (22. 6. 2014 21:51)
- David Matějka
- Moderator | 6445
@SontoEremo: v pripade pouziti tovarnicky se na zobrazeni nic nemeni – ty jen vezmes ten kod z createComponent* metody a presunes ho do tovarnicky, kterou pak v one createComponent* metode zavolas a vrati ti nakonfigurovany formular.
V pripade formulare jako komponenty (druha cast v prispevku, na ktery jsem odkazoval) je to o malinko komplikovanejsi – tam ono zobrazeni formulare resi samotna komponenta
Editoval matej21 (22. 6. 2014 21:56)
- SontoEremo
- Člen | 341
@matej21 tebe veľmi moc Ďakujem za rady.
Ale mal by som prosbu mohol by si mi ukázať ako to s tou továrničkou?
nejaký rýchli kód ako to všetko vytvoriť pretože som prešiel všetko ale
nejak mi to stále neni jasné :)
- SontoEremo
- Člen | 341
Napr. Vytvorím si továrničku ako ju pomenovať Register… ← čo? čo doň dať? kde uložiť? kam zadať a ako vložiť do presenteru?
- David Matějka
- Moderator | 6445
@SontoEremo zkus si to :) kdyz ti budu posilat hotove reseni, tak je to k nicemu. Hezky priklad je v tomto clanku, na ktery uz jsem odkazoval
Proste si vytvoris tridu RegisterationFormFactory, do create metody das
inicializaci formulare, vytvoris si tam nejakou processForm metodu, kde se form
zpracuje a ulozi. Tuhle factory registrujes jako sluzbu, injectnes do presenteru
a v prislusne createComponent* metode zavolas
$his->registrationFormFactory->create()
… ale to vsechno je
v tom clanku, tak si ho precti :)
a jeste jeden prispevek pro inpiraci: https://forum.nette.org/…nebo-tovarna
Editoval matej21 (22. 6. 2014 22:07)
- SontoEremo
- Člen | 341
@matej kdyz ti budu posilat hotove reseni, tak je to
k nicemu.
Pravda pravdúca :) ale teraz k problému takže som sa niečo pokúsil
splácať a hádam je to ok
moj components/RegisterFormFactory.php
<?php
namespace App\components;
use Nette\Object;
use Nette\Application\UI\Form;
use Nette\Utils\Html;
use Nette\Security as NS;
use Nette\Mail\Message;
use Nette\Mail\SmtpMailer;
use Nette\Utils;
use Nette\Templating\FileTemplate;
use Nette\Latte\Engine;
use Nette\Templating\Helpers;
class RegisterFormFactory extends Object
{
private $database;
public function __construct(\Nette\Database\Connection $database)
{
$this->database = $database;
}
/**
* @var \App\Model\UserRepository
* @inject
*/
public $userRepository;
public function createSignUpForm()
{
$repository = $this->userRepository;
$sign_UP_form = new Form();
$sign_UP_form->addProtection();
$sign_UP_form->addText('username')
->setAttribute('placeholder', 'Užívateľské meno')
->setAttribute('class', 'form-control')
->addRule(Form::MIN_LENGTH, 'Užívateľské meno musí obsahovať aspoň %d znaky', 4)
->addRule(Form::MAX_LENGTH, 'Užívateľské meno môže obsahovať max. %d znakov', 32)
->addRule(function($input) use($repository)
{
return !$repository->userexists($input->value);
}, 'Toto užívateľské meno je obsadené prosím vyberte si iné!');
$sign_UP_form->addText('email')
->setAttribute('placeholder', 'Emailová adresa')
->setAttribute('class', 'form-control')
->addRule(Form::EMAIL, 'Zadajte prosím, platnú emailovú adresu!')
->addRule(function($input) use($repository)
{
return !$repository->usermailexists($input->value);
}, 'Táto emailová adresa je už v systéme zaregistrovaná!');
$sign_UP_form->addPassword('password')
->setAttribute('placeholder', 'Heslo')
->setAttribute('class', 'form-control')
->addRule(Form::MIN_LENGTH, 'Heslo musí obsahovať aspoň %d znaky', 4)
->addRule(Form::MAX_LENGTH, 'Heslo môže obsahovať max. %d znakov', 32);
$sign_UP_form->addPassword('confirm_password')
->setAttribute('placeholder', 'Potvrďe Heslo')
->setAttribute('class', 'form-control')
->addRule(Form::EQUAL, 'Zadané heslá nie sú zhodné!', $sign_UP_form['password']);
$sign_UP_form->addSubmit('signUp', 'Vytvoriť účet');
$sign_UP_form->onSuccess[] = $this->processForm;
return $sign_UP_form;
}
public function processForm(Form $sign_UP_form)
{
}
}
môj SignPresenter
<?php
namespace App\presenters;
use Nette\Application\UI\Form;
class SignPresenter extends DefaultPresenter {
/**
* @var \App\components\RegisterFormFactory
* @inject
*/
public $registerFormFactory;
protected function createComponentRegisterForm() {
$signUp_Form = $this->registerFormFactory->createSignUpForm();
return $signUp_Form;
}
}
môj Config
services:
- App\components\RegisterFormFactory
A nakoniec up.latte
<div id="signup-page-content">
{form registerForm}
<h1 class="block-heading">Registrácia</h1>
{if $form->hasErrors()}
<div id="errors" class="alert alert-danger">
<ul>
{foreach $form->errors as $error}
<li>{$error}</li>
{/foreach}
</ul>
</div>
{/if}
<div n:foreach="$flashes as $flash" id="flashes" class="alert alert-danger flash {$flash->type}">
<strong >{$flash->message}</strong>
</div>
<div class="input-icon mbl">
<i class="fa fa-user"></i>
{input username}
</div>
<div class="input-icon mbl">
<i class="fa fa-envelope"></i>
{input email}
</div>
<div class="input-icon mbl">
<i class="fa fa-key"></i>
{input password}
</div>
<div class="input-icon mbl">
<i class="fa fa-shield"></i>
{input confirm_password}
</div>
<span class="help-block mtm mbl text-muted">
Kliknutím na tlačidlo <strong>"Vytvoriť účet"</strong> súhlasíte so <a href="#">Všeobecnými podmienkami</a> ako aj s <a href="#">Ochranou Osobných údajov</a>. Potvrdzujete, že ste si podmienky a všetky potrebné dokumenty prečítali pred tým ako ste si účet vytvorili.
</span>
{input signUp class=>"btn btn-lg btn-red btn-block"}
{/form}
<div class="sign-footer">
<ul class="list-unstyled">
<li>Už mám účet <a n:href="Sign:in">chcem sa Prihlásiť</a></li>
</ul>
</div>
</div>
- David Matějka
- Moderator | 6445
@SontoEremo: skoro dobre :) jen nepouzivej mimo presentery injectovani pomoci anotace inject. to se hodi (jen) do presenteru (je tady uz spoustu diskuzi ohledne toho a nebudu zacinat dalsi proc tomu tak je). takze si ten repozitar vyzadej v konstruktoru, stejne jako to delas s tim databazovym spojenim. Jinak tam na prvni pohled nevidim chybu.
- SontoEremo
- Člen | 341
hej do constructoru ale ako keď chcem injectovať viac súborov?
private $userRepository;
public function __construct(\App\Model\UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
private $userManager;
public function __construct(\App\Model\UserManager $userManager)
{
parent::__construct($userManager);
$this->userManager = $userManager;
}
Je hlúposť asi, že? :)
- David Matějka
- Moderator | 6445
Neinjectujes soubory, ale objekty (instance trid)
Vic konstruktoru mit nemuzes, proste do jednoho konstruktoru pridej vice
parametru
protected $userRepository;
protected $userManager;
public function __construct(\App\Model\UserRepository $userRepository, \App\Model\UserManager $userManager)
{
$this->userRepository = $userRepository;
$this->userManager = $userManager;
}
- SontoEremo
- Člen | 341
@matej na prvý krát som to aj tak skúšal ale bez
čiarky a skončil som s chybou tak som to zmenil no nevadí Ďakujem za pomoc
a nakopnutie tak zas nabudúce :D
Ešte raz díky @matej21
- SontoEremo
- Člen | 341
Predsa len ešte jedná otázka
v presenteri som uvádzal $template->verifyLink =
$this->presenter->link(‚//Sign:verify‘, array(‚token‘ ⇒
$token,));
Teraz by som asi mal namiesto $this->presenter->link()
mal používať niečo iné ale čo?
- Šaman
- Člen | 2666
V presenteru je $this
totožné s
$this->presenter
, takže ten presenter je tam zbytečné psát.
Jestli jsi v komponentě, tak se tímto zápisem dostaneš na presenter a dál
pracuješ jako uvnitř presenteru. Nebo napíšeš
$this->plink(…);
. Plink si najde presenter a zavolá nad
ním link.
- SontoEremo
- Člen | 341
@Šaman
Vyhadzuje mi vždy
Call to undefined method
App\components\RegisterFormFactory::plink()
- Šaman
- Člen | 2666
- Link, nebo plink bych nepředával šabloně, ale volal makrem. To tu chybu ale nezpůsobuje.
- Jaká RegisterFormFactory? Ty si píšeš vlastní tovární třídu? Ta samozřejmě žádný presenter nezná. Obsluhu musíš napsat přímo do komponenty (něco, co dědí od PresenterComponent). Ukázku formuláře v komponentě máš třeba tady.
- SontoEremo
- Člen | 341
@Šaman ja mám RegisterForm a vňom
<?php
namespace App\components;
use Nette\Object;
use Nette\Application\UI\Form;
use Nette\Utils\Html;
use Nette\Security as NS;
use Nette\Mail\Message;
use Nette\Mail\SmtpMailer;
use Nette\Utils;
use Nette\Templating\FileTemplate;
use Nette\Latte\Engine;
use Nette\Templating\Helpers;
class RegisterFormFactory extends Object
{
/** @persistent */
public $backlink = '';
protected $userRepository;
protected $userManager;
public function __construct(\App\Model\UserRepository $userRepository, \App\Model\UserManager $userManager)
{
$this->userRepository = $userRepository;
$this->userManager = $userManager;
}
public function createSignUpForm()
{
$repository = $this->userRepository;
$sign_Up = new Form();
$sign_Up->addProtection();
$sign_Up->addText('username')
->setAttribute('placeholder', 'Užívateľské meno')
->setAttribute('class', 'form-control')
->addRule(Form::MIN_LENGTH, 'Užívateľské meno musí obsahovať aspoň %d znaky', 4)
->addRule(Form::MAX_LENGTH, 'Užívateľské meno môže obsahovať max. %d znakov', 32)
->addRule(function($input) use($repository)
{
return !$repository->userexists($input->value);
}, 'Toto užívateľské meno je obsadené prosím vyberte si iné!');
$sign_Up->addText('email')
->setAttribute('placeholder', 'Emailová adresa')
->setAttribute('class', 'form-control')
->addRule(Form::EMAIL, 'Zadajte prosím, platnú emailovú adresu!')
->addRule(function($input) use($repository)
{
return !$repository->usermailexists($input->value);
}, 'Táto emailová adresa je už v systéme zaregistrovaná!');
$sign_Up->addPassword('password')
->setAttribute('placeholder', 'Heslo')
->setAttribute('class', 'form-control')
->addRule(Form::MIN_LENGTH, 'Heslo musí obsahovať aspoň %d znaky', 4)
->addRule(Form::MAX_LENGTH, 'Heslo môže obsahovať max. %d znakov', 32);
$sign_Up->addPassword('confirm_password')
->setAttribute('placeholder', 'Potvrďe Heslo')
->setAttribute('class', 'form-control')
->addRule(Form::EQUAL, 'Zadané heslá nie sú zhodné!', $sign_Up['password']);
$sign_Up->addSubmit('signUp', 'Vytvoriť účet');
$sign_Up->onSuccess[] = $this->signupsuccessForm;
return $sign_Up;
}
public function signupsuccessForm(Form $sign_Up) {
$values = $sign_Up->getValues();
if ($values->password === $values->confirm_password){
$user = $this->userManager;
$token = Utils\Random::generate(32);
$userip = $_SERVER['REMOTE_ADDR'];
$user->add($values->username, $values->email, $values->password, $token, $userip);
$template = new FileTemplate(__DIR__ . '/../templates/Activate/email.latte');
$template->registerFilter(new Engine);
$template->registerHelperLoader('\Nette\Templating\Helpers::loader');
$template->user = $user;
$template->verifyLink = $this->presenter->plink('//Sign:verify', array(
'token' => $token,
));
$mail = new Message;
$mail->setFrom('VistmoLabs <vistmolabs@vistmolabs.org>')
->addTo($values['email'])
->setSubject('Aktivácia účtu')
->setHtmlBody($template);
$mailer = new SmtpMailer(array(
'host' => 'smtp.vistmolabs.org',
'port' => 465,
'username' => 'vistmolabs@vistmolabs.org',
'password' => 'WS83ZYF6427g5G9',
'secure' => 'ssl',
));
$mailer->send($mail);
$this->flashMessage(Html::el('div')
->setHtml('Registrácia prebehla úspešne. Na zadanú Emailovú adresu <strong class="email-strong"><br>' . $values->email . '<br></strong> Vám bol zaslaný aktivačný link, po úspešnej aktivácii sa budete môcť prihlásiť.'));
$this->redirect('finish');
} else {
$signup_form->addError($this->getMessage());
}
}
}
problém mi robí táto časť
$template->verifyLink = $this->presenter->plink('//Sign:verify', array(
'token' => $token,
));
- Šaman
- Člen | 2666
Jj. A když to zakomentujes, tak ti bude zlobit i
$this->flashMessage
a $this->redirect
. Proč?
Protože dědíš od Object
, který žádně flahmessage ani
redirecty nezná. To všechno musíš volat nad presenterem. Na presenter se
dostaneš (běžným způsobem) jen z komponenty, která bude k presenteru
připojena (až bude připojena, takže ne třeba v konstruktoru). Takže:
- potřebuješ komponentu, viz třeba můj odkaz – pak bude fungovat $this->presenter->link/redirect/flashMessage
- volitelně na ni buď dopíšes ručně tovární třídu, nebo použiješ generované továrničky
- SontoEremo
- Člen | 341
@Šaman dík spravil som to podľa tvojho formulára a všetko funguje ok ale chcem sa spýtať v šablone Components/LoginForm/loginForm.latte chcem uviesť link ale vyhadzuje to chybu
<ul class="list-unstyled">
<li><a n:href="#">Zabudli ste heslo?</a></li>
<li>Ešte nemáte účet? <a n:href="#">Vytvorte si ho</a></li>
<li>Nedostali ste aktivačný email? <a n:href="#">Pošlite si ho znova</a></li>
</ul>
predtým v presenter-y stačili <li><a n:href=„Sign:reset“>Zabudli ste heslo?</a></li> ale tu neviem ako na to.
A ešte sa chcem spítať naozaj je potrebné mať komponentu keď prihlasovací a vlastne všetky formuláre budú len na jednom mieste a nikde inde?
- Šaman
- Člen | 2666
n:href
je zkratka pro makrolink
. Takže pokud jsi v komponentě a odkazuješ se na presenter, musíš použíthref="{plink …}"
- Není. Já mám komponenty raději, ale zrovna u takového jednoúčelového formuláře, jako přihlašování je to jedno. Obecně ale presentery dělají moc věcí a jdou to dlouhé třídy, takže se snažím je co nejvíc osekat. Druhým důvodem pro komponenty je snadné znovupoužití.
Editoval Šaman (24. 6. 2014 0:00)