Problem s redirect
- trubi
- Člen | 25
Ahoj, mam asi zacatecnicky dotaz, vcera jsem si zacal hrat s nette ale nemam zkusenosti s zadnym php frameworkem. Narazim jenom na same problemy, vetsinu se mi podarilo vyresit, ale ted uz fakt nemuzu:
Jenom na zacatek, pochopil jsem to spravne:
Chci mit na strance nejaky content + treba v levem sloupci dalsi moduly (nebo
widgety chcete-li).
Hlavni obsah generuju v presenteru a pro kazdy widget mam vytvoreny Control, do
layoutu je vkladam takhle:
<div id="body">
{include $content}
</div>
<div id="right">
{$userBox->render()}
{$newsBox->render()}
...
</div>
je to spravne?
a ted k tomu dotazu: V UserControl mam neco jako
public function loginHandler() {
...
$user = new UserModel();
$user->login($id);
$this->redirect('this');
}
Vse mi funguje, problem je s presmerovanim. redirect mi vyhazuje IllegalStateException: Cannot set HTTP code after HTTP headers have been sent (output started at E:\work\test_nette\app\temp\cache-Nette.Template%00dea1fad6787f7f2774a37ec7030902211.%40layout.phtml:14).
Pokud misto redirect pouziju primo header(‚Location: ..‘), vyhodi to Warning: Cannot modify header information – headers already sent. Z cehoz jsem zmateny. Vypada to, jakoby se nejdrive vytiskl layout a az potom zpracoval ten loginHandler v UserControlu.
Nevim si rady. Mozna jsem to cele pochopil spatne a mam to udelat uplne jinak. Budu vdecny za kazdou radu tykajici se tohoto. Co delam spatne, co udelat jinak…
moc diky
Trubi
Editoval trubi (31. 3. 2009 22:51)
- pmg
- Člen | 372
{$userBox->render()}
se převede na
<?php echo htmlspecialchars($userBox->render()) ?>
.
Escapování ani echo
v tomto případě patrně nejsou potřeba
(metoda render rovnou vypisuje kód), takže by stačilo
{?$userBox->render()}
.
Přesměrování se provádí posláním HTTP hlavičky, po prvním
odeslání výstupu do prohlížeče už ale nelze hlavičku přidat. Metoda
loginHandler
by se proto měla volat před generováním nějakého
výstupu, tzn. z metody actionLogin
(obecně action*
,
viz Životní cyklus
presenteru). Můžeš také zkusit metodu jen přejmenovat na
actionLogin
.
- Honza Marek
- Člen | 1664
{$userBox->render()}
se převede na<?php echo htmlspecialchars($userBox->render()) ?>
.
Jen takový detail: David řikal kdysi na školení, že když je to objekt, tak se htmlspecialchars nezavolá. Nic to ale nemění na tom, že by se mělo použít {? $userBox->render()}.
- trubi
- Člen | 25
Mno, sice jsem se ptal na neco jineho, ale mate pravdu..:)
Nicmene se mi asi rozsvitilo, zrejme jsem se mel vice soustredit na zivotni
cyklus presenteru. Jenom malickost (asi hloupa), jak nasmeruju odeslani
formulare na tu akci actionLogin()? Protoze samotne prejmenovani metody
nefunguje (jak by taky mohlo ze). Zkousel jsem neco jako
$form->setAction($this->link(..)); ale to nefunguje, rekl bych ze proto,
ze ten UserControl je widget, jenomze ja potrebuju presmerovat na aktualni
presenter a zavolat actionLogin() UserControlu. Muzete nekdo? Jen 4 vety,
dik :)
- pmg
- Člen | 372
Bohužel mi nebylo zcela jasné, jak kód vypadá, takže jinak. Definici
formuláře dej do UserControl::actionLogin
a potom můžeš
přidat $form->onSubmit[] = 'loginHandler'
. V metodě
loginHandler
provedeš přihlášení a přesměruješ, jak jsi to
dělal. Viz také Nette\Forms.
Jinak když něco se šablonou nefunguje, je dobré se kouknout na vygenerované PHP v keši.
Honzo, testoval jsem to, ale zdá se, že se to escapuje. Možná se to
měnilo, ale podle mě je to takhle lepší. Také jsem zjistil, že nefunguje
zápis {$obj->{'member'}}
. :-(
- trubi
- Člen | 25
Definici formuláře dej do
UserControl::actionLogin
Tak to mi mozek nebere..Myslel jsem, ze action**() metoda se vola az po provedeni nejake akce? Zkusil jsem to tak jak pises a nefunguje to. Protoze se actionLogin vubec neprovede. Ani nechapu, proc by se mela provadet (kdyz nacitam stranku, potrebuju vykreslit formular, ale nechci ho nijak zpracovavat a nikde ani programu nerikam, ze by se actionLogin zpracovavat mel).
Tak to zkusim jinak, tady je kod: (muzu vedet co delam spatne? opakuji, ten UserControl je na kazde strance webu a potrebuju ho zpracovavat pokazde). Rek bych ze mi dela problem proste to, ze nevim jak udelat, aby se spravne zpracovavaly ty „widgety“.
public function render() {
$template = $this->template;
$template->setFile(dirname(__FILE__) . '/UserControl.phtml');
$template->registerFilter('CurlyBracketsFilter::invoke');
if ($template->isLogged = $this->_isLogged) {
$template->logoutForm = $this->getComponent('logoutForm');
} else {
$template->loginForm = $this->getComponent('loginForm');
}
$template->render();
}
public function actionLogout() {
$user = new UserModel();
$form = new Form();
$form->addSubmit('logout', 'Odhlásit se');
$form->onSubmit[] = array($this, 'logoutHandler');
$this->addComponent($form, 'logoutForm');
$cUser = $user->getById($_COOKIE['auth']);
$this->template->nick = $cUser['nick'];
$this->_isLogged = $user->isLogged();
}
public function logoutHandler() {
if ($form->isSubmitted()) {
$user->logout();
$this->redirect('this');
}
}
public function actionLogin() {
$user = new UserModel();
$form = new Form();
$form->addText('nick', 'Nick:', 12);
$form->addPassword('password', 'Heslo:', 12);
$form->addSubmit('login', 'Přihlásit se');
$form->onSubmit[] = array($this, 'loginHandler');
$this->addComponent($form, 'loginForm');
$this->_isLogged = $user->isLogged();
}
public function loginHandler() {
if ($form->isSubmitted()) {
$values = $form->getValues();
$cUser = $user->getByNick($values['nick']);
if (md5($values['password']) == $cUser['heslo']) {
$user->login($cUser['id']);
}
$this->redirect('this');
}
}
ted to mam zrejme cele uplne spatne..
- Ondřej Mirtes
- Člen | 1536
Pozor, Control nemá žádný životní cyklus. Formuláře a podobné věci lze vytvářet lazy (on-demand) způsobem pomocí createComponent. Je to k dohledání tady na fóru.
Zpracování formulářů se dělá nejlépe přes události, viz thread, ve kterém jsem to sám potřeboval řešit :)
Editoval LastHunter (1. 4. 2009 11:44)
- David Grudl
- Nette Core | 8228
Honza M. napsal(a):
{$userBox->render()}
se převede na<?php echo htmlspecialchars($userBox->render()) ?>
.Jen takový detail: David řikal kdysi na školení, že když je to objekt, tak se htmlspecialchars nezavolá. Nic to ale nemění na tom, že by se mělo použít {? $userBox->render()}.
Což ale myslím není dobře a chtělo by to změnit.
- pmg
- Člen | 372
Zřejmě se nikomu nechce to po mě převzít, takže dobře. Tím
LoginControl::actionLogin
jsem myslel obecně metodu, která se
zavolá před vykreslováním, aby bylo možné provést redirect.
Tuto metodu je ovšem z presenteru nutné zavolat ručně, nejlépe
asi z metody action*
, která tvoří součást ŽCP a je první
action-specific metodou.
Při použití $form->onSubmit[]
není nutné znovu volat
metodu isSubmitted
. Pro ten logout by možná bylo lepší použít
signál komponenty handleLogout
a nasměrovat na něj akci
formuláře. Pak by se šlo odhlásit i odkazem.
Použití komponenty demonstruje příklad Fifteen v distribuci.
Cituji se pro Davida, protože si nejsem jist, zda to viděl:
Honzo, testoval jsem to, ale zdá se, že se to escapuje. Možná se to měnilo, ale podle mě je to takhle lepší. Také jsem zjistil, že nefunguje zápis {$obj->{‚member‘}}. :-(
- David Grudl
- Nette Core | 8228
{$b->c}
escapuje, cokoliv v {....}
escapuje,
pokud první znak není vykřičník. Že neescapuje objekt je spíš chyba
implementace.
- David Grudl
- Nette Core | 8228
pmg napsal(a):
Cituji se pro Davida, protože si nejsem jist, zda to viděl:
Honzo, testoval jsem to, ale zdá se, že se to escapuje. Možná se to měnilo, ale podle mě je to takhle lepší. Také jsem zjistil, že nefunguje zápis {$obj->{‚member‘}}. :-(
Ano, to je pravda, ale dost těžko řešitelná. Se znaky { } se uvnitř nepočítá.
- trubi
- Člen | 25
Nechal jsem se trochu insirovat jinymi topicy a napsal jsem to takhle:
public function createComponent($name) {
switch ($name) {
case 'loginForm':
$form = new AppForm();
$this->addComponent($form, $name);
$form->addText('nick', 'Nick:', 12);
$form->addPassword('password', 'Heslo:', 12);
$form->addSubmit('login', 'Přihlásit se');
$form->onSubmit[] = array($this, 'loginHandler');
return;
default:
parent::createComponent($name);
return;
}
}
public function loginHandler() {
$values = $form->getValues();
$cUser = $this->user->getByNick($values['nick']);
if (md5($values['password']) == $cUser['heslo']) {
$this->user->login($cUser['id']);
}
$this->redirect('this');
}
public function render() {
$template = $this->template;
$template->setFile(dirname(__FILE__) . '/UserControl.phtml');
$template->registerFilter('CurlyBracketsFilter::invoke');
$template->isLogged = $this->user->isLogged();
$template->form = $this->getComponent('loginForm');
$template->render();
}
Jenomze kdyz se formular pokusim odeslat, vyhodi se vyjimka The signal receiver component ‚userBox-loginForm‘ is not found. Muzete nekdo, snad naposled, poradit? Doufam ze to nebude vypadat, ze jsem natvrdly…diky
- mancze
- Člen | 58
vlki napsal(a):
Neregistruješ si ten
AppForm
k té komponentě, ve které to voláš.Nahraď tedy
$form = new AppForm();
za
$form = new AppForm($this, $name);
Ale
$form = new AppForm();
$this->addComponent($form, $name);
je snad s výše uvedeným ekvivalentní, ne (i když preferuju vlkiho zápis)? Stejně jako
public function loginHandler(Form $form) {
...
}
se mi zdá čistější a přinejmenším čitelnější.
Napadlo mne, jestli tu komponentu „userBox“ registruješ dostatečně brzo v životním cyklu Presenteru?
Editoval mancze (2. 4. 2009 17:13)
- kravčo
- Člen | 721
Nebojte sa na vlákna, na ktoré sa odvolávate odkazovať… Teraz len veľmi ťažko môžeme odhadnúť odkiaľ si čerpal… Predpokladám továrničku, ostatné netuším.
Najprv nejaké pripomienky mimo tému:
- metóda
loginHandler
má ako argument formulár, ktorý bol odoslaný – nie je tam ale čítaš z neho hodnoty (chyba sa ešte nestihla prejaviť). - formuláru by bolo vhodné pridať validáciu (chápem, že pri skúšaní samozrejme máš čo najmenej vecí, skôr do budúcna…)
Problém:
Podľa toho, čo si nenapísal, máš tento formulár v controle, nie v prezenteri – väčšina príkladov na fóre je práve o tom, že je formulár priamo v prezenteri – ale na to si už zjavne prišiel…
Podľa chybovej hlášky je problém v tom, že samotný control (nazvaný
„userBox“), ktorý obsahuje formulár, nie je v čase spracovania
signálov vytvorený. To sa dá docieliť rovnakým spôsobom, ako si
automatizoval vytváranie formulára – implementovaním továrničkovej
metódy createComponent()
u rodiča (predpokladám prezenter).
- kravčo
- Člen | 721
mancze napsal(a):
vlki napsal(a):
$form = new AppForm($this, $name);
Ale
$form = new AppForm(); $this->addComponent($form, $name);
je snad s výše uvedeným ekvivalentní, ne (i když preferuju vlkiho zápis)?
Je.
Výhodou použitia addComponent()
je, že ho nemusíš
registrovať hneď pri vytvorení, ale v podstate kedykoľvek po ňom.
Možno ešte pomôže vlákno Jak na odeslání formuláře z komponenty
Editoval kravco (2. 4. 2009 17:35)