Yii2 RESTful API-інтерфейс для AngularJS


Здрастуйте, дорогі читачі! Судячи з вашим коментарям попереднім статтям, багатьом з вас дуже цікаво, як саме ми будемо використовувати AngularJS в зв'язці з нашим додаток на Yii2 фреймворку. У цій статті я підніму завісу і опишу процес підключення фреймворку AngularJS і способи його застосування.

Я планую використовувати Yii2 для основної маси програми, але Angular я буду використовувати для таких частин програми, як модальне вікно додавання оголошення, пошук по сайту, карбилдер (частина функціональності сайту, що дозволяє зібрати машину своєї мрії і відправити запит на неї адміністратору сайту і дилерам партнерам) і т. д. Нічого такого, що б змінило поточний рауса, але подібний підхід додасть нашому додатком чуйності і простоти взаємодії з відвідувачем.

В перспективі ми будемо переводити проект в одностраничное додаток на AngularJS, яке буде взаємодіяти з бекендом Yii2 REST. Але на даний момент, я не хочу це робити, тому що тоді нам треба буде обробляти маршрутизацію, валідацію, авторизацію, права доступів і тому подібне як в Angular.js, так і на бэкенде.

Структура RESTful API Yii надає нам чистий API, який може обмінюватися даними з вбудованим AngularJS додатком, або ж, з нашим мобільним додатком в майбутньому.

Так як ми дбаємо про продуктивності, ми будемо обома ногами йти в бік REST. Добре структуроване RESTful додаток є великим плюсом, особливо якщо там опрацьована хороша система кешування з гнучкою стратегією. Також, ми плануємо розмістити бекенд і базу даних на сервері Amazon EC2 і надавати тільки JSON дані для мінімального використання смуги пропускання. Тим більше, що Amazon EC2 володіє такою особливістю, як автоматичне масштабування, яка дозволяє йому автоматично адаптувати обчислювальні потужності в залежності від трафіку сайту. А що стосується нашого статичного контенту, ми будемо зберігати його на оптимізованої CDN Amazon S3, яка має найнижчу вартість і більш швидку швидкість відповідей. Про це я буду писати більш докладно в своїх наступних статтях.

Yii2 фреймворк надає цілий набір інструментів для спрощення реалізації і впровадження RESTful API. Зараз я покажу приклад, як можна побудувати набір RESTful
API, з мінімальними витратами зусиль на написання коду.

Для початку створимо контролер для API в модулі
Lease
, він буде перебувати по шляху
modules/lease/controllers/frontend
файл
ApiController.php
і в ньому пропишемо наступне:

<?php
namespace modules\lease\controllers\frontend;

use yii\rest\ActiveController;

class ApiController extends ActiveController
{
public $modelClass = 'modules\lease\models\frontend\Lease';
}

Клас контролера успадковується від yii\rest\ActiveController, який реалізує загальний набір RESTful дій. Вказавши
modelClass
,
modules\lease\models\frontend\Lease
, контролер знає, яка модель може бути використана для вилучення і маніпулювання даними.

Для того, щоб надати можливість користуватися нашим API допомогою ajax-запитів з інших доменів, можна додати поведінка
cors
в контролер, якщо це необхідно:

...
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['corsFilter' ] = [
'class' => \yii\filters\Cors::className(),
];
// У це місце ми будемо додавати поведінки (читай нижче)
return $behaviors;
}
...

Зверніть увагу, що ми хочемо зберегти конфігурацію поведінки yii\rest\Controller, ось чому ми зберігаємо його у першому рядку
$behaviors = parent::behaviors();
і продовжуємо додавати до нього поведінки.

Yii2 REST API-інтерфейс здатний віддавати відповіді в XML, JSON форматах. Ми будемо використовувати тільки формат JSON, — повідомимо про це фреймворку:

...
$behaviors['contentNegotiator'] = [
'class' => \yii\filters\ContentNegotiator::className(),
'formats' => [
'application/json' => \yii\web\Response::FORMAT_JSON,
],
];
...

Ми легко можемо налаштувати різні права доступів до дій контролера API c допомогою класу yii\filtersAccessControl. Зазначимо, що створювати, редагувати і видаляти лістинги можуть тільки авторизовані користувачі:

...
$behaviors['access'] = [
'class' => \yii\filters\AccessControl::className(),
'only' => ['create', 'update', 'delete'],
'rules' => [
[
'actions' => ['create', 'update', 'delete'],
'allow' => true,
'roles' => ['@'],
],
],
];
...

Також, для забезпечення контролю доступу до дій можна перевизначити метод
CheckAccess()
, для того, щоб перевірити, чи є поточний користувач привілей на виконання певної дії над зазначеною моделлю даних. Наприклад, напишемо перевірку, щоб користувач міг редагувати або видаляти тільки свої оголошень:

...
public function checkAccess($action, $model = null, $params = [])
{
// перевіряємо чи може користувач редагувати або видалити запис
// викидаємо виняток ForbiddenHttpException якщо доступ заборонений
if ($action === 'update' || $action === 'delete') {
if ($model->user_id !== \Yii::$app->user->id)
throw new \yii\web\ForbiddenHttpException(sprintf('You can only %s lease that you\'ve created.', $action));
}
}
...

Цей метод буде автоматично викликатися для дій класу yii\rest\ActiveController. Якщо ви створюєте в контролері нову дію, в якому також необхідно виконати перевірку доступу, ви повинні викликати цей метод явним чином в нових діях.

Наступним етапом ми змінимо конфігурацію компонента
urlManager
в бутстрэппинг класі модуля у файлі
modules/lease/Bootstrap.php


...
$app->getUrlManager()->addRules(
[
[
'class' => 'yii\rest\UrlRule',
'controller' => ['leases' => 'lease/api'],
'prefix' => 'api'
]
]
);
...

Наведена вище конфігурація в основному додає URL правило для контролера API таким чином, що дані можуть бути доступні і ними можна буде маніпулювати з використанням короткого URL-адреси і відповідного HTTP заголовка. Ми додали
prefix
, для того, щоб всі посилання для звернення до API сервісів починалися з
/api/*
, таким чином, з цього паттерну цей контент можна буде запросто приховати від пошукових машин у файлі robots.txt. Надалі, в кожному модулі, який буде брати участь у RESTful API-інтерфейсу, ми таким же чином будемо додавати контролер для API.

Написаним вище мінімальною кількістю коду, ми вже виконали своє завдання по створенню RESTful API-інтерфейсу для доступу до даних з моделі
Lease
. Ми створили API-інтерфейси, які включають в себе:

  • GET /api/leases: Отримати всі листинги
  • HEAD /api/leases: Отримати заголовок відповіді на запит GET /api/leases
  • POST /api/leases: Створити новий лістинг
  • GET /api/leases/3: Отримати дані з лістингу id=3
  • HEAD /api/leases/3: Отримати заголовок відповіді GET /api/leases/3
  • PATCH /api/leases/3 і PUT /api/leases/3: Змінити дані лістингу з id=3
  • DELETE /api/leases/3: Видалити лістинг id=3
  • OPTIONS /api/leases: Отримати список доступних методів запиту для /api/leases
  • OPTIONS /api/leases/3: Отримати список доступних методів запиту для /api/leases/3
З допомогою механізму RESTful API фреймворку Yii2, ми реалізували API эндпоинты у вигляді дій контролера і використовуємо ці дії для одного типу даних (для однієї моделі).

На даний момент, ми реалізували частину бекенду для додатка і тепер можемо приступити до реалізації фронтенда – почнемо з того, що підключимо AngularJS. Робити це будемо використовуючи composer, нам просто потрібно додати наступні рядки в секцію
"require"
у файлі
composer.json
.

...
"bower-asset/angular": "^1.5",
"bower-asset/angular-animate": "^1.5",
"bower-asset/angular-bootstrap": "^2.2"
...

І виконати команду в командному рядку
php composer.phar update
.

Тепер давайте зробимо
AssetBundle
у файлі так, щоб ми могли з легкістю підчепити AngularJS файли до лейауту. Ось так буде виглядати файл
\frontend\assets\AngularAsset.php


<?php
namespace frontend\assets;

use yii\web\AssetBundle;
use yii\web\View;

class AngularAsset extends AssetBundle
{
public $sourcePath = '@bower';
public $js = [
'angular/angular.js',
'angular-animate/angular-animate.min.js',
'angular-bootstrap/ui-bootstrap.min.js',
'angular-bootstrap/ui-bootstrap-tpls.min.js'
];

public $jsOptions = [
'position' => View::POS_HEAD,
];
}

public $jsOptions = [ 'position' => View::POS_HEAD, ];
повідомляє Yii, що необхідно підключити JavaScript файли у секції head нашого лейаута замість того, щоб розміщувати їх в самому кінці секції body, таким чином, ядро AngularJS завантажиться настільки швидко, наскільки це можливо. Ми також повинні додати цей Bundle залежно головного
asset bundle
програми і там же підключити ще один файл
'js/app.js'
, в якому буде знаходиться примірник AngularJS програми.

<?php

namespace frontend\assets;

use yii\web\AssetBundle;

/**
* Main frontend application asset bundle.
*/
class AppAsset extends AssetBundle
{
public $basePath = '@webroot';
public $baseUrl = '@web';
public $css = [
'css/site.css',
];
public $js = [
'js/app.js',
'js/controllers.js',
'js/directives.js',
'js/services.js',
];
public $depends = [
'yii\web\YiiAsset',
'yii\bootstrap\BootstrapAsset',
'frontend\assets\AngularAsset'
];
}

Давайте заодно додамо файл
controllers.js
в цей же бандл. Надалі, ми пропишемо в ньому контролери нашого фронтенда,
directives.js
та
services.js
для опису директив і сервісів відповідно.

Почнемо імплементувати Angular під в'юхи програми з додавання директиви
ng-app
до елемента html – це буде запускати наше AngularJS додаток, яке ми назвемо
"app"
.

...
<html lang="<?= Yii::$app->language ?>" data-ng-app="app" >
...

Зверніть увагу, що я написав директиву
data-ng-app
саме так, інакше, наша html розмітка не пройде W3C валідацію. На роботу директив це ніякого впливу не надає, а верстка у нас повинна проходити валідацію згідно специфікації проекту. Тому для всіх нестандартних директив ми будемо дописувати приставку
data-
.

Код для Angular програми буде розташовуватися в
frontend/web/js/app.js
. Там ми визначимо додаток і підключимо до нього модулі, які ми збираємося використовувати.

'use strict';

var app = angular.module('app', [
'ngAnimate',
'ui.bootstrap',
'controllers' //наш модуль frontend/web/js/controllers.js
]);

У файлі
frontend/web/js/services.js
напишемо сервіс
LeaseService
, який буде виконувати CRUD операції для лістингів допомогою спілкування з REST API бекенду:

'use strict';

var app = angular.module('app');

app.service('LeaseService', function($http) {
this.get = function() {
return $http.get('/api/leases');
};
this.post = function (data) {
return $http.post('/api/leases', data);
};
this.put = function (id data) {
return $http.put('/api/leases/' + id data);
};
this.delete = function (id) {
return $http.delete('/api/leases/' + id);
};
});

На сьогоднішній день, наш модуль
controllers
буде містити в собі дуже простий контролер
LeaseController
. Він буде запитувати дані від
/api/leases
і передавати дані в відображення.

'use strict';

var controllers = angular.module('controllers', []);

controllers.controller('LeaseController', ['$scope', 'LeaseService',
function ($scope, LeaseService) {
$scope.leases = [];
LeaseService.get().then(function (data) {
if (data.status == 200)
$scope.leases = data.data;
}, function (err) {
console.log(err);
})
}
]);

Використовуємо цей контролер у файлі
\modules\lease\views\frontend\default\index.php
вказавши через директиву
ng-controller
і виводимо в таблицю дані, які нам надав API:

<div class="lease-default-index" data-ng-controller="LeaseController">
<div>
<h1>All Leases</h1>
<div data-ng-show="leases.length > 0">
<table class="table table-striped table-hover">
<thead>
<th>Year</th>
<th>Make</th>
<th>Model</th>
<th>Trim</th>
</thead>
<tbody>
<tr data-ng-repeat="lease in leases">
<td>{{lease.year}}</td>
<td>{{lease.make}}</td>
<td>{{lease.model}}</td>
<td>{{lease.trim}}</td>
</tr>
</tbody>
</table>
</div>
<div data-ng-show="leases.length == 0">
No results
</div>
</div>
</div>

Тепер, коли ми переглянемо сторінку по шляху
/lease/default/index
, ми побачимо список всіх тексти, записані в базу даних.


Сподіваюся, ви переконалися, наскільки швидко і легко можна створити RESTful API-інтерфейс на базі фреймворку Yii2. Весь описаний в цій та попередніх статтях код, доступний для вас у репозиторії.

Матеріал підготували: greebn9k(Сергій Грибняк), pavel-berezhnoy(Павло Бережний)
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.