Enhancement: Co takhle create<componen­tName>($name)?

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

Poznámka: je součástí frameworku od verze 0.9

Chtěl rozvést diskuzi o tom, zda by se továrnička na koponenty neměla posunout ještě o kousek dál.

Jde o to, že jsem kolikrát měl v metodě createComponent($name) switch, který mi jen rozhodil vytvoření komponent mezi funkce uzpůsobené pro vytvoření právě dané komponenty. Vedlo mne k tomu to, že jsem předtím měl v createComponent() obrovskej štrůdl kódu a mi se to nelíbilo :). Vypadalo to asi takto:

public function createComponent($name) {
	switch ($name) {
		case 'name1': $this->createName1($name); break;
		case 'name2': $this->createName2($name); break;
		/* ... a několik dalších komponent */
		default: parent::createComponent($name); break;
}

protected function createName1($name) { /* vytvoří komponentu name1 */ }
protected function createName1($name) { /* vytvoří komponentu name2 */ }

To mne velice rychle omrzelo a namísto toho jsem v BasePresenteru uzpůsobil createComponent tak, že automaticky se snaží vyvolat create<$name>($name). Od té doby jsem navýsost spokojen a mám od továrničky pokoj (i když mne ještě trochu obtěžuje ten parametr $name, ale to se ještě dá tolerovat ;).

Nedávno mne začalo vrtat hlavou, zda bych se s tímto neměl podělit s ostatními a jejich názory na takový přístup. Není to nic objevného, ale přeci jen se mi to zdá víc Netťácké, no ne? Když už se nám Nette stará automaticky o mapování akcí, pohledů, tak proč ne rovnou i vytváření komponent? Zase bychom měli o starost méně :). Co myslíte?

jasir
Člen | 746
+
0
-

Mě to přijde jako velice pěkný nápad, můžeš sem dát pro inspiraci svojí implementaci createComponent z BasePresenteru? Díky

Jan Tvrdík
Nette guru | 2595
+
0
-

jasir napsal(a):

Mě to přijde jako velice pěkný nápad, můžeš sem dát pro inspiraci svojí implementaci createComponent z BasePresenteru? Díky

Předpokládám, že to vypadá nějak takto:

protected function createComponent($name)
{
	$callback = array($this, 'create' . ucfirst($name));

	if (is_callable($callback)) {
		call_user_func($callback, $name)
	} else {
		// Něco jiného
	}
}
Jod
Člen | 701
+
0
-

Asi tak nejak by to malo fungovať aj dla mňa. Ja som nato myslel akurát včera, kedže tam robím rovnakú štrúdlu :))) . Ale som si povedal, že ma to tak neobťažuje a aj tak používam napr. na editačný formulár a ten na pridávanie rovnakú metódu s parametrom true/false.

washo
Člen | 88
+
0
-

Jod napsal(a):

Asi tak nejak by to malo fungovať aj dla mňa. Ja som nato myslel akurát včera, kedže tam robím rovnakú štrúdlu :))) . Ale som si povedal, že ma to tak neobťažuje a aj tak používam napr. na editačný formulár a ten na pridávanie rovnakú metódu s parametrom true/false.

Me to prijde nejake divne… Co to teda umoznuje? Proc pouzivat metodu createComponent($name)? a nevolat rovnou createName1() ?

Honza Marek
Člen | 1664
+
0
-

(i když mne ještě trochu obtěžuje ten parametr $name, ale to se ještě dá tolerovat ;).

Dostal jsem takový nápad. Možná by mohly metody create{ComponentName} komponentu vracet a ta by se přidala v metodě createCompoment pomocí $this->addComponent($component, $name).

Honza Marek
Člen | 1664
+
0
-

Takhle to bude asi vůbec nejlepší:

<?php
class BasePresenter extends Presenter
{
	/**
	 * Vytvořit komponentu - zavolá metodu $this->create{Name}
	 * @param string $name jméno komponenty
	 */
	protected function createComponent($name)
	{
		if (String::lower($name) === "component") {
			throw new InvalidArgumentException("Argument name '$name' is not allowed.");
		}

		$callback = array($this, "create" . $name);

		if (is_callable($callback)) {
			$component = call_user_func($callback, $name);

			if ($component instanceof IComponent && $component->getParent() === NULL) {
				$this->addComponent($component, $name);
			}

		} else {
			parent::createComponent($name);
		}
	}

}
?>

Komponenta se může připojit buď:

  1. v metodě create{ComponentName}. Dostane k dispozici parametrem název komponenty.
  2. může být vrácena a pokud nemá rodiče, tak se automaticky připojí k presenteru v metodě createComponent.

Editoval Honza M. (3. 4. 2009 14:38)

jasir
Člen | 746
+
0
-

washo napsal(a):

Jod napsal(a):

Asi tak nejak by to malo fungovať aj dla mňa. Ja som nato myslel akurát včera, kedže tam robím rovnakú štrúdlu :))) . Ale som si povedal, že ma to tak neobťažuje a aj tak používam napr. na editačný formulár a ten na pridávanie rovnakú metódu s parametrom true/false.

Me to prijde nejake divne… Co to teda umoznuje? Proc pouzivat metodu createComponent($name)? a nevolat rovnou createName1() ?

Stále to funguje na podobném principu jako továrnička, čili komponenty si bereš lazy pomocí $this->getComponent('jmenokomponenty') (bez předchozího explicitního volání createName1())

Editoval jasir (3. 4. 2009 14:08)

jasir
Člen | 746
+
0
-

Honza M. napsal(a):

Komponenta se může připojit buď:

  1. v metodě create{ComponentName}. Dostane k dispozici parametrem název komponenty.
  2. může být vrácena a pokud nemá rodiče, tak se automaticky připojí k presenteru v metodě createComponent

Jojo, takhle mi to přijde velice pěkné. Dovolil jsem si tam přidat ucfirst() a zase odstranit :P

Editoval jasir (3. 4. 2009 14:42)

LM
Člen | 206
+
0
-

Honza M. napsal(a):

Takhle to bude asi vůbec nejlepší:

<?php
class BasePresenter extends Presenter
{
	/**
	 * Vytvořit komponentu - zavolá metodu $this->create{Name}
	 * @param string $name jméno komponenty
	 */
	protected function createComponent($name)
	{
		$callback = array($this, "create" . $name);

		if (is_callable($callback)) {
			$component = call_user_func($callback, $name);

			if ($component instanceof IComponent && $component->getParent() === NULL) {
				$this->addComponent($component, $name);
			}

		} else {
			parent::createComponent($name);
		}
	}

}
?>

Komponenta se může připojit buď:

  1. v metodě create{ComponentName}. Dostane k dispozici parametrem název komponenty.
  2. může být vrácena a pokud nemá rodiče, tak se automaticky připojí k presenteru v metodě createComponent

Co když si zavolám $component->getComponent('component')?

jasir
Člen | 746
+
0
-

LM napsal(a):
Co když si zavolám $component->getComponent('component')?

No tak to funguje podle očekávání, stejně jako továrnička.

Editoval jasir (3. 4. 2009 14:46)

Honza Marek
Člen | 1664
+
0
-

jasir napsal(a):
Jojo, takhle mi to přijde velice pěkné. Dovolil jsem si tam přidat ucfirst()

To není potřeba, protože php není case sensitive.

jasir
Člen | 746
+
0
-

Honza M. napsal(a):

jasir napsal(a):
Jojo, takhle mi to přijde velice pěkné. Dovolil jsem si tam přidat ucfirst()

To není potřeba, protože php není case sensitive.

Máš pravdu, nějak jsem žil v domění, že názvy metod jsou case sensitive, stejně jako jména proměnných (která jsou case sensitive). To PHP je ale schizofrení :)

Editoval jasir (3. 4. 2009 14:26)

Honza Marek
Člen | 1664
+
0
-

stejně jako jména proměnných (která jsou case sensitive)

Hm… to jsem zase nevěděl já :-D

LM
Člen | 206
+
0
-

jasir napsal(a):

No tak to funguje podle očekávání, stejně jako továrnička.

Ee tohle skončí nekonečným cyklem (createComponent volá sama sebe).

Honza Marek
Člen | 1664
+
0
-

Ee tohle skončí nekonečným cyklem (createComponent volá sama sebe).

Jo, už jsem ten dotaz pochopil. Prostě to musíš buď speciálně ošetřit nebo nepoužívat název komponenty „component“.

Přidám tam vyhození výjimky.

Editoval Honza M. (3. 4. 2009 14:32)

Jan Tvrdík
Nette guru | 2595
+
0
-

Pokud to dobře chápu, tak už ani není třeba předávat metodě create{$name} parametr $name.

Honza Marek
Člen | 1664
+
0
-

Jan Tvrdík napsal(a):

Pokud to dobře chápu, tak už ani není třeba předávat metodě create{$name} parametr $name.

Jj, je to tam jen kvůli případné zpětné kompatibilitě se starými funkcemi.

RaR
Člen | 42
+
0
-

Pokud zavolám $component->getComponent('cosi'), výše popsaná funkce protected function createComponent($name) v basePresenteru
bude hledat protected function createCosi($name) kde v $name bude zase ‚cosi‘.
Je to tak?
Pokud mám definovaný jeden formulář $form = new AppForm($this, $name); a na základě jeho jména se rozhoduji, co se s daty má dělat, tak toho výše uvedeným způsobem nedosáhnu, protože když zavolám $component->getComponent('Form'), budou se všechny formuláře jmenovat ‚form‘.
Abych formům mohl přiřadit jméno musím přidat další parametr.

Honza Marek
Člen | 1664
+
0
-

RaR napsal(a):
když zavolám $component->getComponent('Form'), budou se všechny formuláře jmenovat ‚form‘.

Tak v tom případě dostaneš vždycky jeden a ten samý formulář. Pokud již nebude vytvořen (tedy při prvním volání), tak ho vytvoří (a buď vrátí nebo sama připojí – podle implementace) metoda createForm.

Jan Tvrdík
Nette guru | 2595
+
0
-

Honza M. napsal(a): Takhle to bude asi vůbec nejlepší

Tak to už nějakou dobu používám a dnes jsem narazil na dost hustou chybu. Jedná se o nesmyslné použití funkce is_callable, která vždycky vrátí TRUE, protože Presenter dědí od Object, který definuje metodu __call a v případě, že je definována metoda __call, tak funkce is_callable vrací vždy TRUE (viz komentář na php.net).

Tzn., že funkci is_callable je nutné nahradit funkcí method_exists :)