Simple Restful Implementation for Yii
Introduction
I decided on copper to write the google chrome extension for the project of the company in which I work. The extension and the project itself is dedicated to auto topics. In the process of writing, the server side annoyed me, which gave the extension ready-made data layout and styles, but I wanted json. An article about restful caught my eye and decided to write the server side on restful architecture.
Our project is implemented on native php without using any frameworks. I wrote a simple class that implements the necessary fennies, but could not stop there, since I am a fan of yii decided to implement this for him as well. I looked at third-party developments, but I wanted my own, moreover, the class I wrote normally coped with the tasks assigned to it. My adapted class for yii was shown to colleagues, saying yes and how they liked it. But then I suffered, I wanted it even easier.
so
In yii there is such a wonderful opportunity “ Binding action parameters ” as the manual says - you can set named parameters that will automatically get the corresponding value from $ _GET.
class UpdateAction extends CAction
{
public function run($id)
{
// $id будет заполнен значением из $_GET['id']
}
}
Here the idea visited the opportunity not only to receive from GET but also POST, PUT, DELETE according to restful. As a result, I had to redefine the CController class with its runAction method and add the missing one.
GetActionParams is responsible for getting the GET; it is simple to the ugliness.
/**
* Returns the request parameters that will be used for action parameter binding.
* By default, this method will return $_GET. You may override this method if you
* want to use other request parameters (e.g. $_GET+$_POST).
* @return array the request parameters to be used for action parameter binding
* @since 1.1.7
*/
public function getActionParams()
{
return $_GET;
}
By adding your methods to the overridden class ORestController.
(I used the latest version 1.1.9 and get the data from $ _POST I didn’t succeed as I didn’t try, I had to take everything from the stream)
//возвращает POST данные
public function getActionParamsPOST()
{
//Получаем данные
$fh = fopen("php://input", 'r');
$post_string=stream_get_contents($fh);
$post_param = explode("&", $post_string);
$array_put=array();
foreach($post_param as $post_val)
{
$param = explode("=", $post_val);
$array_post[$param[0]]=urldecode($param[1]);
}
return $array_post;
}
//возвращает DELETE данные
public function getActionParamsDELETE()
{
//получаем данные
$fh = fopen("php://input", 'r');
$delete_string=stream_get_contents($fh);
$delete_param = explode("&", $delete_string);
$array_delete=array();
foreach($delete_param as $delete_val)
{
$param = explode("=", $delete_val);
$array_delete[$param[0]]=urldecode($param[1]);
}
if($_GET)
$_delete=$_GET;
else
$_delete=$array_delete;
return $_delete;
}
//возвращает PUT данные
public function getActionParamsPUT()
{
//Получаем данные из PUT
$fh = fopen("php://input", 'r');
$put_string=stream_get_contents($fh);
$put_param = explode("&", $put_string);
$array_put=array();
foreach($put_param as $put_val)
{
$param = explode("=", $put_val);
$array_put[$param[0]]=urldecode($param[1]);
}
return $array_put;
}
And slightly changing the original method
/**
* Runs the action after passing through all filters.
* This method is invoked by {@link runActionWithFilters} after all possible filters have been executed
* and the action starts to run.
* @param CAction $action action to run
*
* Переопределяем runAction для получения PUT,DELETE,POST
*/
public function runAction($action)
{
$priorAction=$this->_action;
$this->_action=$action;
$params=false;
if($this->beforeAction($action))
{
switch ($_SERVER['REQUEST_METHOD'])
{
case "POST":
$params= $this->getActionParamsPOST();
break;
case "PUT":
$params= $this->getActionParamsPUT();
break;
case "DELETE":
$params= $this->getActionParamsDELETE();
break;
default:
$params= $this->getActionParams();
}
if($action->runWithParams($params)===false)
$this->invalidActionParams($action);
else
$this->afterAction($action);
}
$this->_action=$priorAction;
}
Well, that's all in principle, we inherit our new controller and work.
Example:
class ApiController extends ORestController
{
public function actions()
{
return array
(
'test' => 'application.controllers.actionsApi.actionTest',
);
}
}
class actionTest extends CAction
{
public function run($params='')
{
switch ($_SERVER['REQUEST_METHOD'])
{
case "POST":
echo "POST ".$params;
break;
case "PUT":
echo "PUT ".$params;
break;
case "DELETE":
echo "DELETE ".$params;
break;
default:
echo "GET ".$params;
}
}
}
It remains to implement the necessary functionality for each action, each action works with a separate entity as described in restful. No need to set the URL rules therefore does not affect the performance of the application.
PS: The methods getActionParamsPOST, getActionParamsPUT, getActionParamsDELETE can be reduced to one method, thereby reducing another code.