Authoring a widget that provides its own ajax actions

I was wondering how to bundle a widget together with the ajax actions it needs into an extension that is easy to reuse across projects.

rAWTAZ pointed me at a nice wiki How to use a Widget as an Action Provider which shows how Yii provides for that contingency.

But the wiki doesn’t address bundling the widget class and its (many?) action classes. So I tried putting all the classes in one file and it works very nicely. The Yii autoloader finds everything.

So I documented my example (based on the wiki mentioned) and invite your comments. The code is also on github if you are interested.


<?php


/**

 * This package contains a widget class and two action method classes that the

 * widget provides to the CController that uses it.

 *

 * The controller must register the actions provided by this widget as follows,

 * in which we assume this package is in the 'extensions' directory under

 * CWebApplication::$basePath

 * <code>

 * <?php

 *     public function actions() {

 *         return array(

 *             'prefix.' => 'ext.SillyWidget',

 *             // etc...

 *         );

 *     }

 * </code>

 * In which 'prefix' is a string of the users choosing that must also be set in

 * the widget's CWidget::$actionPrefix property (see below).

 *

 * The user can use the widget in a view, setting the action prefix declared in

 * the controller (see above), thus:

 * <code>

 * <?php

 * $this->widget('ext.SillyWidget', array(

 *     'actionPrefix' => 'prefix.',

 *     'label' => 'Click one',

 * ));

 *

 * $this->widget('ext.SillyWidget', array(

 *     'actionPrefix' => 'prefix.',

 *     'buttonAction' => 'get2',

 *     'message' => 'Different message',

 * ));

 * </code>

 *

 * @package SillyWidget

 * @author {@link https://github.com/tom--}

 * @copyright Copyright (c) 2012, {@link https://github.com/tom--}

 * @license {@link http://www.opensource.org/licenses/BSD-3-Clause}

 */


/**

 * Provides a rot13 action used by {@link TestWidget}.

 */

class GetData extends CAction {

    /**

     * Responds with the rot13 of a message.

     * @param string $message The message sent by the client.

     * @response JSON object with one string member named 'result'.

     */

    public function run($message = 'Default1') {

        echo CJSON::encode(array('result' => str_rot13($message)));

    }

}


/**

 * Provides a string shuffling action used by {@link TestWidget}.

 */

class GetMoreData extends CAction {

    /**

     * Responds with message string shuffled.

     * @param string $message The message sent by the client.

     * @response JSON object with one string member named 'result'.

     */

    public function run($message = 'Default2') {

        echo CJSON::encode(array('result' => str_shuffle($message)));

    }

}


/**

 * Renders an ajax button that requests a JSON object from the server

 * and logs the 'result' member of that object.

 *

 * The user of the widget **must** set the CWidget::$actionPrefix property.

 */

class SillyWidget extends CWidget {

    /**

     * @var string Action ID used in the URL of the button's ajax request.

     */

    public $buttonAction = 'get1';

    /**

     * @var string Button label.

     */

    public $label = 'Click';

    /**

     * @var string Text sent to server in the ajax request's 'message' param.

     */

    public $message = 'HELLO WORLD';


    /**

     * Override CWidget::actions() to register the Action ID to CAction class

     * mapping of the actions this widget provides.

     * @return array

     */

    public static function actions() {

        return array(

            'get1' => 'ext.SillyWidget.GetData',

            'get2' => 'ext.SillyWidget.GetMoreData',

        );

    }


    public function run() {

        YiiBase::getLogger()->log($this->buttonAction);

        echo CHtml::ajaxButton($this->label,

            array($this->controller->id . '/'

                . $this->actionPrefix . $this->buttonAction

            ),

            array(

                'dataType' => 'JSON',

                'data' => array('message' => $this->message),

                'success' => <<<JS

                    function (data) {

                        console.log(data.result);

                    }

JS

            )

        );

    }

}

Just a quick note: if your extension ships as a module, there is a path alias for it, so you can use Yii::import() to load your classes.