Skip to content

Commit

Permalink
REST wip.
Browse files Browse the repository at this point in the history
  • Loading branch information
qiangxue committed Mar 5, 2014
1 parent c41e389 commit 3f42d58
Show file tree
Hide file tree
Showing 12 changed files with 309 additions and 76 deletions.
4 changes: 2 additions & 2 deletions docs/guide/rest.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ of the object if it implements [[yii\base\ArrayableInterface]]. If an object doe
an array consisting of all its public properties will be returned.

For classes extending from [[yii\base\Model]] or [[yii\db\ActiveRecord]], besides directly overriding `toArray()`,
you may also override the `fields()` method and/or the `expandableFields()` method to customize the data to be returned.
you may also override the `fields()` method and/or the `extraFields()` method to customize the data to be returned.

The method [[yii\base\Model::fields()]] declares a set of fields of an object that should be included in the result.
The default implementation returns all attributes of a model as the output fields. You can customize it to add,
Expand Down Expand Up @@ -254,7 +254,7 @@ returning the corresponding field values.
You may use the `fields` query parameter to specify which fields in `fields()` should be included in the result.
If this parameter is not specified, all fields returned by `fields()` will be returned.

The method [[yii\base\Model::expandableFields()]] is very similar to [[yii\base\Model::fields()]].
The method [[yii\base\Model::extraFields()]] is very similar to [[yii\base\Model::fields()]].
The difference between these methods is that the latter declares the fields that should be returned by default,
while the former declares the fields that should only be returned when the user specifies them in the `expand` query parameter.

Expand Down
163 changes: 163 additions & 0 deletions framework/base/ArrayableTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
<?php
/**
* @link https://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license https://www.yiiframework.com/license/
*/

namespace yii\base;

use Yii;
use yii\helpers\ArrayHelper;
use yii\web\Link;
use yii\web\Linkable;

/**
*
* @author Qiang Xue <[email protected]>
* @since 2.0
*/
trait ArrayableTrait
{
/**
* Returns the list of fields that should be returned by default by [[toArray()]] when no specific fields are specified.
*
* A field is a named element in the returned array by [[toArray()]].
*
* This method should return an array of field names or field definitions.
* If the former, the field name will be treated as an object property name whose value will be used
* as the field value. If the latter, the array key should be the field name while the array value should be
* the corresponding field definition which can be either an object property name or a PHP callable
* returning the corresponding field value. The signature of the callable should be:
*
* ```php
* function ($field, $model) {
* // return field value
* }
* ```
*
* For example, the following code declares four fields:
*
* - `email`: the field name is the same as the property name `email`;
* - `firstName` and `lastName`: the field names are `firstName` and `lastName`, and their
* values are obtained from the `first_name` and `last_name` properties;
* - `fullName`: the field name is `fullName`. Its value is obtained by concatenating `first_name`
* and `last_name`.
*
* ```php
* return [
* 'email',
* 'firstName' => 'first_name',
* 'lastName' => 'last_name',
* 'fullName' => function () {
* return $this->first_name . ' ' . $this->last_name;
* },
* ];
* ```
*
* In this method, you may also want to return different lists of fields based on some context
* information. For example, depending on the privilege of the current application user,
* you may return different sets of visible fields or filter out some fields.
*
* The default implementation of this method returns the public object member variables.
*
* @return array the list of field names or field definitions.
* @see toArray()
*/
public function fields()
{
$fields = array_keys(Yii::getObjectVars($this));
return array_combine($fields, $fields);
}

/**
* Returns the list of fields that can be expanded further and returned by [[toArray()]].
*
* This method is similar to [[fields()]] except that the list of fields returned
* by this method are not returned by default by [[toArray()]]. Only when field names
* to be expanded are explicitly specified when calling [[toArray()]], will their values
* be exported.
*
* The default implementation returns an empty array.
*
* You may override this method to return a list of expandable fields based on some context information
* (e.g. the current application user).
*
* @return array the list of expandable field names or field definitions. Please refer
* to [[fields()]] on the format of the return value.
* @see toArray()
* @see fields()
*/
public function extraFields()
{
return [];
}

/**
* Converts the model into an array.
*
* This method will first identify which fields to be included in the resulting array by calling [[resolveFields()]].
* It will then turn the model into an array with these fields. If `$recursive` is true,
* any embedded objects will also be converted into arrays.
*
* If the model implements the [[Linkable]] interface, the resulting array will also have a `_link` element
* which refers to a list of links as specified by the interface.
*
* @param array $fields the fields being requested. If empty, all fields as specified by [[fields()]] will be returned.
* @param array $expand the additional fields being requested for exporting. Only fields declared in [[extraFields()]]
* will be considered.
* @param boolean $recursive whether to recursively return array representation of embedded objects.
* @return array the array representation of the object
*/
public function toArray(array $fields = [], array $expand = [], $recursive = true)
{
$data = [];
foreach ($this->resolveFields($fields, $expand) as $field => $definition) {
$data[$field] = is_string($definition) ? $this->$definition : call_user_func($definition, $field, $this);
}

if ($this instanceof Linkable) {
$data['_links'] = Link::serialize($this->getLinks());
}

return $recursive ? ArrayHelper::toArray($data) : $data;
}

/**
* Determines which fields can be returned by [[toArray()]].
* This method will check the requested fields against those declared in [[fields()]] and [[extraFields()]]
* to determine which fields can be returned.
* @param array $fields the fields being requested for exporting
* @param array $expand the additional fields being requested for exporting
* @return array the list of fields to be exported. The array keys are the field names, and the array values
* are the corresponding object property names or PHP callables returning the field values.
*/
protected function resolveFields(array $fields, array $expand)
{
$result = [];

foreach ($this->fields() as $field => $definition) {
if (is_integer($field)) {
$field = $definition;
}
if (empty($fields) || in_array($field, $fields, true)) {
$result[$field] = $definition;
}
}

if (empty($expand)) {
return $result;
}

foreach ($this->extraFields() as $field => $definition) {
if (is_integer($field)) {
$field = $definition;
}
if (in_array($field, $expand, true)) {
$result[$field] = $definition;
}
}

return $result;
}
}
54 changes: 6 additions & 48 deletions framework/base/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,12 @@
*/
class Model extends Component implements IteratorAggregate, ArrayAccess, Arrayable
{
use ArrayableTrait;

/**
* The name of the default scenario.
*/
const SCENARIO_DEFAULT = 'default';

/**
* @event ModelEvent an event raised at the beginning of [[validate()]]. You may set
* [[ModelEvent::isValid]] to be false to stop the validation.
Expand Down Expand Up @@ -829,8 +830,8 @@ public static function validateMultiple($models, $attributes = null)
* ```
*
* In this method, you may also want to return different lists of fields based on some context
* information. For example, depending on privilege of the current application user, you may return different
* sets of visible fields.
* information. For example, depending on [[scenario]] or the privilege of the current application user,
* you may return different sets of visible fields or filter out some fields.
*
* The default implementation of this method returns [[attributes()]] indexed by the same attribute names.
*
Expand All @@ -843,52 +844,9 @@ public function fields()
return array_combine($fields, $fields);
}

/**
* Returns the list of fields that can be expanded further and returned by [[toArray()]].
*
* This method is similar to [[fields()]] except that the list of fields returned
* by this method are not returned by default by [[toArray()]]. Only when field names
* to be expanded are explicitly specified when calling [[toArray()]], will their values
* be exported.
*
* The default implementation returns an empty array.
*
* @return array the list of expandable field names or field definitions. Please refer
* to [[fields()]] on the format of the return value.
* @see toArray()
* @see fields()
*/
public function expandableFields()
{
return [];
}

/**
* Converts the object into an array.
* The default implementation will return [[attributes]].
* @param array $fields the fields being requested. If empty, all fields as specified by [[fields()]] will be returned.
* @param array $expand the additional fields being requested for exporting. Only fields declared in [[expandableFields()]]
* will be considered.
* @param boolean $recursive whether to recursively return array representation of embedded objects.
* @return array the array representation of the object
*/
public function toArray(array $fields = [], array $expand = [], $recursive = true)
{
$data = [];
foreach ($this->resolveFields($fields, $expand) as $field => $definition) {
$data[$field] = is_string($definition) ? $this->$definition : call_user_func($definition, $field, $this);
}

if ($this instanceof Linkable) {
$data['_links'] = Link::serialize($this->getLinks());
}

return $recursive ? ArrayHelper::toArray($data) : $data;
}

/**
* Determines which fields can be returned by [[toArray()]].
* This method will check the requested fields against those declared in [[fields()]] and [[expandableFields()]]
* This method will check the requested fields against those declared in [[fields()]] and [[extraFields()]]
* to determine which fields can be returned.
* @param array $fields the fields being requested for exporting
* @param array $expand the additional fields being requested for exporting
Expand All @@ -912,7 +870,7 @@ protected function resolveFields(array $fields, array $expand)
return $result;
}

foreach ($this->expandableFields() as $field => $definition) {
foreach ($this->extraFields() as $field => $definition) {
if (is_integer($field)) {
$field = $definition;
}
Expand Down
16 changes: 15 additions & 1 deletion framework/data/Pagination.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
namespace yii\data;

use Yii;
use yii\base\Arrayable;
use yii\base\Object;
use yii\web\Link;
use yii\web\Linkable;
Expand Down Expand Up @@ -67,7 +68,7 @@
* @author Qiang Xue <[email protected]>
* @since 2.0
*/
class Pagination extends Object implements Linkable
class Pagination extends Object implements Linkable, Arrayable
{
const LINK_NEXT = 'next';
const LINK_PREV = 'prev';
Expand Down Expand Up @@ -315,6 +316,19 @@ public function getLinks($absolute = false)
return $links;
}

/**
* @inheritdoc
*/
public function toArray()
{
return [
'totalCount' => $this->totalCount,
'pageCount' => $this->getPageCount(),
'currentPage' => $this->getPage(),
'perPage' => $this->getPageSize(),
];
}

/**
* Returns the value of the specified query parameter.
* This method returns the named parameter value from [[params]]. Null is returned if the value does not exist.
Expand Down
2 changes: 1 addition & 1 deletion framework/db/BaseActiveRecord.php
Original file line number Diff line number Diff line change
Expand Up @@ -1361,7 +1361,7 @@ public function fields()
*
* The default implementation returns the names of the relations that have been populated into this record.
*/
public function expandableFields()
public function extraFields()
{
$fields = array_keys($this->getRelatedRecords());
return array_combine($fields, $fields);
Expand Down
2 changes: 1 addition & 1 deletion framework/rest/Action.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public function findModel($id)
if (count($keys) === count($values)) {
$model = $modelClass::find(array_combine($keys, $values));
}
} else {
} elseif ($id !== null) {
$model = $modelClass::find($id);
}

Expand Down
5 changes: 3 additions & 2 deletions framework/rest/ActiveController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
namespace yii\rest;

use yii\base\InvalidConfigException;
use yii\base\Model;
use yii\web\ForbiddenHttpException;

/**
Expand Down Expand Up @@ -44,12 +45,12 @@ class ActiveController extends Controller
* @var string the scenario used for updating a model.
* @see \yii\base\Model::scenarios()
*/
public $updateScenario = 'api-update';
public $updateScenario = Model::SCENARIO_DEFAULT;
/**
* @var string the scenario used for creating a model.
* @see \yii\base\Model::scenarios()
*/
public $createScenario = 'api-create';
public $createScenario = Model::SCENARIO_DEFAULT;
/**
* @var boolean whether to use a DB transaction when creating, updating or deleting a model.
* This property is only useful for relational database.
Expand Down
2 changes: 1 addition & 1 deletion framework/rest/AuthInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
use yii\web\UnauthorizedHttpException;

/**
* AuthInterface is the interface required by classes than support user authentication.
* AuthInterface is the interface required by classes that support user authentication.
*
* @author Qiang Xue <[email protected]>
* @since 2.0
Expand Down
Loading

0 comments on commit 3f42d58

Please sign in to comment.