Skip to content

Latest commit

 

History

History
297 lines (248 loc) · 10.7 KB

README.md

File metadata and controls

297 lines (248 loc) · 10.7 KB

Yii REST

The Yii REST extension adds classes and filters that help you write RESTful controllers. Each RESTController has three components:

  1. An Adaptor. This maps client data to your action parameters, e.g. $_GET['Customer'] =>"data".
  2. A Params model. This validates action parameters and loads your underlying ActiveRecord (or other) models.
  3. The controller. This ties the above two together along with actions and filters.

The result is a controller that can describe its interface (via the Adaptor) and easily respond with detailed error messages (via the params model). The controller and its actions are extremely thin and each component can be swapped out given a different user/ request scenario.

Overview

Key components:

  • RESTAdaptor: An adaptor layer translating raw client request data to a format understood by your controller actions. An OOP version of CController::getActionParams().
  • RESTParams: A form model responsible for validating and loaded your action parameters based on the raw data parsed by the adaptor. An OOP version of loadModel().
  • RESTController: A base controller that ties together the two components above along with sensible default filters, e.g. verbs and validation filtering.
  • RESTAction(s): A set of lightweight actions that handle all of your CRUD needs.

The ultimate goal is to create controllers and actions that are extremely thin, with swappable components for both their public interface and parameter generation, and that can be described programatically for later documentation via the HTTP OPTIONS method.

In addition, the controllers should be format-agnostic. Actions should only expose raw data via RESTAction::getResult(), which the controller can translate (through delegation) to a response that converts the raw data into a type allowed by the user. (E.g. JSON, HTML, XML, ...) However this is not currently implemented, as it is a feature much more easily handled using the upcoming Yii2.

Installation

Copy or clone the folder into your extensions directory and add the following alias to your application:

<?php
Yii::setPathOfAlias('yii-rest', dirname(__FILE__).'/../extensions/yii-rest');
?>

The file yii-rest/components/RESTController.php inherits from a custom base controller assumed to live at application.components.Controller. You must modify this if your base controller is located elsewhere, named differently, or if you do not use a custom base controller.

Components

RESTController

The RESTController introduces two new properties:

  • $_restParams: A form model representing the parameters passed to your controller actions. This is analogous to the loadModel() method in the standard Gii crud controllers, but in an OOP fashion.
  • $_restAdaptor: A class that maps client request data to $_restParams attributes. This is analogous to CController::getActionParams() in that it returns the raw request data bound to your action parameters.

There are several benefits to including these new components:

  • RESTParams makes it trivial to render API error messages back to users
  • RESTAdaptor describes the public interface for the controller
  • To change the public interface, change the adaptor model
  • To change the action parameters, change the params model. (E.g. swap out "Admin" and "User" models depending on the current user.)

Example: CustomersController.php

<?php
/**
 * RESTful customers controller
 *
 * The default implementation will load a Adaptor component called `CustomersAdaptor` and a params
 * model called `CustomersParams`. The rest is done for you. The parent implementation adds standard
 * REST verb filters and validates the params model.
 *
 * @see CustomersAdaptor
 * @see CustomersParams
 */
class CustomersController extends RESTController
{
    // You actually don't have to configure anything.
    //
    // The default RESTController comes with new, create, edit,
    // update, list, and delete actions. In addition, sensible
    // default filters prevent invalid HTTP requests from reaching
    // those methods.
    //
    // However, you will need to configure your adaptor and params
    // classes to return the parameters expected by those actions.
    //
    // You might also want to configure your `accessRules()`.
}
?>

RESTAdaptor

The RESTAdaptor maps client request data to the attributes of your RESTParams model.

For example, RESTActionSave expects to receive a parameter called 'data'. Your forms likely submit data with a key like 'Customer'. You can use the adaptor to map the 'Customer' key generated by CActiveForm to the 'data' attribute expected by the params.

The RESTAdaptor allows you to configure:

  • What client params the controller will check for data
  • Where the controller will look for that data. (E.g. GET, POST, ...)
  • What internal attribute that data is mapped to. (I.e. RESTParams attribute names)

The RESTController uses the RESTAdaptor to parse the raw request for model attributes and to populate the RESTParams model with those attributes.

Example: CustomersAdaptor.php

<?php
/**
 * The client->backend adaptor for the CustomersController
 */
class CustomersAdaptor extends RESTAdaptor
{
    /**
     * @return array  RESTAdaptorParam configuration arrays
     * @see  RESTAdaptor::loadParams() for how to specify these
     */
    public $interface = array(
        // These public and private keys are identical
        array(RESTSource::GET, 'id'),
        array(RESTSource::GET, 'type'),
        // Here the client provides the 'Customer' parameter, which is mapped
        // to the 'data' property of the params model
        array(RESTSource::ANY, 'Customer' =>'data'),
    );
}
?>

RESTParams

RESTParams validates and processes raw request parameters extracted by the adaptor and makes them available to your actions. RESTParams::$scenario should correspond to an action ID.

This class serves as the action parameter provider and validator for your controller. It's similar to the standard loadModel() method generated by Gii, but more powerful and reusable.

If implementing RESTParams, you must implement the loadModel() method. However, you do not have to implement RESTParams; any CFormModel will also work, with its public properties used to bind action parameters. But if you find yourself adding a 'model' or 'data' property to your form model, RESTParams is probably an easier choice.

Example: CustomersParams.php

<?php
/**
 * Represents action parameters for the CustomersController
 */
class CustomersParams extends RESTParams
{
    /**
     * @var integer  customer id. If set, [loadModel()] will attempt to load this customer after
     *                            applying all other filters and scopes.
     */
    public $id;

    /**
     * @var integer  customer type. If set, scope is restricted to customers of this type.
     */
    public $type;

    /**
     * @see RESTParams::$model
     */
    // public $model;
    //
    /**
     * @see RESTParams::$data
     */
    // public $data;

    /**
     * Loads the Customer model
     *
     * The model's scope is always restricted to return only active customers owned by the current user.
     * If the `type` attribute is set, only customers of that type will be returned. If the `id`
     * attribute is set, this will attempt to load that Customer. (After applying all filters.)
     *
     * You should not call this method directly. It is set a a filter on the [model] property in
     * [RESTParams::rules()].
     *
     * @return Customer  the customer model for this request
     */
    protected function loadModel()
    {
        $user = Yii::app()->getUser();

        $model = new Customer();
        // Apply scopes first
        $model->scopeByOwner($user->id)->scopeByStatus(Customer::STATUS_ACTIVE);
        if ($this->type) {
            $model->scopeByType($this->type);
        }
        // Scenario-specific rules
        if ('list' === $this->scenario) {
            $model->setScenario('search');
            $model->unsetAttributes();
        } else if ('create' === $this->scenario) {
            $model->owner_id = $user->id;
        }
        // Attempt to retrieve a specific customer
        if ($this->id) {
            $model = $model->findByPk($this->id);
        }
        return $model;
    }

    /**
     * Returns validation rules
     *
     * Scenario names are action IDs.
     *
     * @return array  validation rule configurations
     */
    public function rules()
    {
        return CMap::mergeArray(
            array(
                // ID is only valid on actions dealing with a specific customer
                array(
                    'id',
                    'required',
                    'on' =>array('view', 'update', 'delete'),
                ),
                array(
                    'type',
                    'in',
                    'range' =>array(Customer::ACTIVE, Customer::DISABLED),
                    'on' =>array('list'),
                ),
                array(
                    'data',
                    'type',
                    'type' =>'array',
                ),
            ),
            // Merge parent rules so that [model] is loaded, required, and declared unsafe.
            parent::rules()
        );
    }
}
?>

RESTActions

This extension ships with four basic CRUD actions:

  • RESTActionLoad Loads data into a model and renders a view. Works for 'list' or plain 'view' actions.
  • RESTActionValidate Loads data into a model and validates it. Works well for 'new' and 'edit' actions which present forms.
  • RESTActionSave Loads data into a model and saves it. Works well for 'create' and 'update' actions that actually modify the database.
  • RESTActionDelete Deletes a model.

Each action can be configured with its own $view and $params, which determines the view into with the action's result and parameters are rendered.

Filters

Two filters are bundled with this extension:

  • RESTVerbsFilter, which filters requests which use the wrong HTTP method
  • RESTParamsFilter, which filters requests where $_restParams is invalid

(There is an empty base yii-rest.components.RESTFilter that you can override as you see fit.)

These are pre-configured in the RESTController as follows:

<?php
class RESTController extends CController {
    ...
    public function filters()
    {
        return array(
            array('RESTVerbsFilter', 'actions' =>array('update'), 'verbs' =>array('PUT', 'PATCH')),
            array('RESTVerbsFilter', 'actions' =>array('create'), 'verbs' =>array('POST')),
            array('RESTVerbsFilter', 'actions' =>array('delete'), 'verbs' =>array('DELETE')),
            array('RESTParamsFilter'),
        );
    }
    ...
}
?>