📜 ⬆️ ⬇️

Yii2 RESTful API for AngularJS


Hello dear readers! Judging by your comments on previous articles , many of you are very interested in how we will use AngularJS in conjunction with our application on the Yii2 framework. In this article I will raise the curtain and describe the process of connecting the AngularJS framework and how to use it.

I plan to use Yii2 for the main part of the application, but I will use Angular for such parts of the application as a modal window for adding ads, searching the site, a carbilder (part of the site’s functionality that allows you to assemble your dream car and send a request to the site administrator and dealers ) etc. Nothing that would change the current route, but this approach will give our application responsiveness and ease of interaction with the visitor.

In the future, we will translate the project into a one-page application on AngularJS, which will interact with the Yii2 REST backend. But at the moment, I do not want to do this, because then we will need to handle routing, validation, authorization, access rights, and the like, both in Angular.js and on the backend.
')
RESTful API structure Yii provides us with a clean API that can communicate with the embedded AngularJS application, or with our mobile application in the future.

Since we care about performance, we will walk with both feet towards REST. A well-structured RESTful application is a big plus, especially if there is a good caching system with a flexible strategy. Also, we plan to place the backend and database on Amazon EC2 server and provide only JSON data for minimal bandwidth usage. Moreover, Amazon EC2 has such a feature as automatic scaling, which allows it to automatically adapt computing capacity depending on site traffic. As for our static content, we will store it on an optimized Amazon S3 CDN, which has the lowest cost and faster response speed. I will write about this in more detail in my next articles.

The yii2 framework provides a range of tools to simplify the implementation and implementation of the RESTful API. Now I will demonstrate an example of how to build a RESTful set.
API, with minimal effort to write code.

First of all, let's create a controller for the API in the Lease module, it will be located along the modules/lease/controllers/frontend file of the ApiController.php file and write the following in it:

 <?php namespace modules\lease\controllers\frontend; use yii\rest\ActiveController; class ApiController extends ActiveController { public $modelClass = 'modules\lease\models\frontend\Lease'; } 

The controller class is inherited from yii \ rest \ ActiveController , which implements a common set of RESTful actions. modelClass specifying modelClass as modules\lease\models\frontend\Lease , the controller knows which model can be used to extract and manipulate data.

In order to provide the ability to use our API via ajax requests from other domains, you can add cors behavior to the controller, if necessary:

 ... public function behaviors() { $behaviors = parent::behaviors(); $behaviors['corsFilter' ] = [ 'class' => \yii\filters\Cors::className(), ]; //        ( ) return $behaviors; } ... 

Notice that we want to save the configuration of behavior from yii \ rest \ Controller , which is why we save it in the first line $behaviors = parent::behaviors(); and continue to add behaviors to it.

The Yii2 REST API is capable of responding in XML and JSON formats. We will only use the JSON format, and let us inform the framework about it:

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

We can also easily configure various access rights to actions of the API controller using the yii \ filtersAccessControl class . Specify that only authorized users can create, edit and delete listings:

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

Also, to provide access control to actions, you can override the CheckAccess() method to check whether the current user has the privilege to perform a specific action on the specified data model. For example, let's write a check so that a user can edit or delete only his listings:

 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)); } } ... 

This method will be called automatically for actions of the yii \ rest \ ActiveController class. If you create a new action in the controller in which you also need to perform an access check, you must explicitly call this method in the new actions.

In the next step, we change the configuration of the urlManager component in the bootstrapping module class in the modules/lease/Bootstrap.php file

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

The above configuration basically adds the URL to the API controller so that the data can be accessed and manipulated using the short URL and the corresponding HTTP header. We added prefix so that all links for accessing API services start with /api/* , so this pattern can be easily hidden from search engines in the robots.txt file. In the future, in each module that will participate in the RESTful API, we will add the controller for the API in the same way.

Written above with the minimum amount of code, we have already completed our task of creating a RESTful API for accessing data from the Lease model. We have created APIs that include:


Using the Yii2 framework's RESTful API mechanism, we implemented the endpoint APIs as controller actions and use these actions for one data type (for one model).

At the moment, we have implemented part of the backend for the application and now we can begin to implement the frontend - let's start with the fact that we connect AngularJS. We will do this using composer, we just need to add the following lines to the “require” section of the composer.json file.

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

And run the command on the php composer.phar update command line php composer.phar update .

Now let's make the AssetBundle in the file so that we can easily pick up AngularJS files to the layout. This is what the \frontend\assets\AngularAsset.php file will look like

 <?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, ]; Tells Yii that you need to attach JavaScript files in the head section of our layout instead of placing them at the very end of the body section, so the AngularJS core will load as quickly as possible. We also have to add this Bundle in the dependencies of the main asset bundle application and in the same place connect another 'js/app.js' file, which will contain an instance of the AngularJS application.

 <?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' ]; } 

Let's add the file controllers.js to the same bundle at the same time. In the future, we will write in it our front-end controllers, directives.js and services.js to describe the directives and services, respectively.

We start implementing Angular into the application views by adding the ng-app directive to the html element - this will launch our AngularJS application, which we will call “app” .

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

Please note that I wrote the data-ng-app directive exactly this way; otherwise, our html markup will not pass W3C validation. This does not have any effect on the work of the directives, and the layout should be validated in accordance with the project specification. Therefore, for all non-standard directives we will add the data- prefix.

The code for the Angular application will be located in frontend/web/js/app.js There we will define the application and connect to it the modules that we are going to use.

 'use strict'; var app = angular.module('app', [ 'ngAnimate', 'ui.bootstrap', 'controllers' //  frontend/web/js/controllers.js ]); 

In the frontend/web/js/services.js LeaseService , we will write the LeaseService service, which will perform CRUD operations for listings through communication with the REST API of the backend:

 '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); }; }); 

Today, our controllers module will contain a very simple LeaseController controller. It will request data from /api/leases and pass the data to the display.

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

We use this controller in the \modules\lease\views\frontend\default\index.php file using the ng-controller directive and output the data provided by the API to the table:

 <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> 

Now, when we browse the page along the path /lease/default/index , we will see a list of all listings recorded in the database.


I hope you can see how quickly and easily you can create a RESTful API based on the Yii2 framework. All the code described in this and previous articles is available for you in the repository .

Material prepared: greebn9k (Sergey Gribnyak), pavel-berezhnoy (Pavel Berezhnoy)

Source: https://habr.com/ru/post/318242/


All Articles