The Yii REST extension adds classes and filters that help you write RESTful controllers. Each RESTController has three components:
- An Adaptor. This maps client data to your action parameters, e.g.
$_GET['Customer'] =>"data"
. - A Params model. This validates action parameters and loads your underlying ActiveRecord (or other) models.
- 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.
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.
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.
The RESTController introduces two new properties:
$_restParams
: A form model representing the parameters passed to your controller actions. This is analogous to theloadModel()
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 toCController::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 usersRESTAdaptor
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.)
<?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()`.
}
?>
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.
<?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 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.
<?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()
);
}
}
?>
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.
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'),
);
}
...
}
?>