API RESTful - Ресурсы

RESTful API - это все о доступе и управлении ресурсами. Вы можете рассматривать ресурсы как модели в парадигме MVC.

Хотя нет ограничений в представлении ресурса, в Yii вы обычно представляли бы ресурсы в терминах объектов yii\base\Model или его дочерних классов (например, yii\db\ActiveRecord) по следующим причинам:

  • yii\base\Model реализует интерфейс yii\base\Arrayable, который позволяет вам настроить, как вы хотите предоставлять данные о ресурсах через API RESTful.
  • yii\base\Model поддерживает проверку ввода, что полезно, если ваши RESTful API должны поддерживать ввод данных.
  • yii\db\ActiveRecord обеспечивает мощную поддержку доступа к данным и манипуляций с БД, что делает его идеальным решением, если ваши данные о ресурсах хранятся в базах данных

В этом разделе мы в основном опишем, как класс ресурса, расширяющийся от yii\base\Model (или его дочерних классов), может указывать, какие данные могут быть возвращены через API RESTful. Если класс ресурса не распространяется от yii\base\Model, тогда будут возвращены все его открытые переменные-члены.

Поля

При включении ресурса в ответ RESTful API ресурс должен быть сериализован в строку. Yii разбивает этот процесс на два этапа. Во-первых, ресурс преобразуется в массив посредством yii\rest\Serializer. Во-вторых, массив сериализуется в строку в запрошенном формате (например, JSON, XML) с помощью форм ответа. Первым шагом является то, что вы должны в основном сосредоточиться при разработке класса ресурсов.

Переопределяя fields() или extraFields(), вы можете указать, какие данные, называемые полями, в ресурсе могут быть помещены в его представление массива. Разница между этими двумя методами заключается в том, что первый определяет набор полей по умолчанию, который должен быть включен в представление массива, а второй указывает дополнительные поля, которые могут быть включены в массив, если конечный пользователь запрашивает их через параметр запроса expand. Например:

// returns all fields as declared in fields()
http://localhost/users

// only returns field id and email, provided they are declared in fields()
http://localhost/users?fields=id,email

// returns all fields in fields() and field profile if it is in extraFields()
http://localhost/users?expand=profile

// only returns field id, email and profile, provided they are in fields() and extraFields()
http://localhost/users?fields=id,email&expand=profile

Переопределение fields()

По умолчанию yii\base\Model::fields() возвращает все атрибуты модели в виде полей, в то время как yii\db\ActiveRecord::fields() возвращает только атрибуты, которые были заполнены из БД.

Вы можете переопределить fields() для добавления, удаления, переименования или переопределения полей. Возвращаемое значение fields() должно быть массивом. Ключами массива являются имена полей, а значениями массива являются соответствующие определения полей, которые могут быть либо именами свойств/атрибутов, либо анонимными функциями, возвращающими соответствующие значения полей. В особом случае, когда имя поля совпадает с именем определяющего атрибута, вы можете пропустить ключ массива. Например:

// 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 ($model) {
            return $model->first_name . ' ' . $model->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;
}

Переопределение extraFields()

По умолчанию yii\base\Model::extraFields() возвращает пустой массив, в то время как yii\db\ActiveRecord::extraFields() возвращает имена отношений, которые были заполнены из базы данных.

Формат возвращаемых данных extraFields() такой же, как у fields(). Обычно extraFields() в основном используется для указания полей, значения которых являются объектами. Например, учитывая следующее объявление поля:

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

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

Запрос с http://localhost/users?fields=id,email&expand=profile может возвращать следующие данные JSON:

[
    {
        "id": 100,
        "email": "100@example.com",
        "profile": {
            "id": 100,
            "age": 30,
        }
    },
    ...
]

Ссылки

HATEOAS, аббревиатура для Hypermedia как двигателя состояния приложения, способствует тому, что API RESTful должны возвращать информацию, которая позволяет клиентам обнаруживать действия, поддерживаемые для возвращаемых ресурсов. Ключ HATEOAS - вернуть набор гиперссылок с информацией о связи, когда данные ресурса обслуживаются API.

Ваши классы ресурсов могут поддерживать HATEOAS, реализуя интерфейс yii\web\Linkable. Интерфейс содержит единственный метод getLinks(), который должен возвращать список ссылок. Как правило, вы должны вернуть по крайней мере ссылку, представляющую URL-адрес, самому ресурсу. Например:

use yii\base\Model;
use yii\web\Link; // represents a link object as defined in JSON Hypermedia API Language.
use yii\web\Linkable;
use yii\helpers\Url;

class UserResource extends Model implements Linkable
{
    public $id;
    public $email;

    //...

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

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

    public function getLinks()
    {
        return [
            Link::REL_SELF => Url::to(['user/view', 'id' => $this->id], true),
            'edit' => Url::to(['user/view', 'id' => $this->id], true),
            'profile' => Url::to(['user/profile/view', 'id' => $this->id], true),
            'index' => Url::to(['users'], true),
        ];
    }
}

Когда в ответе возвращается объект UserResource, он будет содержать элемент _links, представляющий ссылки, связанные с пользователем, например:

{
    "id": 100,
    "email": "user@example.com",
    // ...
    "_links" => {
        "self": {
            "href": "https://example.com/users/100"
        },
        "edit": {
            "href": "https://example.com/users/100"
        },
        "profile": {
            "href": "https://example.com/users/profile/100"
        },
        "index": {
            "href": "https://example.com/users"
        }
    }
}

Коллекции

Объекты ресурсов могут быть сгруппированы в коллекции. Каждая коллекция содержит список объектов ресурсов одного типа.

Хотя коллекции могут быть представлены как массивы, обычно более желательно представлять их в качестве поставщиков данных. Это связано с тем, что поставщики данных поддерживают сортировку и разбиение на страницы ресурсов, что обычно необходимо для возврата коллекций RESTful API. Например, следующее действие возвращает поставщика данных о ресурсах сообщения:

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(),
        ]);
    }
}

Когда поставщик данных отправляется в ответ RESTful API, yii\rest\Serializer выведет текущую страницу ресурсов и сериализует их как массив объектов ресурсов. Кроме того, yii\rest\Serializer также будет включать информацию о разбиении на страницы по следующим HTTP-заголовкам:

  • X-Pagination-Total-Count: Общее количество ресурсов;
  • X-Pagination-Page-Count: Количество страниц;
  • X-Pagination-Current-Page: Текущая страница (на основе 1);
  • X-Pagination-Per-Page: Количество ресурсов на каждой странице;
  • Link: Набор навигационных ссылок, позволяющих клиенту перемещаться по страницам страниц.