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)