<?xml version="1.0" encoding="UTF-8"?>
<!-- Reviewed: no -->
<sect1 id="zend.controller.action">
    <title>Action Controllers</title>

    <sect2 id="zend.controller.action.introduction">
        <title>Introduction</title>

        <para>
            <classname>Zend_Controller_Action</classname> is an abstract class you may use
            for implementing Action Controllers for use with the Front
            Controller when building a website based on the
            Model-View-Controller (<acronym>MVC</acronym>) pattern.
        </para>

        <para>
            To use <classname>Zend_Controller_Action</classname>, you will need to
            subclass it in your actual action controller classes (or subclass it
            to create your own base class for action controllers). The most
            basic operation is to subclass it, and create action methods that
            correspond to the various actions you wish the controller to handle
            for your site. <classname>Zend_Controller</classname>'s routing and dispatch handling
            will autodiscover any methods ending in 'Action' in your class as
            potential controller actions.
        </para>

        <para>
            For example, let's say your class is defined as follows:
        </para>

        <programlisting language="php"><![CDATA[
class FooController extends Zend_Controller_Action
{
    public function barAction()
    {
        // do something
    }

    public function bazAction()
    {
        // do something
    }
}
]]></programlisting>

        <para>
            The above <emphasis>FooController</emphasis> class (controller
            <emphasis>foo</emphasis>) defines two actions, <emphasis>bar</emphasis> and
            <emphasis>baz</emphasis>.
        </para>

        <para>
            There's much more that can be accomplished than this, such as custom
            initialization actions, default actions to call should no action (or
            an invalid action) be specified, pre- and post-dispatch hooks, and a
            variety of helper methods. This chapter serves as an overview of the
            action controller functionality
        </para>

        <note>
            <title>Default Behaviour</title>

            <para>
                By default, the <link linkend="zend.controller.front">front
                    controller</link> enables the <link
                    linkend="zend.controller.actionhelpers.viewrenderer">ViewRenderer</link>
                action helper. This helper takes care of injecting the view
                object into the controller, as well as automatically rendering
                views. You may disable it within your action controller via one
                of the following methods:
            </para>

            <programlisting language="php"><![CDATA[
class FooController extends Zend_Controller_Action
{
    public function init()
    {
        // Local to this controller only; affects all actions,
        // as loaded in init:
        $this->_helper->viewRenderer->setNoRender(true);

        // Globally:
        $this->_helper->removeHelper('viewRenderer');

        // Also globally, but would need to be in conjunction with the
        // local version in order to propagate for this controller:
        Zend_Controller_Front::getInstance()
            ->setParam('noViewRenderer', true);
    }
}
]]></programlisting>

            <para>
                <methodname>initView()</methodname>, <methodname>getViewScript()</methodname>,
                <methodname>render()</methodname>, and <methodname>renderScript()</methodname> each
                proxy to the <emphasis>ViewRenderer</emphasis> unless the helper is not
                in the helper broker or the <emphasis>noViewRenderer</emphasis> flag has
                been set.
            </para>

            <para>
                You can also simply disable rendering for an individual view by
                setting the <emphasis>ViewRenderer</emphasis>'s <emphasis>noRender</emphasis>
                flag:
            </para>

            <programlisting language="php"><![CDATA[
class FooController extends Zend_Controller_Action
{
    public function barAction()
    {
        // disable autorendering for this action only:
        $this->_helper->viewRenderer->setNoRender();
    }
}
]]></programlisting>

            <para>
                The primary reasons to disable the <emphasis>ViewRenderer</emphasis> are
                if you simply do not need a view object or if you are not
                rendering via view scripts (for instance, when using an action
                controller to serve web service protocols such as <acronym>SOAP</acronym>,
                <acronym>XML-RPC</acronym>, or <acronym>REST</acronym>). In most cases, you will
                never need to globally disable the <emphasis>ViewRenderer</emphasis>, only
                selectively within individual controllers or actions.
            </para>
        </note>
    </sect2>

    <sect2 id="zend.controller.action.initialization">
        <title>Object Initialization</title>

        <para>
            While you can always override the action controller's constructor, we
            do not recommend this. <methodname>Zend_Controller_Action::__construct()</methodname>
            performs some important tasks, such as registering the request and
            response objects, as well as any custom invocation arguments passed
            in from the front controller. If you must override the constructor,
            be sure to call <methodname>parent::__construct($request, $response,
            $invokeArgs)</methodname>.
        </para>

        <para>
            The more appropriate way to customize instantiation is to use the
            <methodname>init()</methodname> method, which is called as the last task of
            <methodname>__construct()</methodname>. For example, if you want to connect to
            a database at instantiation:
        </para>

        <programlisting language="php"><![CDATA[
class FooController extends Zend_Controller_Action
{
    public function init()
    {
        $this->db = Zend_Db::factory('Pdo_Mysql', array(
            'host'     => 'myhost',
            'username' => 'user',
            'password' => 'XXXXXXX',
            'dbname'   => 'website'
        ));
    }
}
]]></programlisting>
    </sect2>

    <sect2 id="zend.controller.action.prepostdispatch">
        <title>Pre- and Post-Dispatch Hooks</title>

        <para>
            <classname>Zend_Controller_Action</classname> specifies two methods that may
            be called to bookend a requested action, <methodname>preDispatch()</methodname>
            and <methodname>postDispatch()</methodname>. These can be useful in a variety of
            ways: verifying authentication and <acronym>ACL</acronym>'s prior to running an action
            (by calling <methodname>_forward()</methodname> in
            <methodname>preDispatch()</methodname>, the action will be skipped), for instance, or
            placing generated content in a sitewide template
            (<methodname>postDispatch()</methodname>).
        </para>

        <note>
            <title>Usage of init() vs. preDispatch()</title>

            <para>
                In the <link linkend="zend.controller.action.initialization">previous
                    section</link>, we introduced the <methodname>init()</methodname> method, and
                in this section, the <methodname>preDispatch()</methodname> method. What is the
                difference between them, and what actions would you take in each?
            </para>

            <para>
                The <methodname>init()</methodname> method is primarily intended for extending the
                constructor. Typically, your constructor should simply set object state, and not
                perform much logic. This might include initializing resources used in the controller
                (such as models, configuration objects, etc.), or assigning values retrieved from
                the front controller, bootstrap, or a registry.
            </para>

            <para>
                The <methodname>preDispatch()</methodname> method can also be used to set object
                or environmental (e.g., view, action helper, etc.) state, but its primary purpose
                is to make decisions about whether or not the requested action should be dispatched.
                If not, you should then <methodname>_forward()</methodname> to another action, or
                throw an exception.
            </para>

            <para>
                Note: <methodname>_forward()</methodname> actually will not work correctly when
                executed from <methodname>init()</methodname>, which is a formalization of the
                intentions of the two methods.
            </para>
        </note>
    </sect2>

    <sect2 id="zend.controller.action.accessors">
        <title>Accessors</title>

        <para>
            A number of objects and variables are registered with the object,
            and each has accessor methods.
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    <emphasis>Request Object</emphasis>: <methodname>getRequest()</methodname>
                    may be used to retrieve the request object used to call the action.
                </para>
            </listitem>

            <listitem>
                <para>
                    <emphasis>Response Object</emphasis>:
                    <methodname>getResponse()</methodname> may be used to retrieve the
                    response object aggregating the final response. Some typical
                    calls might look like:
                </para>

                <programlisting language="php"><![CDATA[
$this->getResponse()->setHeader('Content-Type', 'text/xml');
$this->getResponse()->appendBody($content);
]]></programlisting>
            </listitem>

            <listitem>
                <para>
                    <emphasis>Invocation Arguments</emphasis>: the front
                    controller may push parameters into the router, dispatcher,
                    and action controller. To retrieve these, use
                    <methodname>getInvokeArg($key)</methodname>; alternatively, fetch the
                    entire list using <methodname>getInvokeArgs()</methodname>.
                </para>
            </listitem>

            <listitem>
                <para>
                    <emphasis>Request parameters</emphasis>: The request object
                    aggregates request parameters, such as any <constant>_GET</constant> or
                    <constant>_POST</constant> parameters, or user parameters specified in the
                    <acronym>URL</acronym>'s path information. To retrieve these, use
                    <methodname>_getParam($key)</methodname> or
                    <methodname>_getAllParams()</methodname>. You may also set request
                    parameters using <methodname>_setParam()</methodname>; this is useful
                    when forwarding to additional actions.
                </para>

                <para>
                    To test whether or not a parameter exists (useful for
                    logical branching), use <methodname>_hasParam($key)</methodname>.
                </para>

                <note>
                    <para>
                        <methodname>_getParam()</methodname> may take an optional second
                        argument containing a default value to use if the
                        parameter is not set or is empty. Using it eliminates
                        the need to call <methodname>_hasParam()</methodname> prior to
                        retrieving a value:
                    </para>

                    <programlisting language="php"><![CDATA[
// Use default value of 1 if id is not set
$id = $this->_getParam('id', 1);

// Instead of:
if ($this->_hasParam('id') {
    $id = $this->_getParam('id');
} else {
    $id = 1;
}
]]></programlisting>
                </note>
            </listitem>
        </itemizedlist>
    </sect2>

    <sect2 id="zend.controller.action.viewintegration">
        <title>View Integration</title>

        <note id="zend.controller.action.viewintegration.viewrenderer">
            <title>Default View Integration is Via the ViewRenderer</title>

            <para>
                The content in this section is only valid when you have explicitly disabled the
                <link linkend="zend.controller.actionhelpers.viewrenderer">ViewRenderer</link>.
                Otherwise, you can safely skip over this section.
            </para>
        </note>

        <para>
            <classname>Zend_Controller_Action</classname> provides a rudimentary and
            flexible mechanism for view integration. Two methods accomplish
            this, <methodname>initView()</methodname> and <methodname>render()</methodname>; the
            former method lazy-loads the <varname>$view</varname> public property, and the
            latter renders a view based on the current requested action, using
            the directory hierarchy to determine the script path.
        </para>

        <sect3 id="zend.controller.action.viewintegration.initview">
            <title>View Initialization</title>

            <para>
                <methodname>initView()</methodname> initializes the view object.
                <methodname>render()</methodname> calls <methodname>initView()</methodname> in
                order to retrieve the view object, but it may be initialized at any time;
                by default it populates the <varname>$view</varname> property with a
                <classname>Zend_View</classname> object, but any class implementing
                <classname>Zend_View_Interface</classname> may be used. If
                <varname>$view</varname> is already initialized, it simply returns
                that property.
            </para>

            <para>
                The default implementation makes the following assumption of
                the directory structure:
            </para>

            <programlisting language="php"><![CDATA[
applicationOrModule/
    controllers/
        IndexController.php
    views/
        scripts/
            index/
                index.phtml
        helpers/
        filters/
]]></programlisting>

            <para>
                In other words, view scripts are assumed to be in the
                <filename>/views/scripts/</filename> subdirectory, and the
                <filename>/views/</filename> subdirectory is assumed to contain sibling
                functionality (helpers, filters). When determining the view
                script name and path, the <filename>/views/scripts/</filename> directory
                will be used as the base path, with directories named after the
                individual controllers providing a hierarchy of view scripts.
            </para>
        </sect3>

        <sect3 id="zend.controller.action.viewintegration.render">
            <title>Rendering Views</title>

            <para>
                <methodname>render()</methodname> has the following signature:
            </para>

            <programlisting language="php"><![CDATA[
string render(string $action = null,
              string $name = null,
              bool $noController = false);
]]></programlisting>

            <para>
                <methodname>render()</methodname> renders a view script. If no arguments are
                passed, it assumes that the script requested is
                <filename>[controller]/[action].phtml</filename> (where
                <filename>.phtml</filename> is the value of the <varname>$viewSuffix</varname>
                property). Passing a value for <varname>$action</varname> will render
                that template in the <filename>/[controller]/</filename> subdirectory. To
                override using the <filename>/[controller]/</filename> subdirectory, pass
                a <constant>TRUE</constant> value for <varname>$noController</varname>. Finally,
                templates are rendered into the response object; if you wish to render to
                a specific <link
                    linkend="zend.controller.response.namedsegments">named
                    segment</link> in the response object, pass a value to
                <varname>$name</varname>.
            </para>

            <note>
                <para>
                    Since controller and action names may contain word delimiter
                    characters such as '_', '.', and '-', <methodname>render()</methodname>
                    normalizes these to '-' when determining the script name. Internally,
                    it uses the dispatcher's word and path delimiters to do this
                    normalization. Thus, a request to
                    <filename>/foo.bar/baz-bat</filename> will render the script
                    <filename>foo-bar/baz-bat.phtml</filename>. If your action method
                    contains camelCasing, please remember that this will result
                    in '-' separated words when determining the view script
                    file name.
                </para>
            </note>

            <para>
                Some examples:
            </para>

            <programlisting language="php"><![CDATA[
class MyController extends Zend_Controller_Action
{
    public function fooAction()
    {
        // Renders my/foo.phtml
        $this->render();

        // Renders my/bar.phtml
        $this->render('bar');

        // Renders baz.phtml
        $this->render('baz', null, true);

        // Renders my/login.phtml to the 'form' segment of the
        // response object
        $this->render('login', 'form');

        // Renders site.phtml to the 'page' segment of the response
        // object; does not use the 'my/' subirectory
        $this->render('site', 'page', true);
    }

    public function bazBatAction()
    {
        // Renders my/baz-bat.phtml
        $this->render();
    }
}
]]></programlisting>
        </sect3>
    </sect2>

    <sect2 id="zend.controller.action.utilmethods">
        <title>Utility Methods</title>

        <para>
            Besides the accessors and view integration methods,
            <classname>Zend_Controller_Action</classname> has several utility methods for
            performing common tasks from within your action methods (or from
            pre- and post-dispatch).
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    <methodname>_forward($action, $controller = null, $module = null,
                        array $params = null)</methodname>: perform another action. If
                    called in <methodname>preDispatch()</methodname>, the currently
                    requested action will be skipped in favor of the new one.
                    Otherwise, after the current action is processed, the action
                    requested in <methodname>_forward()</methodname> will be executed.
                </para>
            </listitem>

            <listitem>
                <para>
                    <methodname>_redirect($url, array $options =
                        array())</methodname>: redirect to another location. This
                    method takes a <acronym>URL</acronym> and an optional set of options. By
                    default, it performs an <acronym>HTTP</acronym> 302 redirect.
                </para>

                <para>
                    The options may include one or more of the following:
                </para>

                <itemizedlist>
                    <listitem>
                        <para>
                            <emphasis>exit:</emphasis> whether or not to exit
                            immediately. If requested, it will cleanly close any
                            open sessions and perform the redirect.
                        </para>

                        <para>
                            You may set this option globally within the
                            controller using the <methodname>setRedirectExit()</methodname>
                            accessor.
                        </para>
                    </listitem>

                    <listitem>
                        <para>
                            <emphasis>prependBase:</emphasis> whether or not to
                            prepend the base <acronym>URL</acronym> registered with the request
                            object to the <acronym>URL</acronym> provided.
                        </para>

                        <para>
                            You may set this option globally within the
                            controller using the
                            <methodname>setRedirectPrependBase()</methodname> accessor.
                        </para>
                    </listitem>

                    <listitem>
                        <para>
                            <emphasis>code:</emphasis> what <acronym>HTTP</acronym> code to utilize
                            in the redirect. By default, an <acronym>HTTP</acronym> 302 is
                            utilized; any code between 301 and 306 may be used.
                        </para>

                        <para>
                            You may set this option globally within the
                            controller using the
                            <methodname>setRedirectCode()</methodname> accessor.
                        </para>
                    </listitem>
                </itemizedlist>
            </listitem>
        </itemizedlist>
    </sect2>

    <sect2 id="zend.controller.action.subclassing">
        <title>Subclassing the Action Controller</title>

        <para>
            By design, <classname>Zend_Controller_Action</classname> must be subclassed
            in order to create an action controller. At the minimum, you will
            need to define action methods that the controller may call.
        </para>

        <para>
            Besides creating useful functionality for your web applications, you
            may also find that you're repeating much of the same setup or
            utility methods in your various controllers; if so, creating a
            common base controller class that extends
            <classname>Zend_Controller_Action</classname> could solve such redundancy.
        </para>

        <example id="zend.controller.action.subclassing.example-call">
            <title>Handling Non-Existent Actions</title>

            <para>
                If a request to a controller is made that includes an undefined
                action method, <methodname>Zend_Controller_Action::__call()</methodname>
                will be invoked. <methodname>__call()</methodname> is, of course,
                <acronym>PHP</acronym>'s magic method for method overloading.
            </para>

            <para>
                By default, this method throws a
                <classname>Zend_Controller_Action_Exception</classname> indicating the
                requested method was not found in the controller. If the method
                requested ends in 'Action', the assumption is that an action was
                requested and does not exist; such errors result in an exception
                with a code of 404. All other methods result in an exception
                with a code of 500. This allows you to easily differentiate
                between page not found and application errors in your error
                handler.
            </para>

            <para>
                You should override this functionality if you wish to perform
                other operations. For instance, if you wish to display an error
                message, you might write something like this:
            </para>

            <programlisting language="php"><![CDATA[
class MyController extends Zend_Controller_Action
{
    public function __call($method, $args)
    {
        if ('Action' == substr($method, -6)) {
            // If the action method was not found, render the error
            // template
            return $this->render('error');
        }

        // all other methods throw an exception
        throw new Exception('Invalid method "'
                            . $method
                            . '" called',
                            500);
    }
}
]]></programlisting>

            <para>
                Another possibility is that you may want to forward on to a
                default controller page:
            </para>

            <programlisting language="php"><![CDATA[
class MyController extends Zend_Controller_Action
{
    public function indexAction()
    {
        $this->render();
    }

    public function __call($method, $args)
    {
        if ('Action' == substr($method, -6)) {
            // If the action method was not found, forward to the
            // index action
            return $this->_forward('index');
        }

        // all other methods throw an exception
        throw new Exception('Invalid method "'
                            . $method
                            . '" called',
                            500);
    }
}
]]></programlisting>
        </example>

        <para>
            Besides overriding <methodname>__call()</methodname>, each of the
            initialization, utility, accessor, view, and dispatch hook methods
            mentioned previously in this chapter may be overridden in order to
            customize your controllers. As an example, if you are storing your
            view object in a registry, you may want to modify your
            <methodname>initView()</methodname> method with code resembling the following:
        </para>

        <programlisting language="php"><![CDATA[
abstract class My_Base_Controller extends Zend_Controller_Action
{
    public function initView()
    {
        if (null === $this->view) {
            if (Zend_Registry::isRegistered('view')) {
                $this->view = Zend_Registry::get('view');
            } else {
                $this->view = new Zend_View();
                $this->view->setBasePath(dirname(__FILE__) . '/../views');
            }
        }

        return $this->view;
    }
}
]]></programlisting>

        <para>
            Hopefully, from the information in this chapter, you can see the
            flexibility of this particular component and how you can shape it to
            your application's or site's needs.
        </para>
    </sect2>
</sect1>
<!--
vim:se ts=4 sw=4 et:
-->
