Form Extension – bude líp?

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

Ahoj,

vytvářím svou první form extension a samozřejmě se inspiruji, procházím řešení.
Mám před sebou tuto funkční registraci:

public function afterCompile(Code\ClassType $class)
{
    parent::afterCompile($class);

    $init = $class->methods['initialize'];
    $config = $this->getConfig($this->defaults);
    $init->addBody('\App\Components\Imager\ImageUploadControl::register(?, ?);', ['addImagerUpload', $config]);
}

… ale mrzí mě, že metodu addImagerUpload nevidím v IDE jako nalezitelnou, rozumím proč.
Horší však je, pokud mám nějaké metody na setování vlastností form prvku, ve výsledku mám pak vedle kód extension a studuji ho řádek po řádku. V lepším případě dokumentaci.

Jedno z řešeních je mít nějaký base form, kde mám anotace a dědím ho, ale to se mi také příliš nelíbí.

Zajímá mě, jestli se s tím plánuje v budoucnu něco dělat, aby to nebyla taková magie, či se to dá zapsat lépe, aniž bych použil samotnou class a bušil parametry constructoru, uprostřed formuláře. Především z pohledu nezasvěceného člověka, který neproleze fórum křížem krážem je to taková skrytá věc.

Druhý problém na který jsem narazil je, jak docílit automatické vyrenderování extension, které má v control více inputů (checkbox, hidden, …). Je to porod. Přitom kdo chce každý formulář kvůli drobnostem ručně renderovat? :[

ping @DavidGrudl

díky a omluvte prosím případnou okázalost :)

Editoval Landsman (16. 9. 2016 21:50)

CZechBoY
Člen | 3608
+
0
-

@FilipProcházka tu kdysi ukazoval nějakou traitu, která ti přidá „čistě“ metody na přidání dalšího prvku do containeru+formu. Nutnost teda byla mít vlastní BaseForm.

Martk
Člen | 661
+
+4
-

Vytvoříš si 2 traity, jednu s přepsáním funkce addContainer a druhou s vlastními komponenty:

trait TReplaceFormContainer {

	public function addContainer() {
		$control = new MyContainer();
		$control->currentGroup = $this->currentGroup;
		if ($this->currentGroup !== NULL) {
			$this->currentGroup->add($control);
		}

		return $this[$name] = $control;
	}

}

trait TFormControls {

	public function addImagerUploader(...) {
		// ...
	}

}

Vytvoříš novou třídu form a container:

class MyContainer extends Nette\Forms\Container {

	use TReplaceFormContainer;
	use TFormControls;

}

class MyForm extends Nette\Application\UI\Form {

	use TReplaceFormContainer;
	use TFormControls;

}

Nic lepšího mě zatím nenapadlo.

Editoval Antik (16. 9. 2016 8:44)

CZechBoY
Člen | 3608
+
+1
-

@Antik něco takovýho měl @FilipProcházka, ale spojil obě traity do jedné. Což by u tebe šlo taky, protože stejně máš use obou trait jak v containeru tak ve formu.

Martk
Člen | 661
+
0
-

@CZechBoY To by šlo :) a taky mě to napadlo, ale nepřidal jsem to, protože každá traita dělá jinou věc. Jedná přidává vlastní komponenty, druhá jen nahrazuje původní kontejner, kvůli tomu, aby šlo v kontejneru použít vlastní komponenty.

Landsman
Člen | 152
+
0
-

@CZechBoY @Antik Jop, to je pěkné, ale stále je to jakýsi hack a není to příliš přívětivé pokud chci používat další 3rd-party rozšíření.

To Andrejovské „Bude líp?“ bylo myšleno, zda to půjde v budoucnu nějakou oficiální cestou a bude se s rozšířením podobného typu počítat & zda je to technicky vůbec reálné udělat jinak.

CZechBoY
Člen | 3608
+
+1
-

Takový psycho řešení co mě teď napadlo by bylo vygenerovat traitu pro container přes DI\Compiler.
Ve form containeru by bylo

class Container ...
{
	use CompiledTrait;
...
}

a pak compiler by vygeneroval traitu a ostatní extensions by mohli do těla traity doplňovat funkce

$compiler->addFormContantainerTraitMethod('addImagerUpload', 'public function addImagerUpload(string $name, string $label) {
	return new App\Forms\FormControls\ImagerUploader($name, $label);
}');

Případně přes callback místo těla funkce.

Editoval CZechBoY (16. 9. 2016 18:30)

Martk
Člen | 661
+
0
-

@Landsman Ještě by šlo udělat toto, abys nemusel kontrolovat změny:

trait TFormControls {

	/** @var callable[] */
	private static $_cacheMethods = [];

    public function addImagerUploader(...$args) {
        $this->tryControl(__METHOD__, $args);
    }


	private function tryControl($name, array $args) {
		if (!isset(self::$_cacheMethods[$name])) {
			if (!($callback = ObjectMixin::getExtensionMethod(get_class($this), $name))) {
				throw new \Exception();
			}
			self::$_cacheMethods[$name] = $callback;
		}

		Callback::invoke(self::$_cacheMethods[$name], $this, ...$args);
	}
}

Editoval Antik (16. 9. 2016 20:08)