Editace formuláře – InvalidArgumentException
- iNyxLadis
- Člen | 48
Zdravím, prokousávám se i nadále Nettem. Mám rozeběhlý dependent select box s Ajaxem z návodu na planette. Vše je OK, problém však nastává, pokud chci editovat formulář, ve kterém se vyskytuje jeden selectbox a jeden na něm závislý selectbox. Při zavolání actionEdit dostanu InvalidArgumentException, konkrétně Value ‚1‘ is out of allowed set [] in field ‚Prj_ID‘. Moc tomu nerozumím, protože právě tu jedničku tam očekávám a potřebuji ji tam.
Laděnka mi označí vzhledem k erroru tuto část kódu. Zkoušel jsem to přepsat i trochu jinak, ale vždy se dopracuji ke stejné chybě.
Presenter, funkce actionEdit
public function actionEdit($id)
{
if ($id) {
($task = $this->timesheetManager->getTask($id)) ? $this['timesheetForm']->setDefaults($task) : $this->flashMessage('Úkol nenalezen.');}
}
EDIT: Ještě zmíním, že před aplikací řešení dependent selectboxu mi editační form fungoval. Po implementaci řešení selectboxů jsem změnil předávání dat při zpracování formuláře z $values = $form->getValues() na $values = $form->getHttpData();. Nevím, jestli to má s tím spojitost, či nikoliv.
Mohu poprosit zkušenější o radu a drobné vysvětlení, k čemu tam vlastně dochází? Děkuji
Editoval iNyxLadis (29. 11. 2016 12:57)
- Jan Mikeš
- Člen | 771
Je důležité porozumět příčině problému – nette vyhazuje výjimku,
v případě, že nastane pokus o nastavení výchozí hodnoty u choice
controlu (selectbox, radio) a hodnota, kterou se pokusíš nastavit jako
výchozí není v seznamu povolených hodnot (setItems(...)
), pak
nette vyhodnotí situaci jako pokus o útok → podstrčení nepovolené
hodnoty.
Musíš si být jist, že ve chvíli, kdy se snažíš nastavit hodnotu prvku, tak prvek má už načtené hodnoty v závislosti na hodnotě předchozího inputu.
Ukaž prosím v jaké fázi životního cyklu presenteru nastavuješ
->setItems()
prvku, kterému chceš nastavit výchozí
hodnotu.
- iNyxLadis
- Člen | 48
Jan Mikeš napsal(a):
Je důležité porozumět příčině problému – nette vyhazuje výjimku, v případě, že nastane pokus o nastavení výchozí hodnoty u choice controlu (selectbox, radio) a hodnota, kterou se pokusíš nastavit jako výchozí není v seznamu povolených hodnot (
setItems(...)
), pak nette vyhodnotí situaci jako pokus o útok → podstrčení nepovolené hodnoty.Musíš si být jist, že ve chvíli, kdy se snažíš nastavit hodnotu prvku, tak prvek má už načtené hodnoty v závislosti na hodnotě předchozího inputu.
Ukaž prosím v jaké fázi životního cyklu presenteru nastavuješ
->setItems()
prvku, kterému chceš nastavit výchozí hodnotu.
Opět děkuji za popíchnutí, dává mi to smysl. Přidal jsem tedy setItems ke zmíněnému selectboxu Prj_ID viz
protected function createComponentTimesheetForm()
{
$form = new Form;
$form->addHidden('Tsk_ID');
$form->addSelect('Cmp_ID', 'Firma', $this->timesheetManager->getCompanies()->fetchPairs('Cmp_ID', 'CmpName1'))
->setPrompt('Vybrat firmu')->setRequired();
$form->addSelect('Prj_ID', 'Projekt')
->setItems($this->timesheetManager->getProjects()->fetchPairs('Prj_ID', 'PrjName'))
->setPrompt('Vybrat projekt');
$form->addText('TskName', 'Obor zájmu')->setRequired();
$form->addText('TskDescription1', 'Popis úkolu')->setRequired();
$form->addText('TskDateStart', 'Datum zahájení')->setRequired();
$form->addText('TskDateEnd', 'Datum ukončení')->setRequired();
$form->addText('TskTaskmaster', 'Zadavatel')->setRequired();
$form->addText('TskWorked', 'Odpracováno (hod.)')->setRequired()->addRule(Form::INTEGER, 'Zadejte číselnou hodnotu');
$form->addSelect('TskUser', 'Provedl', $employees = array(
"user1" => 'user1',
"user2" => 'user2',
"user3" => 'user3',
"user4" => 'user4',
"user5" => 'user5',
"user6" => 'user6',
))->setPrompt('Vybrat zaměstnance')->setRequired();
$form->addSubmit('submit', 'Uložit');
$form->onSuccess[] = [$this, 'timesheetFormSucceeded'];
return $form;
}
Problém je nyní ten, že selectbox hned nabízí veškeré možnosti, které v databázi jsou (jak v čistém formuláři tak při jeho editaci), což však nechci. Já je vlastně potřebuji mít načtené, ale nechci, aby byli vidět. Zobrazit by se v editovaném formuláři měli právě podle IDčka projektu, které se natáhne ze záznamu, jež chci editovat že. Čili stále mi uniká korektní cesta, jak problém vyřešit. :D
EDIT: Znovu jsem zabrouzdal na jedno téma, https://pla.nette.org/…cni-formular, nyní po tvém vysvětlení mě napadá, že toto by mohlo vyřešít můj problém, ne? Pro načtení dat si vytvořit vlastní funkci a samotné loadování hodnot provést až v action nikoliv v továrničce na formulář.
Editoval iNyxLadis (29. 11. 2016 15:40)
- Jan Mikeš
- Člen | 771
Vycházel jsi z tohoto
návodu?
Zde se data loadují v handle
metodách, všimni si, je tam 2×
setItems()
podle toho jestli hodnota byla nalezena nebo ne.
- iNyxLadis
- Člen | 48
Jan Mikeš napsal(a):
Vycházel jsi z tohoto návodu?
Zde se data loadují vhandle
metodách, všimni si, je tam 2×setItems()
podle toho jestli hodnota byla nalezena nebo ne.
Ano, vycházím z tohoto. Problém tedy bude někde v kódu, kde se snažím z databáze tahat projekty na základě ID firmy. Přikládám kód:
Presenter
public function handleTimesheetFormChange($value)
{
if ($value) {
$resultPrj = $this->timesheetManager->getProjectSelect($value);
$secondItems = $resultPrj->fetchPairs('Prj_ID', 'PrjName');
$this['timesheetForm']['Prj_ID']->setPrompt('Vybrat projekt')
->setItems($secondItems);
} else {
$this['timesheetForm']['Prj_ID']->setPrompt('Nejprve zvolit firmu')
->setItems(array());
}
$this->redrawControl('wrapper');
$this->redrawControl('projectSnippet');
}
Model:
const
TABLE_TASK = 'TblTask',
TABLE_TIME = 'TblTime',
TABLE_COMPANY = 'TblCompany',
TABLE_PROJECT = 'TblProject',
TABLE_USER = 'TblUser',
TASK_DATEEND = 'TskDateEnd',
TASK_COLUMN_ID = 'Tsk_ID',
PROJECT_COMPANY_ID = 'Cmp_ID'; // tabulka TblProject, sloupec Cmp_ID
public function getProjectSelect($value)
{
return $this->database->table(self::TABLE_PROJECT)->where(self::PROJECT_COMPANY_ID, $value);
}
- iNyxLadis
- Člen | 48
Pokusil jsem se loadovat data ještě pomocí této kuchařky: https://pla.nette.org/…cni-formular
Bohužel však se stejným výsledkem a stejnou chybou, tedy: InvalidArgumentException, konkrétně Value ‚1‘ is out of allowed set [] in field ‚Prj_ID‘. Začínám se v tom trochu ztrácet :/ :D
- iNyxLadis
- Člen | 48
Ahoj, ještě jednou prosím o pomoct při řešení invalidArgumentException. Stále nemohu přijít na chybu.
Můj presenter
class TimesheetPresenter extends BaseFrontPresenter
{
/** Instance třídy modelu pro práci s úkoly. */
protected $timesheetManager;
/**
* Konstruktor s injektovaným modelem pro práci s úkoly.
*/
public function __construct(TimesheetManager $timesheetManager)
{
parent::__construct();
$this->timesheetManager = $timesheetManager;
}
/**
* Výpis úkolů z tabulky.
*/
public function renderDefault()
{
$this->template->tasks = $this->timesheetManager->getTasks();
}
public function handleTimesheetFormChange($value)
{
if ($value) {
$resultPrj = $this->timesheetManager->getProjectSelect($value);
$secondItems = $resultPrj->fetchPairs('Prj_ID', 'PrjName');
$this['timesheetForm']['Prj_ID']->setPrompt('Vybrat projekt')
->setItems($secondItems);
} else {
$this['timesheetForm']['Prj_ID']->setPrompt('Nejprve zvolit firmu')
->setItems(array());
}
$this->redrawControl('wrapper');
$this->redrawControl('projectSnippet');
}
public function actionEdit($id)
{
if ($id) {
($task = $this->timesheetManager->getTask($id)) ? $this['timesheetForm']->setDefaults($task) : $this->flashMessage('Úkol nenalezen.');}
}
public function actionRemove($id)
{
$this->timesheetManager->removeTask($id);
$this->flashMessage('Úkol byl odstraněn');
$this->redirect(':Front:Timesheet:');
}
protected function createComponentTimesheetForm()
{
$form = new Form;
$form->addHidden('Tsk_ID');
$form->addSelect('Cmp_ID', 'Firma', $this->timesheetManager->getCompanies()->fetchPairs('Cmp_ID', 'CmpName1'))
->setPrompt('Vybrat firmu')->setRequired();
$form->addSelect('Prj_ID', 'Projekt')
->setPrompt('Vybrat projekt');
$form->addText('TskName', 'Obor zájmu')->setRequired();
$form->addText('TskDescription1', 'Popis úkolu')->setRequired();
$form->addText('TskDateStart', 'Datum zahájení')->setRequired();
$form->addText('TskDateEnd', 'Datum ukončení')->setRequired();
$form->addText('TskTaskmaster', 'Zadavatel')->setRequired();
$form->addText('TskWorked', 'Odpracováno (hod.)')->setRequired()->addRule(Form::INTEGER, 'Zadejte číselnou hodnotu');
$form->addSelect('TskUser', 'Provedl', $employees = array(
"user1" => 'user1',
"user2" => 'user2',
"user3" => 'user3',
"user4" => 'user4',
"user5" => 'user5',
"user6" => 'user6',
))->setPrompt('Vybrat zaměstnance')->setRequired();
$form->addSubmit('submit', 'Uložit');
$form->onSuccess[] = [$this, 'timesheetFormSucceeded'];
return $form;
}
public function timesheetFormSucceeded($form)
{
$values = $form->getHttpData();
unset($values['_submit']);
unset($values['_do']);
$this->timesheetManager->saveTask($values);
$this->flashMessage('Záznam byl uložen.');
$this->redirect(':Front:Timesheet:');
}
}
Děkuji
Editoval iNyxLadis (7. 12. 2016 10:37)
- iNyxLadis
- Člen | 48
Jan Mikeš napsal(a):
Je důležité porozumět příčině problému – nette vyhazuje výjimku, v případě, že nastane pokus o nastavení výchozí hodnoty u choice controlu (selectbox, radio) a hodnota, kterou se pokusíš nastavit jako výchozí není v seznamu povolených hodnot (
setItems(...)
), pak nette vyhodnotí situaci jako pokus o útok → podstrčení nepovolené hodnoty.Musíš si být jist, že ve chvíli, kdy se snažíš nastavit hodnotu prvku, tak prvek má už načtené hodnoty v závislosti na hodnotě předchozího inputu.
Ukaž prosím v jaké fázi životního cyklu presenteru nastavuješ
->setItems()
prvku, kterému chceš nastavit výchozí hodnotu.
Nejde mi do hlavy jedna věc – výchozí hodnoty se při editu formuláře snažím nastavit v actionEdit pomocí setDefaults. Jak to, že veškeré jiné prvky formuláře mi touto cestou hodnotu nastaví, ale závislý selectbox v formuláři, řešený ajaxem, hodnotu z databáze nepřijme.
Editoval iNyxLadis (8. 12. 2016 15:16)
- iNyxLadis
- Člen | 48
Mortisson napsal(a):
Action
se provádí dříve nežHandle
, takže problém by mohl být že vactionEdit
nastavuješ výchozí hodnoty, které dodává ažHandle
Jakým způsobem jsem tedy schopen tento problém vyřešit? Pro tento závislý selectbox potřebuji umístit setItems také do actionEdit. Jsem začátečník a nenapadá mě jak to do toho uplně zakomponovat. Je nějaký konkrétní elegantní postup pro tento problém? Díky
EDIT: Pokud si přeformuluju action* nějako takhle a přidám setItems, dostanu se přes error. Výchozí hodnota selectboxu se vybere správná, problém je však ten, že kromě správné hodnoty vidím v selectboxu i všechny ostatní varianty, které bych vidět neměl. Jakým způsobem se dá obejít to, abych měl kvůli setDefaults definované veškeré možnosti, ale nastavil pouze konkrétní hodnotu a ostatní skryl? Jakmile změním v editu první selectbox, handle se již o zbytek postará a i dependent select box vidím správně, tedy jen ty hodnoty, které vidět mají být. Jedná se mi tam pouze o ten prvotní stav po zavilání actionEditu. Díky za pomoc
public function actionEdit($taskId)
{
$task = $this->timesheetManager->getTask($taskId);
if (!$task) {
$this->error('Úkol nenalezen');
}
$this['timesheetForm']['Prj_ID']->setItems($this->timesheetManager->getProjects()->fetchPairs('Prj_ID', 'PrjName'));
$this['timesheetForm']->setDefaults($task);
}
Editoval iNyxLadis (12. 12. 2016 14:57)
- iNyxLadis
- Člen | 48
Vyřešeno následujícím způsobem:
public function actionEdit($taskId, $cmpId)
{
$task = $this->timesheetManager->getTask($taskId);
if (!$task) {
$this->error('Úkol nenalezen');
} else
{
if (!$this->isAjax()) { // pro nastaveni pocatecni hodnoty selectboxu, ve chvíli obsluhy selectboxu se o zbytek stará handle
$selectedProjects = $this->timesheetManager->getProjectsForSelectbox($cmpId);
$this['timesheetForm']['Prj_ID']->setItems($selectedProjects);
$this['timesheetForm']->setDefaults($task);
}
}
}