Nakonfigurované formulářové prvky – Nějaký jiný nápad?

- GEpic
- Člen | 566
Ahoj, abych nemusel v systému neustále znovu definovat formulářové prvky neustále dokola, vytvořil jsem si tuto třídu / službu / model / továrnu / fakt nevím jak to nazvat:
<?php
namespace App\Model;
use Nette\Application\UI\Form;
class FormElements
{
public static function getUsername(Form $form)
{
$form->addText('username', 'Uživatelské jméno:')
->setRequired("Musíte vyplnit uživatelské jméno")
->addRule(Form::MIN_LENGTH, 'Uživatelské jméno musí mít alespoň %d znaky', 4)
->addRule(Form::MAX_LENGTH, 'Nelze', 30);
return $form;
}
public static function getPassword(Form $form)
{
$form->addPassword('password', 'Heslo:')
->setRequired("Musíte vyplnit heslo")
->addRule(Form::MIN_LENGTH, 'Heslo musí mít alespoň %d znaků', 6)
->addRule(Form::MAX_LENGTH, 'Nelze', 30);
return $form;
}
}
Tu si následovně injectuji v presenteru:
/**
* @var FormElements
* @inject
*/
public $elements;
A používám jí takto:
$form = new Form;
$this->elements->getUsername($form);
$this->elements->getPassword($form);
...
Napadá vás nějaký jiný způsob, jak by se to dalo udělat například
přímo přes $form->getPreconfiguredElement("username");
Jde mi o to, že používám různé typy formulářů, pokaždé s jinými prvkami, ale definovat je neustále dokola je pruda.
Díky za tipy a rady.
PS: Toto je jinak prozatím funkční řešení, ale nelíbí se mi neustále
předávat a vracet zpět $form
Editoval GEpic (7. 1. 2016 19:24)

- Oli
- Člen | 1215
Pravděpodobně to co hledáš je Container. Může to vypadat nějak takhle
abstract class BaseContainer extends \Nette\Forms\Container
{
public function __construct()
{
parent::__construct();
$this->monitor('Nette\Forms\Form');
}
protected function attached($obj)
{
parent::attached($obj);
if ($obj instanceof \Nette\Forms\Form)
{
$this->currentGroup = $this->form->currentGroup;
$this->configure();
}
}
abstract protected function configure();
}
class ContactPersonContainer extends BaseContainer
{
private $user;
private $userSettings;
public function __construct(User $user, UserSettings $userSettings)
{
$this->user = $user;
$this->userSettings = $userSettings;
parent::__construct();
}
protected function configure()
{
$user = $this->addText('first', 'firstName', null, 60)
->setRequired('firstNameRequire');
$secondName = $this->addText('second', 'secondName', null, 60)
->setRequired('secondNameRequire');
$email = $this->addText('email', 'email', null, 100)
->setRequired('emailRequire')
->addRule(Form::EMAIL, 'emailFormat');
if($this->user->isLoggedIn())
{
$user->setDisabled()->setValue($this->user->identity->firstName);
$secondName->setDisabled()->setValue($this->user->identity->secondName);
$email->setDisabled()->setValue($this->user->identity->email);
} else
{
$pass = $this->addPassword('password', 'password', null, 200)
->setRequired('passwordRequire')
->addRule(Form::MIN_LENGTH, 'passwordLength', $this->userSettings->passwordLength);
$this->addPassword('passwordVerify', 'passwordVerify', null, 200)
->setRequired()
->addRule(Form::EQUAL, 'passwordVerifyEqual', $pass);
}
}
}
Použití ve formuláři
$form['user'] = new ContactPersonContainer($this->user, $this->userSettings);
nebo si to zaregistrovat jako službu a předat si to DI.

- GEpic
- Člen | 566
Oli napsal(a):
Pravděpodobně to co hledáš je Container. Může to vypadat nějak takhle
abstract class BaseContainer extends \Nette\Forms\Container { public function __construct() { parent::__construct(); $this->monitor('Nette\Forms\Form'); } protected function attached($obj) { parent::attached($obj); if ($obj instanceof \Nette\Forms\Form) { $this->currentGroup = $this->form->currentGroup; $this->configure(); } } abstract protected function configure(); }class ContactPersonContainer extends BaseContainer { private $user; private $userSettings; public function __construct(User $user, UserSettings $userSettings) { $this->user = $user; $this->userSettings = $userSettings; parent::__construct(); } protected function configure() { $user = $this->addText('first', 'firstName', null, 60) ->setRequired('firstNameRequire'); $secondName = $this->addText('second', 'secondName', null, 60) ->setRequired('secondNameRequire'); $email = $this->addText('email', 'email', null, 100) ->setRequired('emailRequire') ->addRule(Form::EMAIL, 'emailFormat'); if($this->user->isLoggedIn()) { $user->setDisabled()->setValue($this->user->identity->firstName); $secondName->setDisabled()->setValue($this->user->identity->secondName); $email->setDisabled()->setValue($this->user->identity->email); } else { $pass = $this->addPassword('password', 'password', null, 200) ->setRequired('passwordRequire') ->addRule(Form::MIN_LENGTH, 'passwordLength', $this->userSettings->passwordLength); $this->addPassword('passwordVerify', 'passwordVerify', null, 200) ->setRequired() ->addRule(Form::EQUAL, 'passwordVerifyEqual', $pass); } } }Použití ve formuláři
$form['user'] = new ContactPersonContainer($this->user, $this->userSettings);nebo si to zaregistrovat jako službu a předat si to DI.
Díky moc, než jsem přečetl forum, došlo mi vlastně co můžu udělat a napadlo mě toto:
<?php
namespace App\Model;
use Nette\Application\UI\Form;
/**
* Class FormElements
* Contains preconfigured form elements with rules
* @package App\Model
*/
class FormElements extends Form
{
public function __construct()
{
parent::__construct();
}
/**
* Returns form with preconfigured Username form element
*/
public function getUsername()
{
$this->addText('username', 'Uživatelské jméno:')
->setRequired("Musíte vyplnit uživatelské jméno")
->addRule(Form::MIN_LENGTH, 'Uživatelské jméno musí mít alespoň %d znaky', 4)
->addRule(Form::MAX_LENGTH, 'Nelze', 30);
}
/**
* Returns form with preconfigured Password form element
*/
public function getPassword()
{
$this->addPassword('password', 'Heslo:')
->setRequired("Musíte vyplnit heslo")
->addRule(Form::MIN_LENGTH, 'Heslo musí mít alespoň %d znaků', 6)
->addRule(Form::MAX_LENGTH, 'Nelze', 30);
}
}
A použití:
$form = new FormElements();
$form->getUsername();
$form->getPassword();
PS: Samozřejmě pojmenování funkcí bude z get na add… co říkáš na toto řešení? :)
PS Každopádně když koukám na tvůj příklad, líbí se mi využívání user identity, ještě jsem to nezkoušel, takže se nejspíš ohledně identity ještě ozvu. :D
Editoval GEpic (7. 1. 2016 20:06)

- duke
- Člen | 650
Nemyslím si, že řešení pomocí dědičnosti je lepší než tvé
původní řešení. Jen bych to lépe pojmenoval.
Místo FormElements bych doporučil použít např. FormElementComposer (lépe
popisuje smysl třídy). A místo metod getUsername() a getPassword() bych
zvolil addUsername() a addPassword() (tyto metody totiž
přidávají inputy (a další věci) do formuláře, get*
metody obvykle pouze vrací již existující a nic nepřidávají).
Editoval duke (7. 1. 2016 20:18)

- GEpic
- Člen | 566
duke napsal(a):
Nemyslím si, že řešení pomocí dědičnosti je lepší než tvé původní řešení. Jen bych to lépe pojmenoval.
Místo FormElements bych doporučil použít např. FormElementComposer (lépe popisuje smysl třídy). A místo metod getUsername() a getPassword() bych zvolil addUsername() a addPassword() (tyto metody totiž přidávají inputy (a další věci) do formuláře, get* metody obvykle pouze vrací již existující a nic nepřidávají).
Jj zapracoval jsem na tom a ohledně pojmenovávání jsem psal již dříve,
postoval jsem tady uprostřed práce, nyní vypadá takto
(addPassword nelze použít, již je definovaný ve
Nette\Application\UI\Form)
Název FormElementComposer se mi líbí, to mě pravda nenapadlo.
<?php
namespace App\Model;
use Nette\Application\UI\Form;
/**
* Class FormElements
* Contains preconfigured form elements with rules
* @package App\Model
*/
class FormElements extends Form
{
public function __construct()
{
parent::__construct();
}
/**
* Adds preconfigured Username element
*/
public function addUsername()
{
$this->addText('username', 'Uživatelské jméno:')
->setRequired("Musíte vyplnit uživatelské jméno")
->addRule(Form::MIN_LENGTH, 'Uživatelské jméno musí mít alespoň %d znaky', 4)
->addRule(Form::MAX_LENGTH, 'Nelze', 30);
}
/**
* Adds preconfigured Password element
* $param string $label
*/
public function addPasswordWithRules($label = NULL)
{
$this->addPassword("password", ($label ?: "Heslo:"))
->setRequired("Musíte vyplnit heslo")
->addRule(Form::MIN_LENGTH, 'Heslo musí mít alespoň %d znaků', 6)
->addRule(Form::MAX_LENGTH, 'Nelze', 30);
}
...
}
Editoval GEpic (7. 1. 2016 20:34)

- Oli
- Člen | 1215
@GEpic a jak to uděláš, když budeš chtít prvky z takových 3 formulářů? :-)
Na dekomponování na menší jednotky slouží právě container. Obecně dědění není nejlepší cesta a pokud to jde (přirozeně), tak je lepší se mu vyhnout a použít dekompozici.
Další možností by mohlo být udělat si nějaký doplněk, který by
dělal právě něco jako $form->addMyPassword(),
$form->addUserName(), … Záleží jak moc jemně to chceš
rozdrobit, jestli na nějaký bloky (jmeno, prijmeni, heslo) nebo na jednotlivé
prvky (jmeno zvlast, prijmeni zvlast, …)

- GEpic
- Člen | 566
V celé aplikaci využívám několik prvků, většinou CheckBoxListy nebo SelectBoxy a ty plním z polí. Každopádně u toho se mi myšlenka líbila, využívám to následovně jak pro registraci, přihlášení, změnu jednotlivých údajů a podobně a nemusím se pokaždé start o pravidla a parametry.
Např:
$form = new FormElementComposer;
$form->addUsername();
$form->addPasswordWithRules();
$form->addPasswordConfirm();
$form->addFirstname();
$form->addSurname();
$form->addPhone();
$form->addEmail();
nebo:
$form = new FormElementComposer;
$form->addPasswordWithRules("Heslo pro autorizaci:");
$form->addPhone();
$form->addHidden('id', $this->user->getId());
$form->addSubmit('changePhone', 'Změnit číslo');
Apod.

- duke
- Člen | 650
Dědičnost bych nevolil už proto, že můžeš chtít např. do formuláře vkládat Containery a i do těch Containerů pak komponované prvky pomocí tohoto tvého nástroje. Tj. můžeš pak mít něco jako:
$form = new Form;
$composer = new FormElementComposer; // nebo si ho předat jako službu
$composer->addPhone($form);
$composer->addPhone($form, 'mobilePhone');
$loginContainer = $form->addContainer('login');
$composer->addUsername($loginContainer);
$composer->addPasswordWithRules($loginContainer); // nebo klidně použít pojmenování addPassword
$form->addSubmit('send', 'Odeslat');
Editoval duke (8. 1. 2016 0:29)