Difference between #11 and #19 of
How-To: Create a REST API

Changes

Title unchanged

How-To: Create a REST API

Category unchanged

How-tos

Yii version unchanged

Tags changed

REST, API, Tutorial, Backbone

Content changed

This article will explain how to create a REST API with the Yii framework. ## Information about REST A good introduction about implementing REST service with PHP can be found on [http://www.gen-x-design.com](http://www.gen-x-design.com/archives/create-a-rest-api-with-php/).
 
 
## Usefull Tools
 
 
To fire up a REST request to a Yii application, you can use the [Firefox REST Client Addon](https://github.com/chao/RESTClient).
 
 
If you want to send REST requests via a console, perhaps you might check [cUrl](http://curl.haxx.se/).
 
 
 
## Requirements
 
 
We will create an API that allows us to
 
 
* Get all items of a certain model
 
* Get one single model item via its primary key (id)
 
* Create a new item
 
* Update an existing item
 
* Delete an existing item.
 
 
In this tutorial, we will use the Yii Blog demo application. Speaking of a model here means the Post model, i.e. creating and reading post items
 
 
The API shall be flexible enough that it can be extended easily to work on more different models, e.g. comments or user data.
 
 
All requests to the API shall use an authorization.
 
 
All right, let's get things going!
 
 
 
## Setting up the URL Manager
 
 
When using the API, we would like to have the following URL scheme:
 
 
* **View all posts:** _index.php/api/posts_ (HTTP method _GET_)
 
* **View a single posts:** _index.php/api/posts/123_ (also _GET_ )
 
* **Create a new post:** _index.php/api/posts_ (_POST_)
 
* **Update a post:** _index.php/api/posts/123_ (_PUT_)
 
* **Delete a post:** _index.php/api/posts_ (_DELETE_)
 
 
 
In order to parse these URL's, set up the URL manager in _config/main.php_ like this:
 
 
 
```php 
...
 
        'urlManager'=>array(
 
         'urlFormat'=>'path',
 
         'rules'=>array(
 
                        'post/<id:\d+>/<title:.*?>'=>'post/view',
 
                        'posts/<tag:.*?>'=>'post/index',
 
                        // REST patterns
 
                        array('api/list', 'pattern'=>'api/<model:\w+>', 'verb'=>'GET'),
 
                        array('api/view', 'pattern'=>'api/<model:\w+>/<id:\d+>', 'verb'=>'GET'),
 
                        array('api/update', 'pattern'=>'api/<model:\w+>/<id:\d+>', 'verb'=>'PUT'),  // Update
 
                        array('api/delete', 'pattern'=>'api/<model:\w+>/<id:\d+>', 'verb'=>'DELETE'),
 
                        array('api/create', 'pattern'=>'api/<model:\w+>', 'verb'=>'POST'), // Create
 
                        // Other controllers
 
                        '<controller:\w+>/<action:\w+>'=>'<controller>/<action>',
 
         ),
 
        ),
 
        ...
 
```
 
 
Note that for _all_ requests, we will get the requested _model_ (e.g. _posts_) via the _GET model_ parameter.
 
 
For the _Get Single Item_ and _Update Item_ method, we will receive the model's primary key via the _GET id_ parameter.
 
 
 
## Create an API controller
 
 
In this tutorial, we will implement all REST methods in a new controller. Put this file in the _controllers_ directory:
 
 
 
```php 
class
 
class ApiController extends Controller
 
{
 
    // Members
 
    /**
 
     * Key which has to be in HTTP USERNAME and PASSWORD headers 
 
     */
 
    Const APPLICATION_ID = 'ASCCPE';
 
 
    /**
 
     * Default response format
 
     * either 'json' or 'xml'
 
     */
 
    private $format = 'json';
 
    /**
 
     * @return array action filters
 
     */
 
    public function filters()
 
    {
 
            return array();
 
    }
 
    // Actions
 
 
    public function actionList()
 
    {
 
    }
 
 
    public function actionView()
 
    {
 
    }
 
 
    public function actionCreate()
 
    {
 
    }
 
 
    public function actionUpdate()
 
    {
 
    }
 
 
    public function actionDelete()
 
    {
 
    }
 
}
 
```
 
 
## Implementing the Actions
 
 
### Get all Models List Action
 
 
 
```php 
public function actionList()
 
    {
 
        // Get the respective model instance
 
        switch($_GET['model'])
 
        {
 
            case 'posts':
 
                $models = Post::model()->findAll();
 
                break;
 
            default:
 
                // Model not implemented error
 
                $this->_sendResponse(501, sprintf('Error: Mode <b>list</b> is not implemented for model <b>%s</b>',$_GET['model']) );
 
                exit;
 
        }
 
        // Did we get some results?
 
        if(is_null($models)) {
 
            // No
 
            $this->_sendResponse(200, sprintf('No items where found for model <b>%s</b>', $_GET['model']) );
 
        } else {
 
            // Prepare response
 
            $rows = array();
 
            foreach($models as $model)
 
                $rows[] = $model->attributes;
 
            // Send the response
 
            $this->_sendResponse(200, CJSON::encode($rows));
 
        }
 
    } // }}} 
 
```
 
 
 
### Get a Single Model Action
 
 
 
```php 
public function actionView()
 
    {
 
        // Check if id was submitted via GET
 
        if(!isset($_GET['id']))
 
            $this->_sendResponse(500, 'Error: Parameter <b>id</b> is missing' );
 
 
        switch($_GET['model'])
 
        {
 
            // Find respective model    
 
            case 'posts':
 
                $model = Post::model()->findByPk($_GET['id']);
 
                break;
 
            default:
 
                $this->_sendResponse(501, sprintf('Mode <b>view</b> is not implemented for model <b>%s</b>',$_GET['model']) );
 
                exit; // }}} 
 
        }
 
        // Did we find the requested model?
 
        if(is_null($model)) {
 
            // No, raise an error 
 
            $this->_sendResponse(404, 'No Item found with id '.$_GET['id']);
 
        } else {
 
            
 
            $this->_sendResponse(200, CJSON::encode($_GET['model']));
 
        }
 
    } // }}} 
 
```
 
 
 
### Create a new Model Action
 
 
 
```php 
public function actionCreate()
 
    {
 
        switch($_GET['model'])
 
        {
 
            // Get an instance of the respective model
 
            case 'posts':
 
                $model = new Post;                    
 
                break;
 
            default:
 
                $this->_sendResponse(501, sprintf('Mode <b>create</b> is not implemented for model <b>%s</b>',$_GET['model']) );
 
                exit;
 
        }
 
        // Try to assign POST values to attributes
 
        foreach($_POST as $var=>$value) {
 
            // Does the model have this attribute?
 
            if($model->hasAttribute($var)) {
 
                $model->$var = $value;
 
            } else {
 
                // No, raise an error
 
                $this->_sendResponse(500, sprintf('Parameter <b>%s</b> is not allowed for model <b>%s</b>', $var, $_GET['model']) );
 
            }
 
        }
 
        // Try to save the model
 
        if($model->save()) {
 
            // Saving was OK
 
            $this->_sendResponse(200, $this->_getObjectEncoded($_GET['model'], $model->attributes) );
 
        } else {
 
            // Errors occurred
 
            $msg = "<h1>Error</h1>";
 
            $msg .= sprintf("Couldn't create model <b>%s</b>", $_GET['model']);
 
            $msg .= "<ul>";
 
            foreach($model->errors as $attribute=>$attr_errors) {
 
                $msg .= "<li>Attribute: $attribute</li>";
 
                $msg .= "<ul>";
 
                foreach($attr_errors as $attr_error) {
 
                    $msg .= "<li>$attr_error</li>";
 
                }        
 
                $msg .= "</ul>";
 
            }
 
            $msg .= "</ul>";
 
            $this->_sendResponse(500, $msg );
 
        }
 
    } // }}}
 
```
 
 
 
### Update a Model Action
 
 
 
```php 
public function actionUpdate()
 
    {
 
 
        // Parse the PUT parameters
 
        parse_str(file_get_contents('php://input'), $put_vars);
 
 
        switch($_GET['model'])
 
        {
 
            // Find respective model
 
            case 'posts':
 
                $model = Post::model()->findByPk($_GET['id']);                    
 
                break;
 
            default:
 
                $this->_sendResponse(501, sprintf('Error: Mode <b>update</b> is not implemented for model <b>%s</b>',$_GET['model']) );
 
                exit;
 
        }
 
        // Did we find the requested model?
 
        if(is_null($model)) {
 
            // No, raise an error
 
            $this->_sendResponse(400, sprintf("Error: Didn't find any model <b>%s</b> with ID <b>%s</b>.",$_GET['model'], $_GET['id']) );
 
        }
 
        
 
        // Try to assign PUT parameters to attributes
 
        foreach($put_vars as $var=>$value) {
 
            // Does model have this attribute?
 
            if($model->hasAttribute($var)) {
 
                $model->$var = $value;
 
            } else {
 
                // No, raise error
 
                $this->_sendResponse(500, sprintf('Parameter <b>%s</b> is not allowed for model <b>%s</b>', $var, $_GET['model']) );
 
            }
 
        }
 
        // Try to save the model
 
        if($model->save()) {
 
            $this->_sendResponse(200, sprintf('The model <b>%s</b> with id <b>%s</b> has been updated.', $_GET['model'], $_GET['id']) );
 
        } else {
 
            // prepare the error $msg
 
            // see actionCreate
 
            // ...
 
            $this->_sendResponse(500, $msg );
 
        }
 
    }
 
```
 
 
 
### Delete a Model Action
 
 
 
```php 
public function actionDelete()
 
    {
 
        switch($_GET['model'])
 
        {
 
            // Load the respective model
 
            case 'posts':
 
                $model = Post::model()->findByPk($_GET['id']);                    
 
                break;
 
            default:
 
                $this->_sendResponse(501, sprintf('Error: Mode <b>delete</b> is not implemented for model <b>%s</b>',$_GET['model']) );
 
                exit;
 
        }
 
        // Was a model found?
 
        if(is_null($model)) {
 
            // No, raise an error
 
            $this->_sendResponse(400, sprintf("Error: Didn't find any model <b>%s</b> with ID <b>%s</b>.",$_GET['model'], $_GET['id']) );
 
        }
 
 
        // Delete the model
 
        $num = $model->delete();
 
        if($num>0)
 
            $this->_sendResponse(200, sprintf("Model <b>%s</b> with ID <b>%s</b> has been deleted.",$_GET['model'], $_GET['id']) );
 
        else
 
            $this->_sendResponse(500, sprintf("Error: Couldn't delete model <b>%s</b> with ID <b>%s</b>.",$_GET['model'], $_GET['id']) );
 
    }
 
```
 
 
 
## Additional Methods Needed
 
 
### Sending the Response
 
 
How are the API responses actually sent? Right, we need to implement the _sendResponse method.
 
 
This code is borrowed from [http://www.gen-x-design.com/archives/create-a-rest-api-with-php](http://www.gen-x-design.com/archives/create-a-rest-api-with-php).
 
 
 
```php 
private function _sendResponse($status = 200, $body = '', $content_type = 'text/html')
 
    {
 
        $status_header = 'HTTP/1.1 ' . $status . ' ' . $this->_getStatusCodeMessage($status);
 
        // set the status
 
        header($status_header);
 
        // set the content type
 
        header('Content-type: ' . $content_type);
 
 
        // pages with body are easy
 
        if($body != '')
 
        {
 
            // send the body
 
            echo $body;
 
            exit;
 
        }
 
        // we need to create the body if none is passed
 
        else
 
        {
 
            // create some body messages
 
            $message = '';
 
 
            // this is purely optional, but makes the pages a little nicer to read
 
            // for your users.  Since you won't likely send a lot of different status codes,
 
            // this also shouldn't be too ponderous to maintain
 
            switch($status)
 
            {
 
                case 401:
 
                    $message = 'You must be authorized to view this page.';
 
                    break;
 
                case 404:
 
                    $message = 'The requested URL ' . $_SERVER['REQUEST_URI'] . ' was not found.';
 
                    break;
 
                case 500:
 
                    $message = 'The server encountered an error processing your request.';
 
                    break;
 
                case 501:
 
                    $message = 'The requested method is not implemented.';
 
                    break;
 
            }
 
 
            // servers don't always have a signature turned on (this is an apache directive "ServerSignature On")
 
            $signature = ($_SERVER['SERVER_SIGNATURE'] == '') ? $_SERVER['SERVER_SOFTWARE'] . ' Server at ' . $_SERVER['SERVER_NAME'] . ' Port ' . $_SERVER['SERVER_PORT'] : $_SERVER['SERVER_SIGNATURE'];
 
 
            // this should be templated in a real-world solution
 
            $body = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
 
                        <html>
 
                            <head>
 
                                <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
 
                                <title>' . $status . ' ' . $this->_getStatusCodeMessage($status) . '</title>
 
                            </head>
 
                            <body>
 
                                <h1>' . $this->_getStatusCodeMessage($status) . '</h1>
 
                                <p>' . $message . '</p>
 
                                <hr />
 
                                <address>' . $signature . '</address>
 
                            </body>
 
                        </html>';
 
 
            echo $body;
 
            exit;
 
        }
 
    } // }}}            
 
```
 
 
 
### Getting the Status Codes
 
 
Also, we need to implement the _getStatusCodeMessage method. This is pretty straight forward:
 
 
 
```php 
private function _getStatusCodeMessage($status)
 
    {
 
        // these could be stored in a .ini file and loaded
 
        // via parse_ini_file()... however, this will suffice
 
        // for an example
 
        $codes = Array(
 
            200 => 'OK',
 
            400 => 'Bad Request',
 
            401 => 'Unauthorized',
 
            402 => 'Payment Required',
 
            403 => 'Forbidden',
 
            404 => 'Not Found',
 
            500 => 'Internal Server Error',
 
            501 => 'Not Implemented',
 
        );
 
        return (isset($codes[$status])) ? $codes[$status] : '';
 
    } // }}} 
 
 
```
 
 
 
### Authentication
 
 
If we want to have the API user authorize himself, we could write something like this:
 
 
 
```php 
private function _checkAuth()
 
    {
 
        // Check if we have the USERNAME and PASSWORD HTTP headers set?
 
        if(!(isset($_SERVER['HTTP_X_USERNAME']) and isset($_SERVER['HTTP_X_PASSWORD']))) {
 
            // Error: Unauthorized
 
            $this->_sendResponse(401);
 
        }
 
        $username = $_SERVER['HTTP_X_USERNAME'];
 
        $password = $_SERVER['HTTP_X_PASSWORD'];
 
        // Find the user
 
        $user=User::model()->find('LOWER(username)=?',array(strtolower($username)));
 
        if($user===null) {
 
            // Error: Unauthorized
 
            $this->_sendResponse(401, 'Error: User Name is invalid');
 
        } else if(!$user->validatePassword($password)) {
 
            // Error: Unauthorized
 
            $this->_sendResponse(401, 'Error: User Password is invalid');
 
        }
 
    } // }}} 
 
```
 
 
Also, in all REST methods where an authentication is required, we need to put
 
 
 
```php 
$this->_checkAuth();
 
```
 
 
at the beginning of each method.
 
 
The API user then needs to set the _X_USERNAME_ and _X_PASSWORD_ headers in his request.
 
 
 
## Apache Issues
 
 
If _PUT_ or _DELETE_ requests don't work in your Apache setup (perhaps you get an _403 - Forbidden_ error), you can put the following _.htaccess_ file in the application's web root:
 
 
    <Limit GET POST PUT DELETE>
 
    order deny,allow
 
    allow from all
 
    </Limit>
 
 
See also [this link](http://www.jfileupload.com/support/forums/viewtopic.php?f=2&t=361). Other thoughts about mimic PUT and DELETE can be found [here](http://stackoverflow.com/questions/2081894/handling-put-delete-arguments-in-php).
 
 
 
## Discussion
 
 
Please add your comments here or send comments to the respective [forum post](http://www.yiiframework.com/forum/index.php?/topic/18412-new-yii-rest-tutorial/).
 
 
 
## Code Download
 
 
Of course you can download the code developed [here](http://www.diggin-data.de/download/yii-blog-rest.tar.gz).
s://web.archive.org/web/20130910164802/http://www.gen-x-design.com/archives/create-a-rest-api-with-php/).
 
 
## Usefull Tools
 
 
To fire up a REST request to a Yii application, you can use the [Firefox REST Client Addon](https://github.com/chao/RESTClient).
 
 
If you want to send REST requests via a console, perhaps you might check [cUrl](http://curl.haxx.se/).
 
 
 
## Requirements
 
 
We will create an API that allows us to
 
 
* Get all items of a certain model
 
* Get one single model item via its primary key (id)
 
* Create a new item
 
* Update an existing item
 
* Delete an existing item.
 
 
In this tutorial, we will use the Yii Blog demo application. Speaking of a model here means the Post model, i.e. creating and reading post items
 
 
The API shall be flexible enough that it can be extended easily to work on more different models, e.g. comments or user data.
 
 
All requests to the API shall use an authorization.
 
 
All right, let's get things going!
 
 
 
## Setting up the URL Manager
 
 
When using the API, we would like to have the following URL scheme:
 
 
* **View all posts:** _index.php/api/posts_ (HTTP method _GET_)
 
* **View a single posts:** _index.php/api/posts/123_ (also _GET_ )
 
* **Create a new post:** _index.php/api/posts_ (_POST_)
 
* **Update a post:** _index.php/api/posts/123_ (_PUT_)
 
* **Delete a post:** _index.php/api/posts/123_ (_DELETE_)
 
 
 
In order to parse these URL's, set up the URL manager in _config/main.php_ like this:
 
 
 
```php 
...
 
'urlManager'=>array(
 
    'urlFormat'=>'path',
 
    'rules'=>array(
 
        'post/<id:\d+>/<title:.*?>'=>'post/view',
 
        'posts/<tag:.*?>'=>'post/index',
 
        // REST patterns
 
        array('api/list', 'pattern'=>'api/<model:\w+>', 'verb'=>'GET'),
 
        array('api/view', 'pattern'=>'api/<model:\w+>/<id:\d+>', 'verb'=>'GET'),
 
        array('api/update', 'pattern'=>'api/<model:\w+>/<id:\d+>', 'verb'=>'PUT'),
 
        array('api/delete', 'pattern'=>'api/<model:\w+>/<id:\d+>', 'verb'=>'DELETE'),
 
        array('api/create', 'pattern'=>'api/<model:\w+>', 'verb'=>'POST'),
 
        // Other controllers
 
        '<controller:\w+>/<action:\w+>'=>'<controller>/<action>',
 
    ),
 
),
 
...
 
```
 
 
Note that for _all_ requests, we will get the requested _model_ (e.g. _posts_) via the _GET model_ parameter.
 
 
For the _Get Single Item_ and _Update Item_ method, we will receive the model's primary key via the _GET id_ parameter.
 
 
 
## Create an API controller
 
 
In this tutorial, we will implement all REST methods in a new controller. Put this file in the _controllers_ directory:
 
 
 
```php 
class ApiController extends Controller
 
{
 
    // Members
 
    /**
 
     * Key which has to be in HTTP USERNAME and PASSWORD headers 
 
     */
 
    Const APPLICATION_ID = 'ASCCPE';
 
 
    /**
 
     * Default response format
 
     * either 'json' or 'xml'
 
     */
 
    private $format = 'json';
 
    /**
 
     * @return array action filters
 
     */
 
    public function filters()
 
    {
 
            return array();
 
    }
 
 
    // Actions
 
    public function actionList()
 
    {
 
    }
 
    public function actionView()
 
    {
 
    }
 
    public function actionCreate()
 
    {
 
    }
 
    public function actionUpdate()
 
    {
 
    }
 
    public function actionDelete()
 
    {
 
    }
 
}
 
```
 
 
## Implementing the Actions
 
 
### Get all Models List Action
 
 
 
```php 
public function actionList()
 
{
 
    // Get the respective model instance
 
    switch($_GET['model'])
 
    {
 
        case 'posts':
 
            $models = Post::model()->findAll();
 
            break;
 
        default:
 
            // Model not implemented error
 
            $this->_sendResponse(501, sprintf(
 
                'Error: Mode <b>list</b> is not implemented for model <b>%s</b>',
 
                $_GET['model']) );
 
            Yii::app()->end();
 
    }
 
    // Did we get some results?
 
    if(empty($models)) {
 
        // No
 
        $this->_sendResponse(200, 
 
                sprintf('No items where found for model <b>%s</b>', $_GET['model']) );
 
    } else {
 
        // Prepare response
 
        $rows = array();
 
        foreach($models as $model)
 
            $rows[] = $model->attributes;
 
        // Send the response
 
        $this->_sendResponse(200, CJSON::encode($rows));
 
    }
 
}
 
```
 
 
 
### Get a Single Model Action
 
 
 
```php 
public function actionView()
 
{
 
    // Check if id was submitted via GET
 
    if(!isset($_GET['id']))
 
        $this->_sendResponse(500, 'Error: Parameter <b>id</b> is missing' );
 
 
    switch($_GET['model'])
 
    {
 
        // Find respective model    
 
        case 'posts':
 
            $model = Post::model()->findByPk($_GET['id']);
 
            break;
 
        default:
 
            $this->_sendResponse(501, sprintf(
 
                'Mode <b>view</b> is not implemented for model <b>%s</b>',
 
                $_GET['model']) );
 
            Yii::app()->end();
 
    }
 
    // Did we find the requested model? If not, raise an error
 
    if(is_null($model))
 
        $this->_sendResponse(404, 'No Item found with id '.$_GET['id']);
 
    else
 
        $this->_sendResponse(200, CJSON::encode($model));
 
}
 
```
 
 
 
### Create a new Model Action
 
 
 
```php 
public function actionCreate()
 
{
 
    switch($_GET['model'])
 
    {
 
        // Get an instance of the respective model
 
        case 'posts':
 
            $model = new Post;                    
 
            break;
 
        default:
 
            $this->_sendResponse(501, 
 
                sprintf('Mode <b>create</b> is not implemented for model <b>%s</b>',
 
                $_GET['model']) );
 
                Yii::app()->end();
 
    }
 
    // Try to assign POST values to attributes
 
    foreach($_POST as $var=>$value) {
 
        // Does the model have this attribute? If not raise an error
 
        if($model->hasAttribute($var))
 
            $model->$var = $value;
 
        else
 
            $this->_sendResponse(500, 
 
                sprintf('Parameter <b>%s</b> is not allowed for model <b>%s</b>', $var,
 
                $_GET['model']) );
 
    }
 
    // Try to save the model
 
    if($model->save())
 
        $this->_sendResponse(200, CJSON::encode($model));
 
    else {
 
        // Errors occurred
 
        $msg = "<h1>Error</h1>";
 
        $msg .= sprintf("Couldn't create model <b>%s</b>", $_GET['model']);
 
        $msg .= "<ul>";
 
        foreach($model->errors as $attribute=>$attr_errors) {
 
            $msg .= "<li>Attribute: $attribute</li>";
 
            $msg .= "<ul>";
 
            foreach($attr_errors as $attr_error)
 
                $msg .= "<li>$attr_error</li>";
 
            $msg .= "</ul>";
 
        }
 
        $msg .= "</ul>";
 
        $this->_sendResponse(500, $msg );
 
    }
 
}
 
```
 
 
 
### Update a Model Action
 
 
 
```php 
public function actionUpdate()
 
{
 
    // Parse the PUT parameters. This didn't work: parse_str(file_get_contents('php://input'), $put_vars);
 
    $json = file_get_contents('php://input'); //$GLOBALS['HTTP_RAW_POST_DATA'] is not preferred: http://www.php.net/manual/en/ini.core.php#ini.always-populate-raw-post-data
 
    $put_vars = CJSON::decode($json,true); //true means use associative array
 
 
    switch($_GET['model'])
 
    {
 
        // Find respective model
 
        case 'posts':
 
            $model = Post::model()->findByPk($_GET['id']);                    
 
            break;
 
        default:
 
            $this->_sendResponse(501, 
 
                sprintf( 'Error: Mode <b>update</b> is not implemented for model <b>%s</b>',
 
                $_GET['model']) );
 
            Yii::app()->end();
 
    }
 
    // Did we find the requested model? If not, raise an error
 
    if($model === null)
 
        $this->_sendResponse(400, 
 
                sprintf("Error: Didn't find any model <b>%s</b> with ID <b>%s</b>.",
 
                $_GET['model'], $_GET['id']) );
 
        
 
    // Try to assign PUT parameters to attributes
 
    foreach($put_vars as $var=>$value) {
 
        // Does model have this attribute? If not, raise an error
 
        if($model->hasAttribute($var))
 
            $model->$var = $value;
 
        else {
 
            $this->_sendResponse(500, 
 
                sprintf('Parameter <b>%s</b> is not allowed for model <b>%s</b>',
 
                $var, $_GET['model']) );
 
        }
 
    }
 
    // Try to save the model
 
    if($model->save())
 
        $this->_sendResponse(200, CJSON::encode($model));
 
    else
 
        // prepare the error $msg
 
        // see actionCreate
 
        // ...
 
        $this->_sendResponse(500, $msg );
 
}
 
```
 
 
Please keep in mind to check your model `beforeSave` and `afterSave` methods if any code eventually uses a logged-in user's id like the blog `Post` model:
 
 
 
```php 
protected function beforeSave()
 
{
 
    ...
 
    // author_id may have been posted via API POST
 
    if(is_null($this->author_id) or $this->author_id=='')
 
        $this->author_id=Yii::app()->user->id;
 
    ...
 
}
 
```
 
 
### Delete a Model Action
 
 
 
```php 
public function actionDelete()
 
{
 
    switch($_GET['model'])
 
    {
 
        // Load the respective model
 
        case 'posts':
 
            $model = Post::model()->findByPk($_GET['id']);                    
 
            break;
 
        default:
 
            $this->_sendResponse(501, 
 
                sprintf('Error: Mode <b>delete</b> is not implemented for model <b>%s</b>',
 
                $_GET['model']) );
 
            Yii::app()->end();
 
    }
 
    // Was a model found? If not, raise an error
 
    if($model === null)
 
        $this->_sendResponse(400, 
 
                sprintf("Error: Didn't find any model <b>%s</b> with ID <b>%s</b>.",
 
                $_GET['model'], $_GET['id']) );
 
 
    // Delete the model
 
    $num = $model->delete();
 
    if($num>0)
 
        $this->_sendResponse(200, $num); //this is the only way to work with backbone
 
    else
 
        $this->_sendResponse(500, 
 
                sprintf("Error: Couldn't delete model <b>%s</b> with ID <b>%s</b>.",
 
                $_GET['model'], $_GET['id']) );
 
}
 
```
 
 
 
## Additional Methods Needed
 
 
### Sending the Response
 
 
How are the API responses actually sent? Right, we need to implement the _sendResponse method.
 
 
This code is borrowed from [http://www.gen-x-design.com/archives/create-a-rest-api-with-php](https://web.archive.org/web/20130910164802/http://www.gen-x-design.com/archives/create-a-rest-api-with-php/).
 
 
 
```php 
private function _sendResponse($status = 200, $body = '', $content_type = 'text/html')
 
{
 
    // set the status
 
    $status_header = 'HTTP/1.1 ' . $status . ' ' . $this->_getStatusCodeMessage($status);
 
    header($status_header);
 
    // and the content type
 
    header('Content-type: ' . $content_type);
 
 
    // pages with body are easy
 
    if($body != '')
 
    {
 
        // send the body
 
        echo $body;
 
    }
 
    // we need to create the body if none is passed
 
    else
 
    {
 
        // create some body messages
 
        $message = '';
 
 
        // this is purely optional, but makes the pages a little nicer to read
 
        // for your users.  Since you won't likely send a lot of different status codes,
 
        // this also shouldn't be too ponderous to maintain
 
        switch($status)
 
        {
 
            case 401:
 
                $message = 'You must be authorized to view this page.';
 
                break;
 
            case 404:
 
                $message = 'The requested URL ' . $_SERVER['REQUEST_URI'] . ' was not found.';
 
                break;
 
            case 500:
 
                $message = 'The server encountered an error processing your request.';
 
                break;
 
            case 501:
 
                $message = 'The requested method is not implemented.';
 
                break;
 
        }
 
 
        // servers don't always have a signature turned on 
 
        // (this is an apache directive "ServerSignature On")
 
        $signature = ($_SERVER['SERVER_SIGNATURE'] == '') ? $_SERVER['SERVER_SOFTWARE'] . ' Server at ' . $_SERVER['SERVER_NAME'] . ' Port ' . $_SERVER['SERVER_PORT'] : $_SERVER['SERVER_SIGNATURE'];
 
 
        // this should be templated in a real-world solution
 
        $body = '
 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
 
<html>
 
<head>
 
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
 
    <title>' . $status . ' ' . $this->_getStatusCodeMessage($status) . '</title>
 
</head>
 
<body>
 
    <h1>' . $this->_getStatusCodeMessage($status) . '</h1>
 
    <p>' . $message . '</p>
 
    <hr />
 
    <address>' . $signature . '</address>
 
</body>
 
</html>';
 
 
        echo $body;
 
    }
 
    Yii::app()->end();
 
}
 
```
 
 
 
### Getting the Status Codes
 
 
Also, we need to implement the _getStatusCodeMessage method. This is pretty straight forward:
 
 
 
```php 
private function _getStatusCodeMessage($status)
 
{
 
    // these could be stored in a .ini file and loaded
 
    // via parse_ini_file()... however, this will suffice
 
    // for an example
 
    $codes = Array(
 
        200 => 'OK',
 
        400 => 'Bad Request',
 
        401 => 'Unauthorized',
 
        402 => 'Payment Required',
 
        403 => 'Forbidden',
 
        404 => 'Not Found',
 
        500 => 'Internal Server Error',
 
        501 => 'Not Implemented',
 
    );
 
    return (isset($codes[$status])) ? $codes[$status] : '';
 
}
 
```
 
 
 
### Authentication
 
 
If we want to have the API user authorize himself, we could write something like this:
 
 
 
```php 
private function _checkAuth()
 
{
 
    // Check if we have the USERNAME and PASSWORD HTTP headers set?
 
    if(!(isset($_SERVER['HTTP_X_USERNAME']) and isset($_SERVER['HTTP_X_PASSWORD']))) {
 
        // Error: Unauthorized
 
        $this->_sendResponse(401);
 
    }
 
    $username = $_SERVER['HTTP_X_USERNAME'];
 
    $password = $_SERVER['HTTP_X_PASSWORD'];
 
    // Find the user
 
    $user=User::model()->find('LOWER(username)=?',array(strtolower($username)));
 
    if($user===null) {
 
        // Error: Unauthorized
 
        $this->_sendResponse(401, 'Error: User Name is invalid');
 
    } else if(!$user->validatePassword($password)) {
 
        // Error: Unauthorized
 
        $this->_sendResponse(401, 'Error: User Password is invalid');
 
    }
 
}
 
```
 
 
Also, in all REST methods where an authentication is required, we need to put
 
 
 
```php 
$this->_checkAuth();
 
```
 
 
at the beginning of each method.
 
 
The API user then needs to set the _X_USERNAME_ and _X_PASSWORD_ headers in his request.
 
 
 
## Apache Issues
 
 
If _PUT_ or _DELETE_ requests don't work in your Apache setup (perhaps you get an _403 - Forbidden_ error), you can put the following _.htaccess_ file in the application's web root:
 
 
    <Limit GET POST PUT DELETE>
 
    order deny,allow
 
    allow from all
 
    </Limit>
 
 
See also [this link](http://www.jfileupload.com/support/forums/viewtopic.php?f=2&t=361). Other thoughts about mimic PUT and DELETE can be found [here](http://stackoverflow.com/questions/2081894/handling-put-delete-arguments-in-php).
 
 
 
## Discussion
 
 
Please add your comments here or send comments to the respective [forum post](http://www.yiiframework.com/forum/index.php?/topic/18412-new-yii-rest-tutorial/).
 
 
 
## Code Download
 
 
Of course you can download the code developed [here](http://www.diggin-data.de/download/yii-blog-rest.tar.gz).
 
 
 
Links
 
-----
 
- [Chinese version](http://www.itkuaixun.com/bbs/thread-217-1-1.html "Chinese version")
98 2
123 followers
Viewed: 506 661 times
Version: 1.1
Category: How-tos
Written by: jwerner
Last updated by: Rohit Suthar
Created on: Apr 15, 2011
Last updated: 10 years ago
Update Article

Revisions

View all history