Search code examples
phpzend-framework2instance-variablesmagic-methods

PHP magic method __invoke, empty instance variables


I'm facing a problem when calling __invoke() on an object. Is __invoke() method agnostic to instance variables? I need to call __invoke() directly on my templates due to some ZF2 injection to call $this->getView()->render(...) (otherwise getView() returns null) and I would like to have instance variables setted there. Any workaround?

See my code:

namespace Person\Person\View\Helper;


use Zend\View\Helper\AbstractHelper;

class PersonShowWidget extends AbstractHelper
{

    protected $model = null;

    public function __construct(array $options = null)
    {
        $this->parseOptions($options);
    }

    public function __invoke()
    {

        var_dump($this->model); //returns null
        return $this->getView()->render('person/show/show_widget', array(
                'title' => 'Cliente',
                'model' => $this->model,
            )
        );
    }

    public function setOptions(array $options = null)
    {
        $this->parseOptions($options);
    }

    protected function parseOptions(array $options = null)
    {
        if (!is_null($options) && is_array($options)) {
            if (isset($options['model'])) {
                $model = $options['model'];
                if (isset($model['id'])) {
                    $this->model['id'] = $model['id'];
                } else {
                    throw new \Exception;
                }
                if (isset($model['form'])) {
                    $this->model['form'] = $model['form'];
                } else {
                    throw new \Exception;
                }
            }
        }

        var_dump($this->model); //returns valid data

    }
}

I do have called the constructor with some options or the setOptions method before calling __invoke().

Thanks,


Solution

  • You have to initialize the view helper with a factory. In this way you can make sure the constructor is called before the __invoke method is called. And no..the __invoke() method is not agnostic to instance variables.

    In the Module.php

    public function getViewHelperConfig()
    {
        return array(
            'factories' => array(
                'personShowWidget' => function ($helpers) {
                    $array = array();
                    $helper = new Person\Person\View\Helper\PersonShowWidget($array);
                    return $helper;
                },
            )
        );
    }
    

    Or in the module.config.php

    'view_helpers' => array
    (
        'factories' => array(
            'personShowWidget' => function ($helpers) {
                $array = array();
                $helper = new Person\Person\View\Helper\PersonShowWidget($array);
                return $helper;
            },
        )
    )
    

    Performance-wise you'd better make a Factory class instead of a callable. More info: http://framework.zend.com/manual/2.0/en/modules/zend.module-manager.module-manager.html

    Edit:

    It seems like you using the ViewHelper wrongly. You don't have to create the instance by yourself. Just use the ViewHelper in the view. So why not just give the $options as parameter to the __invoke method?

    public function __invoke(array $options = null)
    {
        $this->setOptions($options);
    
        return $this->getView()->render('person/show/show_widget', array(
                'title' => 'Cliente',
                'model' => $this->model,
            )
        );
    }
    

    In the Controller pass the options array to the view:

    return array(
        'options' => $options,
    );
    

    And call the ViewHelper in the view:

    <?php echo $this->personShowWidget($this->options); ?>
    

    Remember: In this way you don't need a Factory to init the ViewHelper. Just add it to the invokables.

    module.config.php example:

    'view_helpers' => array(
        'invokables' => array(
            'personShowWidget' => 'Person\Person\View\Helper\PersonShowWidget',
        ),
    ),