[help needed] Jak přidat generika do formulářů?

David Grudl
Nette Core | 8218
+
+2
-

Myslím, že by mohlo být fajn pomocí generik definovat správný typ pro parametr $value v metodě setValue() / setDefaultValue() a návratovou hodnotu getValue(). Mohlo by to fungovat třeba nějak takto (viz anotace @return):

class Container
{
	/**
	 * @return Controls\TextInput<string>
	 */
	public function addText(string $name, $label = null): Controls\TextInput
	{
		return $this[$name] = (new Controls\TextInput($label, $maxLength))
			->setHtmlAttribute('size', $cols);
	}


	/**
	 * @return Controls\TextInput<int>
	 */
	public function addInteger(string $name, $label = null): Controls\TextInput
	{
		return $this[$name] = (new Controls\TextInput($label))
			->setNullable()
			->addRule(Form::Integer);
	}


	/**
	 * @return Controls\TextInput<float>
	 */
	public function addFloat(string $name, $label = null): Controls\TextInput
	{
		return $this[$name] = (new Controls\TextInput($label))
			->setNullable()
			->setHtmlType('number')
			->setHtmlAttribute('step', 'any')
			->addRule(Form::Float);
	}

Zároveň u tříd jako Checkbox by definice generika mohla být přímo součástí třídy, aby nebylo nutné uvádět v tomto případě anotaci @return Checkbox<bool>

	public function addCheckbox(string $name, $caption = null): Controls\Checkbox
	{
		return $this[$name] = new Controls\Checkbox($caption);
	}

Zhostíte se toho někdo a pošlete PR?

Marek Bartoš
Nette Blogger | 1260
+
+1
-

@DavidGrudl Zkouším to teď implementovat. V ideálním případě by generika měla být kompletně součástí třídy a vně by je uživatel nemusel definovat vůbec.

Mohl bys mi sepsat seznam možných typů hodnot pro všechny prvky?
Pokud se liší možné hodnoty v set a get kvůli transformacím (např. HiddenField), tak i včetně toho.

Typ by se též měl změnit podle toho, zda je prvek nullable, disabled (u toho afaik záleží, zda se první volá setValue() nebo setDisabled()?) nebo je volání v podmínce $control->isFilled(). Teoreticky by mělo být možné typy pomocí volání metod rozširovat/zužovat, ale nevím, zda znám všechny případy, kdy se typ mění.

Velký problém s generiky ale je, že se obvykle nepřistupuje k instancím prvků přímo, ale přes formulář a ten vrací jen obecné IComponent. Asi by šlo doplnit generika i do formuláře, aby typy prvků byly známé, ale takový generický typ bude obří a dobře by se s ním pracovalo pouze v rámci jedné funkce.

Co by pomohlo s typovými formuláři nejvíce by byly deklarativní, striktně typované formuláře, ale nevím, zda bych to dovedl snadno vytvořit s aktuálním interface. Posledně když jsem zkoušel udělat nadstavbu nette/security, tak z toho vzniklo orisai/auth.

// Jen rychlý, nepromyšlený example
class RegistrationForm {
    #[FormField(label: "Username", validationRule: "required")]
    private TextControl $username;

    #[FormField(label: "Email", validationRule: "required|email")]
    private EmailControl $email;

    #[FormField(label: "Birthdate", validationRule: "date")]
    private DateTimeControl $birthdate;
}