Pull to refresh

Простая реализация Restful для Yii

Reading time4 min
Views11K
Введение

Решился я на медни написать расширение google chrome для проекта компании в которой работаю. Расширение и сам проект посвящен авто тематики. В процессе написания меня раздражала серверная часть которая отдавало расширению готовые данные разметку и стили, а хотелось json. На глаза попалась статья о restful и решил написать сервеную часть на restful архитектуре.
Наш проект реализован на нативном php без использования каких либо фреймворков. Написал простой класс реализующий нужные феньки, но на этом не смог остановится, так как я поклонник yii решил реализовать сие и для него. Сторонние разработки посмотрел, но хотелось своего, к тому же мной написанный класс нормально справлялся с возложенными на него задачами. Адаптированный мой класс для yii показал коллегам, рассказав что да как им понравилось. Но тут меня понесло, хотелось еще проще.

Итак

В yii есть такая замечательная возможность «Привязка параметров действий» как гласит руководство — можно задать именованные параметры, в которые автоматически будет попадать соответствующее значение из $_GET.

class UpdateAction extends CAction
{
    public function run($id)
    {
        // $id будет заполнен значением из $_GET['id']
    }
} 


Тут и посетила идея возможность не только получать из GET но и POST,PUT,DELETE согласно restful. В итоги пришлось переопределить класс CController с его методом runAction ну и дописать недостающее.
За получение GET отвечает getActionParams он прост до безобразия.
/**
* 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;
}

Добавив свои методы в переопределенном классе ORestController.
(Я использовал последнюю версию фремворка 1.1.9 и получит данные из $_POST мне не удалось как не пытался пришлось все из потока брать)
//возвращает 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;
    }


И немного изменив оригинальный метод
/**
     * 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;
    }


Ну вот и все в принципе, наследуем наш новый контроллер и работаем.
Пример:

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;

     }
  }
}

Остается реализовать нужный функционал для каждого действия, каждый action работает с отдельной сущностью как это описано в restful. Не нужно задавать URL правила следовательно не влияет на производительность приложения.
ЗЫ: Методы getActionParamsPOST, getActionParamsPUT, getActionParamsDELETE можно свести к одному методу тем самым сократить еще код.
Tags:
Hubs:
Total votes 10: ↑6 and ↓4+2
Comments20

Articles