Zend Framework View Helpers

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

Post Reply
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Zend Framework View Helpers

Post by John Cartwright »

Okay I've developed yet another form view helper because of my distaste for the existing ones out there, only to find my understand of Zend Framework's implementation of the view helper was significantly off..

Let me just jump right into the code and explain what I would like to hear your thoughts about.

Code: Select all

/**
 * Zodiac_Form
 *
 * @category   Zodiac
 * @package    Zodiac_Form
 * @author	 John Cartwright
 * @published  September 2007
*/
final class Zodiac_View_Helper_Form extends Zodiac_Form_Options
{	
    /**
    * Form
    * Primary function for view helper class
    *
    * @returns (object) self
    */
    public function form()
    {
        return $this;
    }
	
    /**
    *
    * Snip Snip... 
    *
    */

    /**
    * __call
    * Gateway function for rerouting to form element renderer
    *
    * @returns (string)
    */
    public function __call($method, array $options)
    {
        return Zodiac_Form_Factory::get($method)->render($this->_parseOptions($options));
    }	
}
The View Helper system in the Zend Framework calls the Zodiac_View_Helper_Form::Form() in this case expecting to be the method of the helper required. In my case, this view helper is a miniature library in itself which uses __call to redirect to the proper form element to be rendered.

So this means that I am forced to return an instance of Zodiac_View_Helper_Form object when the helper is called, then using a fluent interface call the right function..

Code: Select all

//what I have to do now
$this->form()->input('name');

//what I want to do now
$this->form->input('name');
I know I'm being a little picky here, but I would prefer store the object itself as a variable in the Zend View Helper object without having to repeatedly call Zodiac_View_Helper_Form::form(). Any workarounds?


Bonus Question: Whats the best way to acquire the request object into a view helper in this case? To simply tuck it into Zend_View then using setView() in my helper class to acquire it?
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

Wouldn't it be easier to extend the Action controller to create a proper Form controller that does what you want it to do. I would stay away from their helper monkey business. It is a wonky, high overhead way to add methods. Every time I hear Matthew explain how to solve a problem with a Helper and a Dispatch Loop Shutdown doohickey ... I cringe.
(#10850)
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Post by John Cartwright »

Thanks for the response arborint.

I think I kind of know where you were going with that (Form Controller).. although I'm still cloudy on the matter. Although, shouldn't the rending of html elements take place in the view?

Can you please elaborate?
User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Post by Luke »

Do you mean something like this arborint? (see last method)

Code: Select all

<?php
require_once 'Zend/Controller/Action.php';

class MC2_Controller_Action extends Zend_Controller_Action
{
    /**
     * Database connection (retrieved from front injection in bootstrap index.php)
     */
    protected $db = null;
    
    /**
     * Configuration object
     */
    protected $config = null;

    /**
     * By setting these variables, I am making them available to all of my controllers
     * It is not necessary to call parent::init() because nothing is in it
     */
    public function init()
    {
        $front = Zend_Controller_Front::getInstance();
        $this->db = $front->getParam('db');
        $this->config = $front->getParam('config');
        $this->initView();
    }
    
    /**
     * This may not be the correct way to do this. I am not sure exactly
     * what initview does and if I should be initializing my view variables
     * here or not. 
     */
    public function initView()
    {
        parent::initView();
        
        $this->view->MEDIA = MC2_Bag::getBaseUrl() . '/' . $this->config->view->media_url;
        $this->view->BASEURL = MC2_Bag::getBaseUrl();
        $this->view->DEBUG = $this->config->debug;
        $this->view->config = $this->config->view;
        
        // if debug is on, let's show the developer their environment 
        if ($this->config->debug)
        {
            $this->view->POST    = $_POST;
            $this->view->GET     = $_GET;
            $this->view->SESSION = $_SESSION;
            $this->view->COOKIE  = $_COOKIE;
        }
    }
    
    /**
     * This is a function that is used by form methods to generically process
     * a form. It takes a Zend_Filter_Input instance as its argumnent, checks
     * the input's validity, and if it is invalid, it sets all the necessary
     * view variables so that error messages are populated etc. and returns
     * false. Otherwise, it returns true
     */
    protected function processInput(MC2_Input $input)
    {
        $input->filter();
        if ($input->isValid())
        {
            $this->view->VALID = TRUE;
        }
        else
        {
            foreach ($input->getMessages() as $element => $messages)
            {
                foreach ($messages as $message)
                {
                    $this->view->formSetError($element, $message);
                }
            }
        }
        d($input->getMessages());
    }
}
?>
User avatar
Maugrim_The_Reaper
DevNet Master
Posts: 2704
Joined: Tue Nov 02, 2004 5:43 am
Location: Ireland

Post by Maugrim_The_Reaper »

View Helper + DispatchLoopShutdown + ActionHelper + ...

Honestly, by the time something useful is added it takes 3+ classes and throws ten years of evaluated practice out the airlock. ;)

To JCart's question, View Helpers in ZF are designed solely to add ONE method to the View. This has notably created a workaround practice which you have at the moment - the ONE method return the actual object to use. You'll see the same thing in ZF 1.1 when my View enhancements are finally integrated.

A really naughty workaround would be to extend Zend_View and use a __call() to register multi-methods. So all calls to input() get routed internally to form()->input(). If you need to keep the $form as an object on the View though, you could amend __get() to instantiate the form helper JIT to the $form class member. If subclassing isn't your cup of tea, you'll need to do something different - perhaps have a FormController parent which when used adds the $form object for all it's children?
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

I think Ninja is heading in the right direction. His code exposes the fact that ZF makes it easier to use the Front Controller as a Registry that to actually use the Registry. And you can see the damage that does. The Controller should stick to controlling -- which is what processInput() is doing. I would rather see the Form Controller pass a registry through to the View. The Form Controller is really just concerned about the state of the form -- is it first time in so initialize?, has it been submitted? and are there errors or not?. It can then display the View or redirect as appropriate.

A Form Controller is a little unique in that upon submission, the Request becomes the Model. There may be a model to get values from on initialization and write to upon success. So create a consistent model -- either just the Request or a real ViewHelper Model. Let the View sort out what values it needs. Keep the controller generic and reusable.

Helpers should be helpers, not extenders. They encapsulate data and re-present it in a useful way.
(#10850)
Post Reply