If widgets go into IoC it should be a specialized "view IoC" not to mix presentation object with core objects. This approach has some disadvantages, e.g., another entry point (widgets bootstrap??) to configure all the widgets used at page beside those instantiated in controller.
The factory approach is easier in terms of lazy loading. I vote +1 for it.
As Andres mentioned, the goal is to allow code reuse in views. I think If widget is something which is shared between pages aka HTML snippet with no or simple presentation logic behind, it can be implemented via partials. By the way, is passing parameters to partials supported or do they use global scope?
If widget has complex logic behind, it should be a class extending some core widget class. Factory which instantiate widgets should be aware of DI and inject it if widget implements Phalcon\DI\InjectionAwareInterface.
For example, we have a SiteMap class and a widget which can render breadcrumbs using current route:
class MyBreadcrubs extends Phalcon\Mvc\View\Widget implements Phalcon\DI\InjectionAwareInterface
{
protected $di;
public function setDi() { ... }
public function getDi() { ... }
public function render()
{
$sitemap = $this->di->get('sitemap');
$router = $this->di->get(''router);
$path = $sitemap->locate(
$router->getModuleName(),
$router->getControllerName(),
$router->getActionName(),
);
$this->view->setVar('path', $path);
// ... rendering
}
}
I think widgets should work their own views. There is a use case, a calendar which changes its layout:
class MyBreadcrubs extends Phalcon\Mvc\View\Widget implements Phalcon\DI\InjectionAwareInterface
{
const VIEW_WEEK = 'week';
const VIEW_MONTH = 'month';
const VIEW_YEAR = 'year';
const VIEW_AGENDA = 'agenda';
protected $di;
protected $viewStyle;
protected $startDate;
public function __construct(array $parameters = array())
{
parent::__construct($parameters);
$this->viewStyle = ( isset($parameters['style']) ? $parameters['style'] : self::VIEW_WEEK );
$this->startDate = ( isset($parameters['date']) ? $parameters['date'] : date('Y-m-d') );
}
public function setDi() { ... }
public function getDi() { ... }
public function render()
{
$this->view->setVar('date', $this->startDate);
switch($this->viewStyle) {
case self::VIEW_WEEK:
$this->view->setMainView('week.html');
break;
case self::VIEW_WEEK:
$this->view->setMainView('week.html');
break;
case self::VIEW_MONTH:
$this->view->setMainView('month.html');
break;
case self::VIEW_YEAR:
$this->view->setMainView('year.html');
break;
case self::VIEW_AGENDA:
$this->view->setMainView('agenda.html');
break;
default:
thrown new \InvalidArgumentException('Invalid calendar view style');
}
// ... rendering