Formulář jako továrnička – co říkáte na mé řešení?
- radekBrno
- Člen | 61
Zdravím,
snažil jsem se vytvořit formulář pro přidání a editaci kategorie diskuze. Chvíli jsem s tím bojoval, ale nakonec se mi povedlo vytvořit funkční řešení. Možná to někomu pomůže a taky by mě zajímal Váš názor, jestli bych nemohl něco udělat jednodušeji/lépe?
Největší problém jsem měl s tím, jak do továrničky dostat data
editované kategorie. Původně jsem se snažil vytvořit metodu
public function actionEditCategory($id) {}
přímo v továrničce,
ale pak mi došlo, že to není správná cesta a pro předání dat jsem
použil parametr.
Presenter pro kategorii diskuze
namespace AdminModule;
use Nette,
Nette\Application\UI;
/**
* Discussion presenter.
*/
class DiscussionPresenter extends BasePresenter {
private $database;
private $data = array();
/** @var IDiscussionCategoryForm @inject */
public $discussionCategoryForm;
public function __construct(Nette\Database\Context $database)
{
$this->database = $database;
}
public function renderDefault() {
$this->template->categories = $this->database->table('discussion_category');
}
public function actionEditCategory($id)
{
$post = $this->database->table('post')->get($id);
if (!$post) {
$this->error('Příspěvek nebyl nalezen.');
} else {
$this->data = $post->toArray();
}
}
protected function createComponentDiscussionCategoryForm()
{
$control = $this->discussionCategoryForm->create($this->data);
$control['form']->onSuccess[] = function (UI\Form $form) {
if (!$form->isValid()) return;
$this->flashMessage('Akce proběhla úspěšně.', 'success');
$this->redirect('this');
};
return $control;
}
}
Rozhraní pro továrničku
namespace AdminModule;
interface IDiscussionCategoryForm
{
/** @return DiscussionCategoryForm */
function create(array $data);
}
Šablona
{form form}
<ul n:if="$form->hasErrors()">
<li n:foreach="$form->errors as $error">{$error}</li>
</ul>
<table>
<tr><td>{label name /}</td></tr>
<tr><td>{input name}</td></tr>
<tr><td>{input send}</td></tr>
</table>
{/form}
Samotná továrnička
namespace AdminModule;
use Nette,
Nette\Application\UI,
Nette\Application\UI\Control;
class DiscussionCategoryForm extends Control {
private $data = array();
private $database;
public function __construct(array $data, Nette\Database\Context $database)
{
parent::__construct();
$this->data = $data;
$this->database = $database;
}
public function render() {
$this->template->render(__DIR__ . '/discussionCategoryForm.latte');
}
protected function createComponentForm()
{
$form = new UI\Form;
$form->addHidden('id')
->setValue( ((isset($this->data["id"])) ? $this->data["id"] : 0) );
$form->addText('name', 'Jméno:');
$form->addSubmit('send', 'Odeslat');
$form->setDefaults($this->data);
$form->onSuccess[] = $this->processForm;
return $form;
}
public function processForm($form)
{
$values = $form->getValues();
if ($values->id == 0) {
$this->database->table('post')->insert(array(
'name' => $values->name
));
} else {
$this->database->table('post')
->where('id', $values->id)
->update(array('name' => $values->name));
}
}
}
A úryvek z config.neon
services:
- implement: AdminModule\IDiscussionCategoryForm
parameters: [array data]
arguments: [%data%]
Editoval radekBrno (3. 8. 2014 11:54)
- David Kudera
- Člen | 455
Vypadá to v pohodě, jen v presenteru jednou používáš injektování služeb přes konstruktor a jednou přes @inject anotaci, tak možná by bylo dobrý to nějak sjednotit ;-)
- David Matějka
- Moderator | 6445
Který způsob je lepší?
tenhle :P krom toho pouzivam v presenterech prevazne injectovani do properties
k tomu tvemu reseni: je to ok, v podstate pouzivam to same (jen s doctrinou a entitama je to predavani dat hezci :)), jen par poznamek:
- v onSuccess je zbytecne
if (!$form->isValid()) return;
, onSuccess se nezavola, pokud neni formular validni - osobne se v presenteru nenapojuji na onSuccess event formulare, ale mam v kazde komponente vytvoren vlastni event – neco jako onSave, ktery zavolam po ulozeni
- Šaman
- Člen | 2666
- Nepředával bych komponentě celé pole, ale jen id editovaného záznamu. Pokud budeš chtít cokoliv změnit, budeš to řešit jen v komponentě. A tím pádem i společně pro vytvářecí i editační formulář.
- V metodě navázané na událost onSuccess už nemusíš kontrolovat, zda je formulář validní. Nevalidní se k této události vůbec nedostane.
- V presenteru doporučuji používat ten čtvrtý způsob – inject metody. Anotace @inject vyžaduje veřejnou property, takže není úplně košer. Kdyby\Autowired není nativní Nette způsob, takže ho používej jen pokud víš proč. Konstruktor je u final presenterů v pohodě, ale každou novou závislostí se ti rozroste. A inject metoda se dá vložit jako už připravená traita.
- Tady je ukázka, jak to dělám já. Do presenteru pak stačí přidat jeden řádek a napsat továrničku.
Aha, já ti celou dobu popisuji, jak udělat továrničku na formulář,
který potřebuje předat parametr (u mě je potřeba znát projekt, pod který
ten vytvářený task patří). Na editaci používám výrazně jednodušší
způsob – továrničku na formulář nezajímá, jestli vytváří
editační, nebo vytváření form. Jediná změna je, že ten editační bude
mít přednastavené hodnoty. A to řeším až v presenteru
Při zpracování formu pak bude jeden jednoduchý
if, který určí, zda se jedná o vytvářecí, nebo editační
formulář.
Druhá možnost, kde naplnit formulář, je mít dvě továrničky
v presenteru a jedna vytvoří newFooForm
a přiřadí tomu nějou
flashmessage, druhá továrnička pomocí stejné tovární třidy vytvoří
editFooForm
, nastaví mu defaultní hodnoty a naváže na něj
jinou flashmessage.
Editoval Šaman (3. 8. 2014 17:51)