Předání parametru z Action do komponenty formuláře (pro zpracování formuláře)

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

Zdravím,
řeším takový drobný refactoring presenterů a formulářů. Chtěl bych se zeptat, jestli a jak je možné předat parametr z action pro vytvoření formuláře v komponentě do zpracování formuláře.

Příklad:

Presenter:

class ItemPresenter extends BasePresenter
{
 	/** @var FormFactory
     * @inject
     */
    public $formFactory;

	protected function createComponentForm()
    {
        $form = $this->formFactory->create();
        $form->onSuccess[] = function ($form) {
            if ($this->id_item)
                $form->getPresenter()->redirect('.....');
            else
                $form->getPresenter()->redirect('.....');
        };
        return $form;
    }

 	public function actionForm($guid)
    {
        if(!$this->user->isLoggedIn()) {
            $this->redirect('Sign:in');
        }

        if ($guid) { //pokud GUID existuje jedná se o edit existujícího, je nutné načíst data do formu

            $this->id_item = $this->itemRepository->getItemID($guid);

            if (!$this->id_item) {
                throw new BadRequestException;
            }

            if (!$this->itemManager->authenticateItemOwner($this->id_item)) {
                $this->error('Toto není Váš předmět!');
            }

            $values = $this->itemRepository->getItemFormData($this->id_item);

             $this['form']->setDefaults($values);
        }
    }
}

FormFactory:

class FormFactory extends Nette\Object
{

	/** @var Nette\Security\User */
    public $user;

    /** @var \App\Model\ItemManager */
    public $itemManager;

    protected $translator;

    public $id_item;

    public function __construct($id_item = NULL, Nette\Security\User $user, Model\ItemManager $itemManager, \Kdyby\Translation\Translator $translator)
    {
        $this->id_item = $id_item;
        $this->user = $user;
        $this->itemManager = $itemManager;
        $this->translator = $translator;
    }

 	public function create()
    {
        $form = new Form;
		//....
	    $form->onSuccess[] = array($this, 'formSucceeded');
		return $form;

 	public function formSucceeded($form, $values)
    {
        if(!$this->user->isLoggedIn()) {
            $this->getPresenter()->redirect('Sign:in');
        }

        if($this->id_item){ // zde si potřebuji sáhnout na $this->id_item z actionForm ItemPresenteru
            if(!$this->itemManager->authenticateItemOwner($this->id_item)){
            	$this->error('Toto není Váš předmět!');
            }

        	$this->ItemManager->update($this->id_item, $values);
        }
		else {
        	$this->ItemManager->add($values);
		}
    }

Aktuálně ho předávám do create formuláře pošlu ho jako hidden pole. Následně jsem zkoušel výše uvedený postup, ale bohužel se ID nepřenese… Prosím tedy o radu co dělám špatně. Díky moc

Editoval Croc (6. 11. 2015 10:20)

F.Vesely
Člen | 369
+
0
-

Ja to delam takhle:

class EditItemControl extends Control
{
	public $onSave = [];
	/** @var Nette\Security\User */
    protected $user;

    /** @var \App\Model\ItemManager */
    protected $itemManager;

    protected $translator;

    protected $id_item;

    public function __construct($id_item = NULL, Nette\Security\User $user, Model\ItemManager $itemManager, \Kdyby\Translation\Translator $translator)
    {
        $this->id_item = $id_item;
        $this->user = $user;
        $this->itemManager = $itemManager;
        $this->translator = $translator;
    }

	public function render()
	{
		$this['form']->render();
	}

	protected function createComponentForm()
	{
		$form = new Form;
        //....
        $form->onSuccess[] = array($this, 'formSucceeded');

		$values = $this->itemRepository->getItemFormData($this->id_item);

        $form->setDefaults($values);

        return $form;
	}

	public function formSucceeded($form, $values)
    {
        if(!$this->user->isLoggedIn()) {
            $this->getPresenter()->redirect('Sign:in');
        }

        if($this->id_item){ // zde si potřebuji sáhnout na $this->id_item z actionForm ItemPresenteru
            if(!$this->itemManager->authenticateItemOwner($this->id_item)){
                $this->error('Toto není Váš předmět!');
            }

            $this->ItemManager->update($this->id_item, $values);
        }
        else {
            $this->ItemManager->add($values);
        }

		$this->onSave();
    }
}
interface IEditItemControlFactory
{
	/**
	 * @return EditItemControl
	 */
	public function create($id_item = null);
}
class ItemPresenter extends BasePresenter
{
    /** @var IEditItemControlFactory
     * @inject
     */
    public $formFactory;

    protected function createComponentForm()
    {
        $form = $this->formFactory->create($this->id_item);
        $form->onSave[] = function () {
            if ($this->id_item)
                $this->redirect('.....');
            else
                $this->redirect('.....');
        };
        return $form;
    }

    public function actionForm($guid)
    {
        if(!$this->user->isLoggedIn()) {
            $this->redirect('Sign:in');
        }

        if ($guid) { //pokud GUID existuje jedná se o edit existujícího, je nutné načíst data do formu

            $this->id_item = $this->itemRepository->getItemID($guid);

            if (!$this->id_item) {
                throw new BadRequestException;
            }

            if (!$this->itemManager->authenticateItemOwner($this->id_item)) {
                $this->error('Toto není Váš předmět!');
            }
        }
    }
}
Croc
Člen | 270
+
0
-

Díky za tip, mám ale ještě otázku.

Jak jsem uvedl v příkladu výše, když volám metodu $values = $this->itemRepository->getItemFormData($this->id_item);, tak tyto data použiju pro naplnění formu, ale 3 parametry zobrazuju také v té šabloně (součást nadpisu) a hlavně 2 parametry potřebuji už při vytváření formu pro načtení hodnot do dvou selectů ve fromuláři.

Pokud přemístím toto volání do createComponentForm() ve třídě EditItemControl tak mi ty data budou chybět v šabloně a při vytváření formuláře pro selecty. Musel bych tedy volat DB znovu v action presenteru pro získání těchto hodnot.

Data z metody getItemFormData($this->id_item) tedy použiji celkem 3x:

  1. V šabloně jako nadpis
  2. Při vytvoáření formu pro načtení hodnot do dvou selectů
  3. Pro vyplnění defaultních hodnot formu

Existuje tedy i jiné řešení této situace?

Editoval Croc (6. 11. 2015 14:19)

greeny
Člen | 405
+
0
-

Předat si místo IDčka do formuláře veškerá data a tím pádem to volat jen jednou v presenteru.

Croc
Člen | 270
+
0
-

Bohužel mi dle výše uvedeného způsobu nejde předat ID ani všechny data, evidentně dělám něco špatně. Zde je můj kód:

ItemFormFactory.php

namespace App\Forms;

use Nette,
    Nette\Application\UI\Control,
    Nette\Application\UI\Form,
    App\Model,
    App\Repository;


class ItemFormControl extends Control
{
    public $onSave = [];

    /** @var Nette\Security\User */
    protected $user;

    /** @var \App\Model\UtilsManager */
    protected $utilsManager;

    /** @var \App\Repository\TypeRepository */
    protected $typeRepository;

    /** @var \App\Model\ItemManager */
    protected $itemManager;

    protected $translator;
    protected $item = NULL;
    protected $category = 'xy';


    public function __construct($category = 'xy', $item = NULL, Nette\Security\User $user, Model\UtilsManager $utilsManager, Model\ItemManager $itemManager,  Repository\TypeRepository $typeRepository, \Kdyby\Translation\Translator $translator)
    {
        $this->item = $item;
        $this->category = $category;

        $this->user = $user;
        $this->utilsManager = $utilsManager;
        $this->itemManager = $itemManager;
        $this->typeRepository = $typeRepository;
        $this->translator = $translator;
    }

    public function render()
    {
        $this['itemForm']->render();
    }

    public function createComponentItemForm()
    {

        $form = new Form;

        $form->setTranslator($this->translator->domain('forms'));
		//...
        $form->addSelectize('id_category', 'labels.category_name', $this->utilsManager->loadCategory($this->translator->getLocale(), $this->category));
		//....
        $form->addSelectize('param', 'labels.param_name', $this->typeRepository->getTypesPairs($this->category));

        $form->addProtection('forms.time_limit');
        $form->addSubmit('send', 'buttons.send');
	    $form->onSuccess[] = array($this, 'formSucceeded');

        $this['itemForm']->setDefaults($this->item);

		return $form;

	}

    public function formSucceeded($form, $values)
    {
        if(!$this->user->isLoggedIn()) {
            $this->getPresenter()->redirect('Sign:in');
        }

        if($this->item){

            if(!$this->itemManager->authenticateItemOwner($this->item->id_item)){
                $form->getPresenter()->redirect('Homepage:');
            }
        }

        $this->itemManager->addItem($values, $this->item->id_item);

        $this->onSave();
    }
}

    interface IItemFormControl
    {
        /**
        * @return ItemFormControl
        */
        public function create($category = 'xy', $item = null);
    }

ItemPresenter.php

<?php

namespace App\Presenters;

use Nette,
    App\Form,
    App\Forms\IItemFormControl;

class ItemPresenter extends BasePresenter
{
     /** @var IItemFormControl
     * @inject
     */
    public $itemFactory;

	private $database;

    private $item;
    private $guid;
    private $id_item;
    private $category;


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

	protected function createComponentItemForm()
    {
        $form = $this->itemFactory->create($this->category, $this->item);
        $form->onSave[] = function ($form) {
            if ($this->guid)
                $form->getPresenter()->redirect('Item:profile', array('guid' => $this->guid));
            else
                $form->getPresenter()->redirect('User:items');
        };
        return $form;
    }

	  public function actionForm($guid, $category)
    {
        if(!$this->user->isLoggedIn()) {
            $this->redirect('Sign:in');
        }

        if ($guid) {
            $this->id_item = $this->itemRepository->getItemID($guid);

            if (!$this->id_item) {
                throw new BadRequestException;
            }

            if (!$this->itemManager->authenticateItemOwner($this->id_item)) {
                $this->error('Toto není Váš předmět!');
            }

            $values = $this->itemRepository->getItemFormData($this->id_item);

            $this->item = $values;
            $this->guid = $values['guid'];
            $this->category = $values['category'];

            if ($values['id_type']) {
                $row = $this->itemRepository->getItem($this->id_item);
                $this->type_name = $row['type_name'];
            } else {
                $this->type_name = $values['type_name'];
            }
        } else {
            $this->category = $category;
        }
    }

    public function renderForm()
    {
        $this->template->item = $this->item;
        $this->template->type_name = $this->type_name;
        $this->template->category = $this->category;
    }
}

Uvedený kód se vůbec neprovede. Stránka se chvilku načítá a pak prohlížeč (ne Tracy) napíše „Spojení bylo přerušeno – Spojení se serverem bylo v průběhu načítání stránky ukončeno“.

Prosím nevíte kde bych mohl mít chybu? Díky moc

EDIT: Kód upraven dle aktuálního stavu. Dodávám kód šablony:
item/form.latte

{block content}

	<div class="page-header">
		{if $item}
			<h3>{$item->item_name} {$item_abb}</h3>
		{else}
			{if $category == 'xy'}
				<h3>......</h3>
			{else}
				<h3>.....</h3>
			{/if}
		{/if}
	</div>

	{control itemForm, $item, $category} //zkoušel jsem toto
	{control itemForm} //zkoušel jsem i toto
{/block}

Editoval Croc (7. 11. 2015 0:42)

greeny
Člen | 405
+
0
-
  1. ukaž ještě šablonu komponenty
  2. v továrničce máš přeházené parametry oproti konstruktoru
  3. locale si nemusíš předávat, vzhledem k tomu, že ho máš v Translatoru, který si také předáváš
Croc
Člen | 270
+
0
-
  1. přidáno do předchozího postu
  2. opraveno
  3. aha, to je dobrá zpráva :) opraveno → předpokládám že hodnota se získá: $this->translator->getLocale()

Hele, když je formulář jako komponenta, nemá mít náhodou vlastní šablonu ve které se definuje vykreslení jednotlivých prvků?

Všechny změny jsem provedl do předchozího postu.

EDIT: Upravil jsem nastavení localhostu dle tohoto tématu, a Tracy promluvila.

Fatal Error

Maximum execution time of 30 seconds exceeded

Formulář se načítal pořád dokola ve smyčce než po 30s padnul. Mohl za to tento řádek:

$this['itemForm']->setDefaults($this->item);

// po tom co jsem ho změnil na:

	if($this->item) {
		$form->setDefaults($this->item);
	}
// se formulář načte bez problému!

A předávání parametru také funguje. Super, díky za rady :)

Editoval Croc (7. 11. 2015 11:20)