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

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

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

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

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

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

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

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

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.

CZechBoY
Člen | 3608
+
0
-

Akorat u zmeny hesla asi budes kontrolovat jestli je pole vyplneny a az potom to validovat. U prihlaseni je zase heslo povinne.
Takovych poli najdes spousty.

duke
Člen | 650
+
0
-

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)