nejlepší konstrukce pro formulář „výběr dat->úprava dat->uložení dat a zobrazení úspěchu“

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

Mohl by mi někdo poradit jak nejlépe sestavit tuto konstrukci/v čem je špatná moje verze?:
1.formulář – výběr řádku z databáze, který chci upravit (řádku odpovídá objekt Description)
2.následuje další formulář obsahuje data z vybraného řádku volné k editaci
3.následuje uložení

já to udělal takto(a moc se mi to nelíbí):
šablona description při načtení zobrazí widget selectForm (bod 1), po jeho odeslání se v redirectu té samé šabloně předá seoUrl vybraného řádku v db… vzhledem k tomu, že po redirectu se nevolá action, tak v render si šáhnu do db pro potřebný řádek, pošlu ho dál do šablony + uložím do proměnné $this->activeItem.

šablona pokud dostane objekt, zobrazí widget editDescriptionForm (bod 2) .. createComponentEditDescriptionForm si vezme data z $this->activeItem a použije je jako hodnoty do formuláře, pole které se neupravují jsou hidden.. celé to musí být ošetřeno jestli $this->activeObject je object.
Po odeslání vytáhnu data z formuláře a vytvořím objekt Description a zavolám metodu save() která už se postará o update, opět přesměruji na tu samou šablonu, která zobrazí flash message a opět úvodní výběr řádku z db..

toto řešení se mi zdá těžkopádné, zkoušel jsem to vyřešit tak, aby byl ten objekt v presenteru stále a já jen u něj pomocí formulářů změním jeden parametr a uložím, jenže se mi vždycky po submitu smazal a vyhodilo mi to hlášku „caling bla bla of a non-object…“ zkoušel jsem i static i persistent.. bez úspěchu..

Ani
Člen | 226
+
0
-

To moc nechápu, ale tak udělej si dvě view (něco jako selectrow a editrow) prvně zobrazíš ten select se selectForm, po provedení provedeš redirect na ten editrow, jako parametr předáš id toho řádku. V tom editrow si to vytáhneš z DB, zedituješ a přesměruješ s flash message. Můžeš tam dát dvě tlačítka a podle těch to přesměruješ na danej view.

Ofi
Člen | 13
+
0
-

v podstatě mi na tom nevoní fakt, že dvakrát budu tahat jeden a ten samý řádek z databáze a tříkrát budu dělat stejný objekt… poprvé při načtení dat do selectu a podruhé při načtení dat pro edit a třetí vytvoření objektu bude při převedení dat z formuláře pro uložení… a to bych vlastně tahal třikrát z db, kdybych si neudělal v presenteru tu pomocnou proměnnou…

já vím, že časové to je nula k nule, ale není to „čisté“, není to „chytré“… prostě mi to nesedí a nevím jak jinak to řešit…

nechápu proč mi nefungovalo použití statické proměnné presenteru…

Editoval Ofi (8. 9. 2010 23:33)

Ani
Člen | 226
+
0
-

A co teda dělá ten selectForm? Jsem myslel, že tam jsou úplně jíná data (id a název všech řádků). A ten editační má už jen konkrétní data z toho řádku. To máš dva jiný dotazy do db.

Asi nechápu, co to vlastně dělá.

Editoval Ani (8. 9. 2010 23:52)

arron
Člen | 464
+
0
-

mozna by to chtelo kousek kodu:-)

Patrik Votoček
Člen | 2221
+
0
-

Obávám se že tohle ani jinak řešít nebude… PHP „neumí“ mezi jednotlivímy požadavky přenášet oběkty…

Můžeš to buď „kešovat“ nebo ukládat do session a nebo se smířít s tím že 3 různé požadavky šahaní 3× do DB…

Ofi
Člen | 13
+
0
-

keš je v nette souborová nemýlím-li se, takže je to buď db nebo file, sessions mě napadly také, k jejich vyzkoušení jsem se ještě nedostal…

já si ve všech případech tahám všechno, protože to má 4 sloupce a z tech bych vždycky tahal 2–4…
takhle vypadá příslušný presenter..

class Admin_DefaultPresenter extends Admin_SecuredPresenter {

    private $contentManager = NULL;
    private $activeItem = NULL;

    public function actionLogout()  {
        Environment::getUser()->logout();
        $this->flashMessage('Právě jste sa odlásili z administrace.');
        $this->redirect('Auth:login');
    }

    public function actionDescrtiption() {
    }

    public function renderDescription($itemSelected = NULL) {
        $this->activeItem = $this->model->getDescription($itemSelected);
        $this->template->description = $this->activeItem;

        $this->template->itemSelected = $itemSelected;
    }

    public function createComponentEditDescriptionForm() {
        $form = new AppForm();

        $form->addHidden("id");
        $form->addHidden("menuItem");
        $form->addHidden("seoUrl");
        $form->addTextArea("description", "Popis: ");
        $form->addSubmit("send", "Změnit popis");

        if(is_object($this->activeItem)) {
        $form->setValues(array('description'=>$this->activeItem->description,
                                'id'=>$this->activeItem->id,
                                'menuItem'=>$this->activeItem->menuItem,
                                'seoUrl'=>$this->activeItem->seoUrl
                        ));
        }
        $form->onSubmit[] = callback($this, 'processEditDescriptionForm');

        return $form;
    }

    public function processEditDescriptionForm(Appform $form) {
        if($form['send']->isSubmittedBy()) {

            $values = $form->getValues();

            $properties = array();
            foreach($values as $key=>$val) {
                $properties[$key]=$val;
            }

            $updatedItem = new Description($properties);

            $updatedItem->save();

            $this->flashMessage('Popis byl úspěšně změněn.');
            $this->redirect('Default:description');
        }
    }

    public function createComponentSelectDescription() {
        $form = new AppForm();

        $descriptions = $this->model->getDescriptions();
        $items = array();

        foreach ($descriptions as $d) {
            $items[$d->seoUrl] = $d->menuItem;
        }

        $form->addSelect('description', 'Položka: ', $items);
        $form->addSubmit('send', 'Vybrat');

        $form->onSubmit[] = callback($this, 'processSelectDescription');
        return $form;
    }

    public function processSelectDescription($form) {
        if($form['send']->isSubmittedBy()) {

            $values = $form->getValues();

            $this->redirect('description', $values['description']);
        }
    }

    public function getModel() {
        if(!isset($this->contentManager))
            $this->contentManager = new ContentManager;

        return $this->contentManager;
    }
}

a takhle příslušná šablona:

{block content}
<h3>Vyberte, pro kterou položku si přejete změnit popis:</h3>
{widget selectDescription}
{if isset($itemSelected) && $itemSelected != NULL}
<h3>
    Úprava popisu:
</h3>
{widget editDescriptionForm}
{/if}
{/block}

čtení flash message mám v @layout takže o to se v téhle šabloně nestarám..

Ani
Člen | 226
+
0
-

Přijde mi to celý zbytečně komplikovaně řešený.

  • Za cenu úspory dotazu do db, tam děláš různý iterace přes celý pole (ta v processEditDescriptionForm mi přijde obzvlášť nelogická).
  • V těch process metodách nemusíš dělat $form[‚send‘]->isSubmittedBy(), když to máš jako appform s jedním tlačítkem (raději koukni do dokumentace).
  • Stejně tak předávání těch hidden polí je pěkně kostrbaté a kromě id, nejspíš zbytečné.
  • V renderDescription předáváš do šablony ten objekt, ale v ní ho pak nepoužíváš.
<?php
    public function renderDescription($itemSelected = NULL) {
        $activeItem = $this->model->getDescription($itemSelected);

        $this->template->description = $activeItem; // to tam potrebujes?
        $this->template->itemSelected = $itemSelected;
	if ($itemSelected != null)
	$this['editDescriptionForm']->setDefaults($activeItem); //tady to mozna bude chtit upravit na array(id => $activeItem->id, description => $activeItem->description)
    }

    public function createComponentEditDescriptionForm() {
        $form = new AppForm();
        $form->addHidden("id");
        $form->addTextArea("description", "Popis: ");
        $form->addSubmit("send", "Změnit popis");
        $form->onSubmit[] = callback($this, 'processEditDescriptionForm');
        return $form;
    }

    public function processEditDescriptionForm(Appform $form) {
            $values = $form->getValues();

	    $updatedItem = new Description($values);
            $updatedItem->save();

            $this->flashMessage('Popis byl úspěšně změněn.');
            $this->redirect('Default:description');
        }
    }

    public function createComponentSelectDescription() {
        $form = new AppForm();

        $descriptions = $this->model->getDescriptionsPairs();

        $form->addSelect('description', 'Položka: ', $descriptions);
        $form->addSubmit('send', 'Vybrat');

        $form->onSubmit[] = callback($this, 'processSelectDescription');
        return $form;
    }

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

            $this->redirect('description', $values['description']);
        }
    }
?>

Takhle potřebuješ dva dotazy (celý jeden řádek) a pak druhý jako všechny řádky…

Editoval Ani (9. 9. 2010 19:25)

Ofi
Člen | 13
+
0
-

dnes dopoledne jsem refaktoroval frontend a:

Ani napsal(a):

Přijde mi to celý zbytečně komplikovaně řešený.

  • Za cenu úspory dotazu do db, tam děláš různý iterace přes celý pole (ta v processEditDescriptionForm mi přijde obzvlášť nelogická).

už není – nevím proč jsem jí tam dával :D

  • V těch process metodách nemusíš dělat $form[‚send‘]->isSubmittedBy(), když to máš jako appform s jedním tlačítkem (raději koukni do dokumentace).

to jsem nevěděl… čili to ještě změním

  • Stejně tak předávání těch hidden polí je pěkně kostrbaté a kromě id, nejspíš zbytečné.

ano to už tam také nemám krom toho id…

  • V renderDescription předáváš do šablony ten objekt, ale v ní ho pak nepoužíváš.

také už je minulost, všiml jsem si toho…

nakonec jsem to zjednodušil, ale i tak.. teď mě to trápí, protože až přijde nějaký projekt kde si budu muset znovu a znovu sahat pro daleko větí objem dat tak si s tím nebudu vědět rady…