Case Studies

How to configure versioning for Yii2 [Code Samples]

19 Nov 2015
2586
4 min

There is often a need to implement versioning of API functions for web and mobile applications. This need becomes a necessity to provide more flexible product support aimed to avoid system crash. I used to hear quite often about problems with using the same API across platforms. Frequently some platforms turn out to be ahead of others: for example, when you need to rewrite the half of the functionality on Android, iOS is still using old APIs. In my case it was necessary to use versioning for Yii2. Yii2 is installed via Composer and I will not describe the process of installation as there is a large amount of official documentation regarding it.

Yii2 directory structure for API

Below you can see the directory structure for API:

root_directory – project directory

api – API directory of our app
config – includes configurations for the app
modules – modules for the app, directories v1, v2, etc. are created for each version.

!

Are you looking for Node.js framework? Read our research to learn How to Decide Between Express.js, Koa.js or Sails.js

It also contains Module.php – the main module file (entry point). We have options what name to choose and what path to set: it can be done in config/main.php.

  • api/
      - assets/
      - config/
        - bootstrap.php
        - main.php
        - params.php
        - routes.php
      - modules/ 
        - v1/
      - controllers/
        - Controller.php
          - Module.php 
        - v2/ 
      - controllers/
        - Controller.php
          - Module.php
    - runtime/
    - web/
    - index.php

Configuration and main files description for API working

We will have to set configurations for Yii2 to launch the app. Below you can see the description:

bootstrap.php

We set the alias for the needed directory paths.

<?php
Yii::setAlias('common', dirname(__DIR__));
Yii::setAlias('frontend', dirname(dirname(__DIR__)) . '/frontend');
Yii::setAlias('backend', dirname(dirname(__DIR__)) . '/backend');
Yii::setAlias('console', dirname(dirname(__DIR__)) . '/console');
Yii::setAlias('api', dirname(dirname(__DIR__)) . '/api');

params.php

We set parameters that the app needs:

<?php
return []

routes.php

This is our application routing. Here you can see two versions of get requests where:

v1, v2 – API versions; 

some – controller someController;
test – method of controller.

URL will look like test_application/api/v1/some/test or test_application/api/v1/some/test, depending on what version we need.

<?php
return array(
    'GET /v1/some/test/'                            => 'v1/some/test',
    'GET /v2/some/test/'                            => 'v2/some/test',
);

main.php

It is the main configuration file. It loads the other. Here you can see the declaration what modules to use. In main.php GII code generator and logging are declared, the rules of routing through urlManager are set. Parsing for JSON requests is allowed.

<?php
$params = array_merge(
    require(__DIR__ . '/../../common/config/params.php'),
    require(__DIR__ . '/params.php')
);

return [
    'id' => 'test_application',
    'basePath' => dirname(__DIR__) . '/..',
    'bootstrap' => [
        'log',
        'gii',
        'debug'
    ],
    'modules' => [
        'debug' => [
            'class' => 'yii\debug\Module',
        ],
        'v1' => [
            'class' => 'app\api\modules\v1\Module',
        ],
        'v2' => [
            'class' => 'app\api\modules\v2\Module',
        ],
        'gii' => 'yii\gii\Module',
    ],
    'components' => [
        'request' => [
            // Enable JSON Input:
            'parsers' => [
                'application/json' => 'yii\web\JsonParser',
            ],

            'enableCookieValidation' => true,
            'enableCsrfValidation' => true,
            'cookieValidationKey' => 'suiseiseki'
        ],
        'user' => [
            'identityClass' => 'app\common\models\User',
            'enableAutoLogin' => true,
            'enableSession' => false,
        ],
        'log' => [
            'traceLevel' => Yii_DEBUG ? 3 : 0,
            'targets' => [
                [
                    'class' => 'yii\log\FileTarget',
                    'levels' => ['error', 'warning'],
                    'logFile' => '@app/runtime/logs/error.log',
                    'maxFileSize' => 1024 * 2,
                    'maxLogFiles' => 20,
                ],
                [
                    'class' => 'yii\log\FileTarget',
                    'levels' => ['info'],
                    'categories' => ['businesses'],
                    'logFile' => '@app/runtime/logs/api_calls_business.log',
                    'maxFileSize' => 1024 * 2,
                    'maxLogFiles' => 20,
                ],
                [
                    'class' => 'yii\log\FileTarget',
                    'levels' => ['info'],
                    'categories' => ['customers'],
                    'logFile' => '@app/runtime/logs/api_calls_customer.log',
                    'maxFileSize' => 1024 * 2,
                    'maxLogFiles' => 50,
                ],
                [
                    'class' => 'yii\log\FileTarget',
                    'levels' => ['info'],
                    'categories' => ['cardspring'],
                    'logFile' => '@app/runtime/logs/cardspring.log',
                    'maxFileSize' => 1024 * 2,
                    'maxLogFiles' => 50,
                ],
            ],
        ],
        'urlManager' => [
            'enablePrettyUrl' => true,
            'enableStrictParsing' => true,
            'showScriptName' => false,
            'rules' => include('routes.php')
        ],
    ],
    'params' => $params,
    'aliases' => [
        '@api' => '/path_to_project_directory/api'
    ],
];

Description of the main files

Entry point

Entry point is the main file of our application. The server starts Entry point first, then our application is launched.

index.php

<?php
    defined('Yii_DEBUG') or define('Yii_DEBUG', true);
    defined('Yii_ENV') or define('Yii_ENV', 'dev');
    require(__DIR__ . '/../vendor/autoload.php');
    require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');

    // Use a distinct configuration for the API
    $config = require(__DIR__ . '/config/main.php');
    (new yii\web\Application($config))->run();

The main file over the module

We inherit it from the basic module, which Yii2 provides.

Module – is sub-application, where you can organise your structure.

!

Cannot decide between front-end frameworks? Read our React vs Angular Research 

$controllerNamespace – namespace for all the controllers in a module. It can also be declared in the configuration file.

Module.php

<?php
    namespace app\api\modules\v1;
    class Module extends \yii\base\Module
    {
        public $controllerNamespace = 'app\api\modules\v1\controllers';

        public function init()
        {
        parent::init();
        }
    }

Here is the main controller, all the other are inherited.

Controller.php

<?php
    namespace app\api\modules\v1\controllers;

    class Controller extends \yii\rest\Controller
    {
    }

A controller, which will contain the API functions, in this case – test:

SomeController.php

<?php
    namespace app\api\modules\v1\controllers;

    class UserController extends Controller
    {
        public function actionTest()
        {
          
        }
    }

NGINX configuration

As a server, I chose nginx in conjunction with PHP-FPM as I find it simple to set up and uses less memory than the apache2. Here is the code of nginx configuration:

Using the error_log and rewrite_log we install nginx logging in case of problems with the server.

We set the size of the request body using client_max_body_size.

We set up the UTF-8 unicode.

server {
    listen       80;
    server_name  test_application;
    root         /path_to_project_directory/;
    index index.php;
    error_log       /var/log/nginx/error_debug.log debug;
    rewrite_log on;

    charset      utf-8;
    client_max_body_size  100M;

    location /api/v2/ {
    autoindex on;
    dav_methods PUT DELETE;
    create_full_put_path on;
    dav_access group:rw all:r;
    rewrite ^/api/v2/(.*)?(.*)$  /api/index.php?$2;    
    alias /path_to_root_directory/api/;
    index index.php;

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;            
        fastcgi_pass 127.0.0.1:9000;
        #fastcgi_pass unix:/var/run/php5-fpm.sock;
        fastcgi_index index.php;
        include fastcgi.conf;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
    }    
    
    location /api/v1/ {
        autoindex on;
        dav_methods PUT DELETE;
        create_full_put_path on;
        dav_access group:rw all:r;
        rewrite ^/api/v1/(.*)?(.*)$  /api/index.php?$2;    
        alias /path_to_project_directory/api/;
        index index.php;

        location ~ \.php$ {
            fastcgi_split_path_info ^(.+\.php)(/.+)$;            
            fastcgi_pass 127.0.0.1:9000;
            #fastcgi_pass unix:/var/run/php5-fpm.sock;
            fastcgi_index index.php;
            include fastcgi.conf;
            fastcgi_param SCRIPT_FILENAME                                             
          $document_root$fastcgi_script_name;
        }
    }    
     location /api/ {
        autoindex on;
        dav_methods PUT DELETE;
        create_full_put_path on;
        dav_access group:rw all:r;
        rewrite ^/api/gii?(.*)$  /api/index.php?$2;    
        alias /path_to_project_directory/api/;
        index index.php;
        location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;            
            fastcgi_pass 127.0.0.1:9000;
            #fastcgi_pass unix:/var/run/php5-fpm.sock;
            fastcgi_index index.php;
            include fastcgi.conf;
            fastcgi_param SCRIPT_FILENAME $request_filename;
        }
    }
}

Description of the main NGINX directives we use:

rewrite – changes the url request using regular expressions

alias – the directory, from which you will take an index file

Index – the index file. You can specify more than one, then they will be checked in the listed order

location – sets the configuration depending on the request URL

fastcgi_pass – sets address of FASTCGI-server. You can set php-fpm to work through sockets or connect on port 9000. It's all set in the php-fpm configuration file

dav_methods – lets you use the PUT and DELETE methods

create_full_put_path – allows you to create files only in the existing directory

dav_access group – set the rights for newly created files and directories

Conclusion

This example should allow to set up versioning fast. I have looked through tons of information and articles, but there was no ready example of setting or intelligible description for Yii2. That’s why I decided to write an article in the hope that it will help to save time for those who want to raise the project.

Rate this article:
( ) ( ) ( ) ( ) ( )
(1625 ratings, average: 4.9 out of 5)
Back to top
As s part of our team, be ready for:
An image
Competitive Base Salary
An image
Comprehensive Benefits
An image
Great Work Environment
An image
Drug Free Workplace
Tell us more about yourself