rest-resources.md 8.24 KB
Newer Older
Qiang Xue committed
1 2 3
Resources
=========

Qiang Xue committed
4 5
RESTful APIs are all about accessing and manipulating *resources*. You may view resources as
[models](structure-models.md) in the MVC paradigm.
Qiang Xue committed
6

Qiang Xue committed
7 8 9
While there is no restriction in how to represent a resource, in Yii you usually would represent resources
in terms of objects of [[yii\base\Model]] or its child classes (e.g. [[yii\db\ActiveRecord]]), for the
following reasons:
Qiang Xue committed
10

Qiang Xue committed
11 12
* [[yii\base\Model]] implements the [[yii\base\Arrayable]] interface, which allows you to
  customize how you want to expose resource data through RESTful APIs.
Qiang Xue committed
13 14
* [[yii\base\Model]] supports [input validation](input-validation.md), which is useful if your RESTful APIs
  need to support data input.
Qiang Xue committed
15 16 17 18 19 20
* [[yii\db\ActiveRecord]] provides powerful DB data access and manipulation support, which makes it
  a perfect fit if your resource data is stored in databases.

In this section, we will mainly describe how a resource class extending from [[yii\base\Model]] (or its child classes)
can specify what data may be returned via RESTful APIs. If the resource class does not extend from [[yii\base\Model]],
then all its public member variables will be returned.
Qiang Xue committed
21 22


Qiang Xue committed
23
## Fields <a name="fields"></a>
Qiang Xue committed
24

Qiang Xue committed
25 26 27 28 29
When including a resource in a RESTful API response, the resource needs to be serialized into a string.
Yii breaks this process into two steps. First, the resource is converted into an array by [[yii\rest\Serializer]].
Second, the array is serialized into a string in a requested format (e.g. JSON, XML) by
[[yii\web\ResponseFormatterInterface|response formatters]]. The first step is what you should mainly focus when
developing a resource class.
Qiang Xue committed
30

Qiang Xue committed
31
By overriding [[yii\base\Model::fields()|fields()]] and/or [[yii\base\Model::extraFields()|extraFields()]],
Qiang Xue committed
32
you may specify what data, called *fields*, in the resource can be put into its array representation.
Qiang Xue committed
33 34 35
The difference between these two methods is that the former specifies the default set of fields which should
be included in the array representation, while the latter specifies additional fields which may be included
in the array if an end user requests for them via the `expand` query parameter. For example,
Qiang Xue committed
36

Qiang Xue committed
37 38 39
```
// returns all fields as declared in fields()
http://localhost/users
Qiang Xue committed
40

Qiang Xue committed
41 42
// only returns field id and email, provided they are declared in fields()
http://localhost/users?fields=id,email
Qiang Xue committed
43

Qiang Xue committed
44 45
// returns all fields in fields() and field profile if it is in extraFields()
http://localhost/users?expand=profile
Qiang Xue committed
46

Qiang Xue committed
47 48 49
// only returns field id, email and profile, provided they are in fields() and extraFields()
http://localhost/users?fields=id,email&expand=profile
```
Qiang Xue committed
50 51


Qiang Xue committed
52
### Overriding `fields()` <a name="overriding-fields"></a>
Qiang Xue committed
53 54 55 56 57 58 59 60 61

By default, [[yii\base\Model::fields()]] returns all model attributes as fields, while
[[yii\db\ActiveRecord::fields()]] only returns the attributes which have been populated from DB.

You can override `fields()` to add, remove, rename or redefine fields. The return value of `fields()`
should be an array. The array keys are the field names, and the array values are the corresponding
field definitions which can be either property/attribute names or anonymous functions returning the
corresponding field values. In the special case when a field name is the same as its defining attribute
name, you can omit the array key. For example,
Qiang Xue committed
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94

```php
// explicitly list every field, best used when you want to make sure the changes
// in your DB table or model attributes do not cause your field changes (to keep API backward compatibility).
public function fields()
{
    return [
        // field name is the same as the attribute name
        'id',
        // field name is "email", the corresponding attribute name is "email_address"
        'email' => 'email_address',
        // field name is "name", its value is defined by a PHP callback
        'name' => function () {
            return $this->first_name . ' ' . $this->last_name;
        },
    ];
}

// filter out some fields, best used when you want to inherit the parent implementation
// and blacklist some sensitive fields.
public function fields()
{
    $fields = parent::fields();

    // remove fields that contain sensitive information
    unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);

    return $fields;
}
```

> Warning: Because by default all attributes of a model will be included in the API result, you should
> examine your data to make sure they do not contain sensitive information. If there is such information,
Qiang Xue committed
95
> you should override `fields()` to filter them out. In the above example, we choose
Qiang Xue committed
96 97 98
> to filter out `auth_key`, `password_hash` and `password_reset_token`.


Qiang Xue committed
99
### Overriding `extraFields()` <a name="overriding-extra-fields"></a>
Qiang Xue committed
100

Qiang Xue committed
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
By default, [[yii\base\Model::extraFields()]] returns nothing, while [[yii\db\ActiveRecord::extraFields()]]
returns the names of the relations that have been populated from DB.

The return data format of `extraFields()` is the same as that of `fields()`. Usually, `extraFields()`
is mainly used to specify fields whose values are objects. For example, given the following field
declaration,

```php
public function fields()
{
    return ['id', 'email'];
}

public function extraFields()
{
    return ['profile'];
}
```

the request with `http://localhost/users?fields=id,email&expand=profile` may return the following JSON data:
Qiang Xue committed
121 122 123 124 125 126 127 128 129 130 131 132 133 134

```php
[
    {
        "id": 100,
        "email": "100@example.com",
        "profile": {
            "id": 100,
            "age": 30,
        }
    },
    ...
]
```
Qiang Xue committed
135 136


Qiang Xue committed
137
## Links <a name="links"></a>
Qiang Xue committed
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158

[HATEOAS](http://en.wikipedia.org/wiki/HATEOAS), an abbreviation for Hypermedia as the Engine of Application State,
promotes that RESTful APIs should return information that allow clients to discover actions supported for the returned
resources. The key of HATEOAS is to return a set of hyperlinks with relation information when resource data are served
by the APIs.

Your resource classes may support HATEOAS by implementing the [[yii\web\Linkable]] interface. The interface
contains a single method [[yii\web\Linkable::getLinks()|getLinks()]] which should return a list of [[yii\web\Link|links]].
Typically, you should return at least the `self` link representing the URL to the resource object itself. For example,

```php
use yii\db\ActiveRecord;
use yii\web\Link;
use yii\web\Linkable;
use yii\helpers\Url;

class User extends ActiveRecord implements Linkable
{
    public function getLinks()
    {
        return [
159
            Link::REL_SELF => Url::to(['user/view', 'id' => $this->id], true),
Qiang Xue committed
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
        ];
    }
}
```

When a `User` object is returned in a response, it will contain a `_links` element representing the links related
to the user, for example,

```
{
    "id": 100,
    "email": "user@example.com",
    // ...
    "_links" => [
        "self": "https://example.com/users/100"
    ]
}
```


Qiang Xue committed
180
## Collections <a name="collections"></a>
Qiang Xue committed
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218

Resource objects can be grouped into *collections*. Each collection contains a list of resource objects
of the same type.

While collections can be represented as arrays, it is usually more desirable to represent them
as [data providers](output-data-providers.md). This is because data providers support sorting and pagination
of resources, which is a commonly needed feature for RESTful APIs returning collections. For example,
the following action returns a data provider about the post resources:

```php
namespace app\controllers;

use yii\rest\Controller;
use yii\data\ActiveDataProvider;
use app\models\Post;

class PostController extends Controller
{
    public function actionIndex()
    {
        return new ActiveDataProvider([
            'query' => Post::find(),
        ]);
    }
}
```

When a data provider is being sent in a RESTful API response, [[yii\rest\Serializer]] will take out the current
page of resources and serialize them as an array of resource objects. Additionally, [[yii\rest\Serializer]]
will also include the pagination information by the following HTTP headers:

* `X-Pagination-Total-Count`: The total number of resources;
* `X-Pagination-Page-Count`: The number of pages;
* `X-Pagination-Current-Page`: The current page (1-based);
* `X-Pagination-Per-Page`: The number of resources in each page;
* `Link`: A set of navigational links allowing client to traverse the resources page by page.

An example may be found in the [Quick Start](rest-quick-start.md#trying-it-out) section.