Problem s redirect

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

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
+
0
-

{$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
+
0
-

{$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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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‘}}. :-(

pmg
Člen | 372
+
0
-

Ještě k tomu escapování. Chápal jsem to tak:

{$a}	escapuje vždy
{$b->c}	neescapuje, protože je tam ->

Ale se rozhodne podle předané hodnoty, tak to je jiná.

David Grudl
Nette Core | 8228
+
0
-

{$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
+
0
-

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
+
0
-

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

vlki
Člen | 218
+
0
-

Neregistruješ si ten AppForm k té komponentě, ve které to voláš.

Nahraď tedy

$form = new AppForm();

za

$form = new AppForm($this, $name);

Edit: Pardon, nevšiml jsem si toho volání metody addComponent. Viz příspěvek od mancze níže.

Editoval vlki (2. 4. 2009 17:19)

mancze
Člen | 58
+
0
-

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
+
0
-

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:

  1. 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ť).
  2. 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
+
0
-

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)

trubi
Člen | 25
+
0
-

Diky moc vsem! funguje, pomohlo implementovat createComponent() v rodicovskem presenteru. Uz tomu asi zacinam prichazet na kloub ;)