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

    Введение

    Решился я на медни написать расширение 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 можно свести к одному методу тем самым сократить еще код.

    Similar posts

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 20

      +2
      Ммм… помнится мне, с определенной версии Yii поддерживает put, get, etc типы даже в UrlManager'е.
      Вот тут подробнее: www.yiiframework.com/wiki/175/how-to-create-a-rest-api/
        0
        Вот блин, не видел.
          –2
          А разве через мерное использование правил URL не приводить к падению производительности? Как гласит документация is.gd/njF4Pb
            +1
            Примечание: Использование правил URL снижает производительность приложения. Это происходит по той причине, что в процессе парсинга запрошенного URL CUrlManager пытается найти соответствие каждому правилу до тех пор, пока какое-нибудь из правил не будет применено. Чем больше правил, тем больший урон производительности. Поэтому в случае высоконагруженных приложений использование правил URL стоит минимизировать.
              0
              ну вы в любом случаи парсинг делаете, думаю не намного пострадает производительность, в любом случаи лучше переписывать CUrlManager, эта возможность появилась в 1.1.8
            0
            Жесть-то какая, вы точно маньяк если пишете заново, то что есть в фреймворке по умолчанию (изучите наконец-то что есть в urlManager)!
              0
              Какая уж это жесть, когда писал не знал что в фреймворк есть встроеная возможность. К тому же как вы заметили я используют urlManager а встроеныйв фреймворк функционал накладывает обязательное его использование.
                0
                Так это и к тому, что может сперва читать документацию, потом искать уже написанный кем-то компонент, а только потом уже писать, а?!
                  0
                  Неужели плохой подход? Доки читал но почему то на глаза не попалась выше описанная возможность.

                  То что я не углядел встроенную возможность использование restful это я моя вина. Но хотелось бы услышать недостатки или почему это плохо.

                  ЗЫ: немного сарказма — в yii реализовано несколько способов обращение к бд, но использование activerecord по большей мере использовать не вариант, так как сказывается на производительности. Почему же не попробовать альтернативные возможности, разве поиск простого и понятного не тренд прогера.
                    0
                    в yii реализовано несколько способов обращение к бд, но использование activerecord по большей мере использовать не вариант, так как сказывается на производительности.

                    Я так понимаю разницу между жадной и ленивой загрузкой вы там не читали да? :)
                      0
                      Ну почему же читал. Но пользуемся преимущественно DAO проще для понимания сложные запросы да и шустрей :).
                        0
                        Тогда не понятно почему у вас что-то сказывается на производительности.
                          0
                          Так показала практика, изначально в одном проекте использовался activerecord, переписали на DAO все стало намного шустрей работать, больше половины это сложные запросы.
              +1
              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;
                  }

              А если в POST будет не form-urlencoded, а multipart/form-data, что тогда?
                0
                php://input не доступен с типом содержимого enctype=«multipart/form-data».
                  0
                  Сейчас решаю данную проблему.
                  0
                  put параметры в request были. А так — неплохо.
                    0
                    Откуда в request был put там насколько помню $_POST, $GET,$_COOKIE.
                      0
                      Yii::app()->request->put :)
                        0
                        От души спасибо, плохо прочел доки. Но реализация такая же :)

                  Only users with full accounts can post comments. Log in, please.