Component factories duplicity solution

Notice: This thread is very old.
eudard
Member | 15
+
+1
-

Hello all, sorry for my english.. I need to help with components factories which are defined in presenter and also in control..

I have Presenter with lot of component factories like this..

class MainPresenter extends BasePresenter {

	public function createComponentText($name) {
	....
	}
	public function createComponentImage($name) {
	....
	}
	public function createComponentMap($name) {
	....
	}
	//...and much more factories
}

One of those factories is component “container”, is defined like this..

....

public function createComponentContainer($name) {
	$control = new UI\Multiplier(function($name) {
		$control=new componentContainer();
		return $control;
        });
	return $control;
    }

....

In this special component I need to define every component factories, which are defined in MainPresenter, so I need to define same component factories in component Container like this..

class componentContainer extends coreComponent {
	public function createComponentText($name) {
	....
	}
	public function createComponentImage($name) {
	....
	}
	public function createComponentMap($name) {
	....
	}

	//...and much more factories
}

My question is: How to centralize definitions component factories in one file/parent or otherwise?
Because everytime I create new component, I have to declare it in MainPresenter and also in componentContainer.

Main problem is, that MainPresenter is presenter a componentContainer is control. Is there posibility to inherite definitions component factories from a parent or any dynamic loading? How to define it?

P.S.: Why duplicate definitions? Factories in presenter allow me to add components into application template (hard coded in LATTE template). Factories in componentContainer allow me to drag and drop component dynamicaly to page content by user admin.

Thanks for all reactions.

Last edited by eudard (2014-06-03 16:18)

Filip Procházka
Moderator | 4668
+
+1
-
  1. createComponent methods should be protected
  2. use traits
  3. Ideally use generated factories

Last edited by Filip Procházka (2014-06-03 18:49)

eudard
Member | 15
+
0
-

Filip Procházka wrote:

  1. createComponent methods should be protected
  2. use traits
  3. Ideally use generated factories

Ok, but if I understand “generated factories”..

presenter have to defined methods..

	private function createComponentText() {
		return $this->textFactory->create();
	    }
	private function createComponentImage($name) {
		return $this->imageFactory->create();
	    }
	public function createComponentMap($name) {
		return $this->mapFactory->create();
	    }

and also in componentContainer component..

but.. i want to “createControl” get out of presenter ..
I there any method to create factories more dynamic?

For example…

  1. i have web which use only “componentImage” and “componentText” which are defined /PageModule/controls/
  2. this controls want to use in /presenters/HomepagePresenter.php
  3. I will create “pageconfig.neon” and there is defined something like this
factoriesInUse:
	/PageModule/componentImage:true
	/PageModule/componentText:true
	/RatingModule/componentRating:false
	/FacebookModule/componentLikebox:false
	....

so is there a way to create factories dynamicaly (for example in BasePresenter) by configuration (file or database config) like above?

Can I rewrite createComponent() function? Is this right way?
Like this..

public function createComponent($name) {
	$component_exists = $this->isComponentDefinedInConfig($name);
	if(!$component_exists) {
		return new componentNotAllowed($this, $name);
	}
	//here i don't know how to do it
	$component_info = $this->getComponentFromConfig($name); //get component data to create (file,class etc)
	$component = parent::createComponent($component_info->name);  //main create instance
	return $component;
}

Benefits:

  • I can have another web with different configuration (limited use of controls)
  • Someone can make new specific module with own controls, which can be injected to presenter by config file. He don't need to change any presenter(s) code and components will be working in presenter templates.
eudard
Member | 15
+
0
-

thanks Filip Procházka to kick me forward :)

My solution to share components from one resource…

Presenter implementation:

class BasePresenter extends Presenter {

	private static $coreComponentFactory;

	public function getCoreComponentFactory() {
		if (empty(self::$coreComponentFactory)) {
			self::$coreComponentFactory = new coreComponentFactory;
		}
		return self::$coreComponentFactory;
	}

	protected function createComponent($name) {
		$ucname = ucfirst($name);
		$method = 'createComponent' . $ucname;
		$componentFactory = $this->getCoreComponentFactory();
		if (method_exists($componentFactory, $method)) {
			$component = $componentFactory->$method($name);
			if (!$component instanceof Nette\ComponentModel\IComponent) {
				$class = get_class($this);
				throw new Nette\UnexpectedValueException("Method $class::$method() did not return or create the desired component.");
			}
		return $component;
		}
	return parent::createComponent($name);
	}

}

Core control implementation:

class coreComponent extends Control {
	protected function createComponent($name) {
		$ucname = ucfirst($name);
		$method = 'createComponent' . $ucname;
		$componentFactory = $this->presenter->getCoreComponentFactory();
		if (method_exists($componentFactory, $method)) {
			$component = $componentFactory->$method($name);
			if (!$component instanceof Nette\ComponentModel\IComponent) {
				$class = get_class($this);
				throw new Nette\UnexpectedValueException("Method $class::$method() did not return or create the desired component.");
			}
		return $component;
		}
	return parent::createComponent($name);
	}
}

And shared components factory…

class coreComponentFactory extends Object {

	public function createComponentMyComponent1($name) {
		$control = new UI\Multiplier(function($name) use ($self) {
			$control=new MyComponent1();
			return $control;
		});
        return $control;
	}

	public function createComponentMyComponent2($name) {
		$control = new UI\Multiplier(function($name) use ($self) {
			$control=new MyComponent2();
			return $control;
		});
        return $control;
	}

	public function createComponentMyComponent3($name) {
		$control = new UI\Multiplier(function($name) use ($self) {
			$control=new MyComponent3();
			return $control;
		});
        return $control;
	}
}

Benefits of this solution:

  • all component factories (createComponent function) are in one file ( not in presenter or controls )
  • all components which inherite coreComponent can create all other components
  • all presenters which inherite BasePresenter can create all components

What you think about it?

Last edited by eudard (2014-06-27 13:12)