Formulář jako továrnička – co říkáte na mé řešení?

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

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
+
0
-

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 ;-)

radekBrno
Člen | 61
+
0
-

David Kudera napsal(a):

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 ;-)

Který způsob je lepší?

David Matějka
Moderator | 6445
+
+1
-

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:

  1. v onSuccess je zbytecne if (!$form->isValid()) return;, onSuccess se nezavola, pokud neni formular validni
  2. 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
+
0
-
  1. 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ář.
  2. 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.
  3. 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.
  4. 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)

CZechBoY
Člen | 3608
+
0
-

U edit formu můžeš zavolat v presenteru

$form->setDefaults($db->getDiscuss());