Problem so ziskanim presenterovej premennej

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

Takto to vyzera na zaciatku mojho presenteru

<?php
private $database,$codeOfCongress,$idOfCongress;

    public function __construct(Nette\Database\Context $database)
    {
        $this->database = $database;
        $this->codeOfCongress = "";
        $this->idOfCongress = 0;
    }

?>

a ak chcem pristupit k premennej $this->codeOfCongress v metode ktora sa vola po odoslani formularu tak akoby ani neexistovala.. cache vsetko premazane a refreshnute.
$this->database funguje normalne…

Jan Endel
Člen | 1016
+
0
-

asi tady musíš hodit víc tvého kódu.

figurluk
Člen | 61
+
0
-
<?php

class HomepagePresenter extends BasePresenter
{
    private $database,$codeOfCongress,$idOfCongress;

    public function __construct(Nette\Database\Context $database)
    {
        $this->database = $database;
        $this->codeOfCongress = "";
        $this->idOfCongress = 0;
    }

    public function renderDefault()
    {
        if (!$this->getUser()->isLoggedIn()) {
            $this->redirect('Sign:in');
        }

        $this->template->rows = $this->database->table('congres')->select('id,name,code')->order('id');
        $this->template->breads = array('Kongresy' => '');
    }

    public function renderCongres($congresID,$congresCode, $congresName, $actualPage)
    {
        $this->codeOfCongress = $congresCode;
        $this->idOfCongress = $congresID;

        $this->template->select_sections = $this->database->table($congresCode.'_sections')->select('id,section');
        $this->template->select_hotels = $this->database->table($congresCode.'_hotels')->select('id,hotel');

        $numberOfRows = $this->database->table($congresCode.'_participants')->count();
        $numberOfPages;

        if ($numberOfRows%10 == 0) {
            $numberOfPages = $numberOfRows/10;
        }
        else {
            $numberOfPages = (int)($numberOfRows/10)+1;
        }

        $this->template->countPages = $numberOfPages;
        $this->template->actualPage = $actualPage;
        $this->template->id = $congresID;
        $this->template->code = $congresCode;
        $this->template->name = $congresName;
        $this->template->rows = $this->database->table($congresCode.'_participants')->order('id')->limit(10,($actualPage-1)*10);
        $this->template->breads = array('Kongresy' => 'Homepage:default',$congresName => '');
    }

    protected function createComponentRegisterForm()
    {
        $form = new Form;
        $form->elementPrototype->addAttributes(array('class' => 'my_form'));
        $form->addText("email")->addRule(Form::EMAIL);
        ...

        $form->onSuccess[] = array($this, 'registerFormSucceeded');
        return $form;
    }

    public function registerFormSucceeded($form)
    {
        $values = $form->getValues(TRUE);

        $titul = trim($values['titul'], '.');
        $lastVS = NULL;
        $lastRow = NULL;

        $select = $this->database->table($this->codeOfCongress.'_participants')->select('id,variable_symbol')
            ->where('variable_symbol IS NOT NULL')
            ->order('id DESC')->limit(1);


        foreach ($select as $symbol)
            $lastVS = $symbol->variable_symbol;

        if ($lastVS == NULL) {
            if ($this->idOfCongress < 10) {
                $lastVS = '150'.$this->idOfCongress.'001';
            }
            else{
                $lastVS = '15'.$this->idOfCongress.'001';
            }
        } else {
            $lastVS++;
        }

        if ($values['typUbytovania'] != 0) {
        } else {
        }

        if($values['aktivnaUcast']) {
        }

        $this->flashMessage("Uloženie účastníka prebehlo v poriadku.", 'success');
    }

}

?>

Editoval figurluk (26. 5. 2015 22:47)

David Matějka
Moderator | 6445
+
0
-

render metody se volaji pred po zpracovani formu, viz zivotni cyklus (form se zpracovava na urovni handle*), takze pouzij action metodu (alespon pro naplneni tech tridnich properties)

figurluk
Člen | 61
+
0
-

David Matějka napsal(a):

render metody se volaji pred zpracovanim formu, viz zivotni cyklus (form se zpracovava na urovni handle*), takze pouzij action metodu (alespon pro naplneni tech tridnich properties)

No ved to je dobre ze sa volaju pred zpracovanim formu… tym padom by tam ta premenna mala byt ne ?
Ako je mozne ze database tam je a funguje ale tie zvysne 2 nefunguju…

Editoval figurluk (26. 5. 2015 22:59)

David Matějka
Moderator | 6445
+
0
-

@figurluk typo :) render metody se volaji po zpracovani formu

tudiz po odeslani dojde ke zpracovani a az pote by se zavolala ta render metoda, kde naplnujes ty properties

Editoval David Matějka (26. 5. 2015 23:03)

figurluk
Člen | 61
+
0
-

David Matějka napsal(a):

@figurluk typo :) render metody se volaji po zpracovani formu

teraz to uz dava zmysel .. akurat moj problem je ze tie 2 premenne viem naplnit az ked prechadzam z default.latte na congres.latte to znamena prave v metode renderCongres

David Matějka
Moderator | 6445
+
+1
-

tady jde pouze o zivotni cyklus presenteru v ramci jednoho requestu – nejdriv se zavola metoda actionCongres (kterou nemas, ale musis ji vytvorit) – v ni prave naplnis ty tridni properties, potom se zavola zpracovani a potom renderCongres

figurluk
Člen | 61
+
0
-

David Matějka napsal(a):

tady jde pouze o zivotni cyklus presenteru v ramci jednoho requestu – nejdriv se zavola metoda actionCongres (kterou nemas, ale musis ji vytvorit) – v ni prave naplnis ty tridni properties, potom se zavola zpracovani a potom renderCongres

Dakujem !!!

Blujacker
Člen | 89
+
0
-

Komponenty nemaji stejny zivotni cyklus jako presentery, action<> metody nefunguji. Je spravne tridni properties plnit v metode attached?

public function attached($presenter){
    parent::attached($presenter);
    $this->template->x = $x;
    $this->var = $this->model->getVar($presenter->user->id);
    }
Unlink
Člen | 298
+
+1
-

Ano, len $presenter nemusí byť presenter, pretože komponenty môžeš do seba ľubovoľne vnárať

protected function attached($component)
{
	parent::attached($component);
	if ($component instanceof \Nette\Application\UI\Presenter)
	{
		//Tuna môžeš
	}
}

Poprípade v metode render, záleží od toho, čo potrebuješ

Editoval Unlink (28. 5. 2015 9:50)

Blujacker
Člen | 89
+
0
-

V render se mi to prave nehodilo, protoze k nim potrebuji pristupovat napr. pri odeslani formulare a to se vola pred render.

Dekuji!

Zax
Člen | 370
+
0
-

Blujacker napsal(a):

Komponenty nemaji stejny zivotni cyklus jako presentery, action<> metody nefunguji. Je spravne tridni properties plnit v metode attached?

public function attached($presenter){
    parent::attached($presenter);
    $this->template->x = $x;
    $this->var = $this->model->getVar($presenter->user->id);
    }

Jak píše Unlink, ano, jde to. Ale doporučuji zvážit, zda by nebylo lepší to obrátit a nechat presenter, aby proměnné předal třeba přes setter, protože jinak vytváříš skrytý závislosti.

// v komponentě
public function setFoo($foo) {
	$this->foo = $foo;
	return $this; // fluent
}
// v presenteru
function createComponentAbc() {
	return $this->abcFactory->create()
		->setFoo($this->getFoo());
}

A pokud nepotřebuješ nic vyloženě z presenteru, ale jen třeba z usera nebo jiný služby, tak si tu službu prostě vyžádej v konstruktoru komponenty a nemusíš vůbec „čekat“ na presenter.

Blujacker
Člen | 89
+
0
-

Vsak ja si danou sluzbu vyzadam v konstruktoru komponenty, ale obcas je potreba naplnit formular i template nejakymi daty z modelu a bez pretizeni attached metody bych to musel volat dvakrat (v render a createComponent, resp. v createComponent kontrolovat jestli uz je to inicializovany a pokud ne tak zavolat).

Pokud bych si vsechny service predal do presenteru a pak je predaval do komponenty (kterou bych pouzival v nekolika presenterech) tak bych pak pri jedne zmene zavislosti musel menit vsechny presentery takze je lepsi si to predat primo do komponenty, nebo ne? – DI si to sam najde a vsechny zavislosti sam preda bez nutnosti toho aby presenter plnil funkci DI kontejneru.

Pokud to dobre chapu tak kazdy potomek \Nette\Application\UI\Control ma pristup na presenter pres $this->presenter takze kdyz se chci dostat na usera tak staci $this->presenter->user... a nemusim si predavat zavislost na \Nette\Security\User – nebo je lepsi reseni nespolehat na presenter a predat si to?

Zax
Člen | 370
+
0
-

Asi nechápu tvůj problém.. jediné místo, kde komponenta není připojená k presenteru, je konstruktor. Opravdu. Rozebereme si to velmi zjednodušeně na jednotlivý kroky:

  1. Presenteru přijde požadavek – „uživatel odeslal formulář form v komponentě control
  2. Presenter zkusí vytáhnout formulář z komponentového stromu – zavolá $this->getComponent('control-form')
  3. To se rozloží na $this->getComponent('control')->getComponent('form')
  4. Metoda getComponent zjistí, že komponenta control neexistuje a zavolá $this->createComponent('control')
  5. Tato metoda podle názvu zavolá příslušnou metodu createComponentControl
  6. Vytvořená komponenta se připojí k presenteru a až pak ji metoda getComponent vrátí
  7. Komponentu control máme, je připojená a až teď z ní dolujeme form – getComponent('form')createComponent('form') → createComponentForm()

Naprosto stejně to funguje i když změníme první krok na:

1. Jsme už v render fázi a chceme vykreslit formulář form v komponentě control

Tahání Nette služeb (user, session, request) z presenteru asi není „trestné“ a bude ti to fungovat, ale konstruktor je určitě „správnější“ – třída se ke svým závislostem otevřeně hlásí a vyžaduje je, netahá si je pokoutně z nějakého service locatoru (což je vlastně to, na co ten presenter tak trochu degraduješ). Pokud si takto taháš vlastní služby, tak je to určitě mnohem horší, přesně jak píšeš – změníš závislost a jde ti to do kopru, nemůžeš komponentu použít v jiném presenteru. Komponenta slepě věří tomu, že v tom presenteru ta služba/proměnná/getter/cokoliv pod tím konkrétním názvem je a to je přece trochu padlý na hlavu, ne? ;-)

BTW pokud potřebuješ stejný data nasypat jak do formuláře tak do šablony, tak není nic jednoduššího:

private $data;

function getData() {
	if($this->data === NULL) {
		$this->data = $this->model->getData();
	}
	return $this->data;
}

function render() {
	$this->template->data = $this->getData();
	// render
}

function createComponentForm() {
	//...
	$data = $this->getData();
	//...
}

Editoval Zax (29. 5. 2015 3:42)

Blujacker
Člen | 89
+
0
-

Svoje vlastni zavislosti komponente predavam pres konstruktor, slo mi prave o seesion, user, request, ktere jsem tahal z presenteru. Takze best practice je i tyto zavislosti si predavat pres konstruktor, koukal jsem do Nette sandboxu a tam to tak opravdu je v komponente. Ale nemelo by se tedy v presenteru https://github.com/…resenter.php#L34 take predat sluzba \Nette\Security\User a nepouzivat presenter->getUser()? Nebo primo v presenteru je toto v poradku?

Jinak se vsim souhlasim, az na ten priklad ktery uvadis, nechtelo se mi psat do dvou metod (render, createComponent) volani $this->getData(), chtel jsem to mit pouze na jednom miste, prislo mi to prehlednejsi mit na jednom miste, proto jsem dal tu inicializaci do attached metody.

Zax
Člen | 370
+
+2
-

Blujacker napsal(a):

Svoje vlastni zavislosti komponente predavam pres konstruktor, slo mi prave o seesion, user, request, ktere jsem tahal z presenteru. Takze best practice je i tyto zavislosti si predavat pres konstruktor, koukal jsem do Nette sandboxu a tam to tak opravdu je v komponente. Ale nemelo by se tedy v presenteru https://github.com/…resenter.php#L34 take predat sluzba \Nette\Security\User a nepouzivat presenter->getUser()? Nebo primo v presenteru je toto v poradku?

Jinak se vsim souhlasim, az na ten priklad ktery uvadis, nechtelo se mi psat do dvou metod (render, createComponent) volani $this->getData(), chtel jsem to mit pouze na jednom miste, prislo mi to prehlednejsi mit na jednom miste, proto jsem dal tu inicializaci do attached metody.

V presenteru je to ok, z UI\Presenter dědíš, takže není důvod nepoužívat jeho služby, pořád pracuješ s injektnutými službami v rámci jednoho objektu, netaháš si to „odněkud“.

U komponent je to horší v tom, že nemáš jistotu, jaký presenter si přes $this->presenter vytáhneš. Jasně – v rámci jednoho projektu je to asi úplně jedno, ale pak třeba převezmeš nějaký zprasený projekt, budeš tam chtít přidat tu svou komponentu, protože ti rychle vyřeší nějaký problém, jenže zjistíš, že to prostě nefunguje – třeba tam někdo přetížil metodu getSession a zrovna se session pracuješ, nebo třeba prostě najednou pracuješ s jinou verzí Nette (příkladem budiž metoda getApplication(), která je normálně v presenteru v 2.0.0, v 2.1.0 je deprecated „use dependency injection instead“ a v 2.2.0 už není vůbec).

S tím attached – já asi uvažuju prostě „obráceně“ :-D Mně přijde lepší mít metodu, kterou získám data, a volat ji a plnit daty co kde potřebuju. Takovej zdroják komponenty pak vypadá třeba takto:

  • __construct
  • getData
  • createComponentForm
    • vytvoř form
    • naplň form
  • render
    • naplň šablonu
    • vyrenderuj

A je naprosto jasné, co se kde děje, i když se k tomu vrátím za půl roku. Řešit to v attached mi prostě přijde neintuitivní, takový lehce WTF:

  • __construct
  • createComponentForm
    • vytvoř form
  • render
    • vyrenderuj
  • attached
    • naplň form
    • naplň šablonu

Ale zas jestli ti to vyhovuje to mít takhle na jednom místě, tak asi proč ne.. Ber to tak, že tu jen píšu tipy, co se mně osobně osvědčily, a přeber si to jak chceš ;-) Nechci nikomu nic diktovat, ani říkat že moje cesta je jediná správná, jsem jen takový samouk a dost sólista (zčásti protože jsem asociál :-D a zčásti protože zatím jsem neměl tu čest programovat po boku někoho, kdo si na čistotu a kvalitu kódu potrpí) a určitě toho dělám spoustu špatně… ;-) Ale zase rád poradím, to né že ne, i kdyby blbě, tak mě dyžtak někdo opraví a já budu zas o něco chytřejší :-)