Zend Framework. Использование логики Webserive.

Я строю приложение, используя Zend Framework.

Вопрос в том, как использовать ту же логику контроллера Zend_Rest_Controller в приложении, не являющемся REST.

Например, давайте предположим, что твиттер был написан с Zend Framework. Вероятно, они использовали бы Zend_Rest_controller и Route для своего API. Однако что они будут использовать для своего сайта (который, очевидно, использует ту же логику API)? Будут ли они писать новое приложение, которое просто запускает запрос REST? Разве это не перегрузка?

[РЕДАКТИРОВАТЬ]
Если веб-приложение вызывает API через некоторый класс http_client для получения данных, это делает другой запрос к серверу (это приводит к снижению производительности и замедлению ответа). Я не хочу делать еще один запрос и хочу использовать ту же бизнес-логику, что и в API.

Спасибо,
Venu

2 ответа

Решение

Новый ответ:

Я придумал шаблон, который, кажется, работает хорошо. Это решает все ваши проблемы: Вот уменьшенная версия того, что я придумал:

Сначала нам нужен наш собственный контроллер. Этот контролер будет иметь службу, в которой он передает любой запрос на действие в службу, если они не определены:

abstract class App_Rest_Controller extends Zend_Controller_Action
{
    /** 
     * @var App_Rest_Service_Abstract
     */
    protected $_service;

    public function __call($methodName, $args)
    {
        if ('Action' == substr($methodName, -6)) {
            $action = substr($methodName, 0, strlen($methodName) - 6);
            return $this->_service()->$action();
        }

        return parent::__call($methodName, $args);
    }
}

Теперь пришло время для обслуживания. Расширяем Action Helper Abstract так, чтобы:

  1. у нас есть прямой доступ к объекту запроса
  2. мы можем легко вызвать сервис с любого контроллера

Это будет действовать как промежуточный пункт для приложения и фактического хранения данных.

abstract class App_Rest_Service_Abstract extends Zend_Controller_Action_Helper_Abstract
{
    /*
     * @var App_Rest_Storage_Interface
     */
    protected $_storage;
    public function __call($methodName, $args)
    {
        if (!method_exists($this->getStorage(), $methodName)) {
            throw new App_Rest_Service_Exception(sprintf('The storage does not have the method "%s"', $methodName));
        }

        switch ($methodName) {
            case 'get':
            case 'put':
            case 'delete':
                //if id param isnot set, throw an exception
                if (FALSE === ($id = $this->getRequest()->getParam('id', FALSE))) {
                    throw new  App_Rest_Service_Exception(sprintf('Method "%s" expects an id param, none provided', $methodName));
                }
                $iterator = $this->getStorage()->$methodName($id, $this->getRequest()->getParams());
                break;
            case 'index':
            case 'post':
            default:
                //if index, post or not a tradition RESTful request, the function must expect the first and only argument to be an array
                $iterator =  $this->getStorage()->$methodName($this->getRequest()->getParams());
                break;
        }


        return $this->_getResult($iterator);
    }

    protected function _getResult($iterator)
{ /*
       * write your own, in my case i make a paginator and then
       *  either return it or send data via the json helper
       * 
      /*
}

Теперь для интерфейса. Это сделает фактическую работу по хранению, изменению и возврату данных. Прелесть использования его в качестве интерфейса в том, что вы можете легко реализовать его независимо от того, что вы используете для слоя модели. Я создал абстрактное хранилище, которое просто имеет Zend_Form (для проверки) и Zend_Db_Table для фактических данных. но вы также можете реализовать его на любом объекте.

interface App_Rest_Storage_Interface extends Zend_Validate_Interface
{
    public function index(array $params = NULL);

    public function get($id, array $params = NULL);

    public function post(array $params);

    public function put($id, array $params);

    public function delete($id, array $params);

}

Теперь операция в любом месте вашего сайта. Предположим, у вас есть услуга "Клиенты". Внутри любого контроллера это так же просто, как

$customer = $this->_helper->helper->customers->get(1);

в другом месте (например, помощник вида):

Zend_Controller_Action_HelperBroker::getStaticHelper('customers')->get(1)

Надеюсь, это поможет. Это хорошо работает для меня.

Отказ от ответственности: я никогда не делал этого, я не знаю, насколько это возможно.

Так как Zend_Rest_Controller расширяет Zend_Controller_Action с помощью реальной, специфичной для отдыха логики, за исключением нескольких абстрактных методов, вы можете просто заставить свои контроллеры веб-сайтов расширять контроллер Rest. Пример:

class Web_IndexController extends Rest_IndexController
{
    public function IndexAction() {
        //do whatever your rest contrller would do
        $result = parent::indexAction();
        //add website specific specific logic here
    }
}

Если действия вашего контроллера покоя возвращают значения, такие как объекты или массивы БД, основанные на том, что произошло, вы можете затем использовать возвращенные данные для создания логики, специфичной для веб-сайта.

Стоит задуматься: если вы используете помощник действий json для возврата значений для вашего оставшегося API, вы должны встроить свойство контроллера для подавления отправки и выхода. Пример:

class Rest_IndexController extends Zend_Rest_Controller
{

    protected $_sendJson = TRUE;
    public function IndexAction() {
        $this->_helper->json($data, $this->_sendJson);
    }
}

class Web_IndexController extends Rest_IndexController
{
    protected $_sendJson = FALSE;
}

Счастливого взлома!

Другие вопросы по тегам