разработка поддерживаемой RPC-системы

Я работаю над веб-приложением, которое будет широко использовать методы AJAX для связи клиент/сервер... в частности, JSON-RPC. Zend Framework используется на стороне сервера и предлагает хороший сервер JSON-RPC, который я хотел бы использовать.

Моя цель – создать удобную в сопровождении систему, предоставляющую большой подмножество серверной функциональности клиентской стороне (javascript) без ненужного дублирования кода. Я видел множество сообщений в блогах и руководств по использованию сервера JSON-RPC ZF (см. здесь и здесь), но все они похоже, он был направлен на раскрытие небольшого общедоступного API. Дублирование кода является распространенным явлением, например, в одном сообщении в блоге представлен следующий метод:

public static function setTitle($bookId, $title) {
    $book = new Nickel_Model_Book($bookId);
    $book->setTitle($title);
    $book->update();
    return true;
}

Мне не нравится тот факт, что есть два метода setTitle. Если сигнатура метода для одного изменяется, другой должен синхронизироваться... это похоже на кошмар с ремонтопригодностью, если ваш API обширен. Мне кажется, должен быть один Book класс, с одним setTitle методом.

Моя первоначальная мысль — добавить аннотацию docblock @export к методам/классам, которые я хочу открыть. Когда я решаю раскрыть метод setTitle, я просто добавляю аннотацию, а не новый метод.

Одна потенциальная проблема, которую я вижу, связана с сохранением объекта. На стороне сервера для setTitle имеет смысл установить свойство заголовка объекта... но не сохранять его в базе данных до тех пор, пока не будет вызвано update(). На стороне клиента вызов setTitle должен немедленно повлиять на базу данных. Одним из возможных решений является изменение всех методов доступа таким образом, чтобы они принимали необязательный второй параметр, означающий, что модификация должна немедленно обновить базу данных:

function setTitle($title, $persist = false) {
    $this->title = $title;

    if ($persist) $this->update();
}

Какой-то прокси-класс может гарантировать, что флаг $persist установлен для всех вызовов RPC на стороне клиента.

Другая проблема — сериализация объектов PHP. На стороне сервера имеет смысл выполнять объектно-ориентированный вызов $book->setTitle("foo"), но на стороне клиента book.setTitle(1234, "foo") имеет смысл (где 1234 — идентификатор книги) из-за отсутствия состояния. Мое решение для этого состояло бы в том, чтобы вышеупомянутый прокси-класс отвечал за то, чтобы каким-то образом превратить book.setTitle(1234, "foo") в:

$book = new Book();
$book->load(1234);
return $book->setTitle($title);

Я чувствую, что эта проблема, должно быть, решалась или обсуждалась раньше... но я не нахожу много ресурсов в Интернете. Это кажется разумным решением?


person Josh Johnson    schedule 22.11.2010    source источник
comment
Вы решили эту проблему или она все еще существует? Не могли бы вы опубликовать свое решение или удалить вопрос или сказать нам, чтобы мы начали думать!   -  person markus    schedule 20.03.2011
comment
Иногда, когда шаблон для чего-то, по вашему мнению, должен существовать, не существует, это происходит потому, что вы представляете неверное решение. Причина, по которой вы не хотите просто выставлять большое количество методов в качестве веб-сервиса, заключается в том, что клиент должен получить как можно более детализированные методы. Дизайн вашего сервиса должен исходить из потребностей клиента. Клиент должен иметь доступ к идеально простому интерфейсу. Создавайте детализированные сервисы и делайте их более детализированными в соответствии с требованиями композиции. Отделение от общедоступного API и вашей библиотеки стоит дополнительных усилий.   -  person JeremyWeir    schedule 07.06.2011
comment
Я решаю эту проблему с помощью nowjs.com и expressjs.com   -  person Heavy Gray    schedule 23.08.2011
comment
Если я хорошо понимаю: вы хотите избавиться от контроллера и вместо этого напрямую вызывать свою модель с помощью своего представления. нет ?   -  person remi bourgarel    schedule 14.09.2011


Ответы (3)


То, что вам нужно, называется Сервисный уровень.

Их объекты должны быть чисто контейнерами данных (если вы не используете Active Record), вы должны предоставлять доступ только к своему сервисному уровню, а это, в свою очередь, обеспечивает доступ к их объектам и их соответствующим методам.

Ваш класс Book является моделью домена, теперь вы должны создать свой сервисный уровень

Ваш класс обслуживания будет примерно таким:

class BookService extends Service {

    //...

    public function changeBookTitle( $title, Book $book )
    {
        //verify if the $title is correct and valid
        $book->setTitle( $title );
        $this->methodToGetThePersistenceManager()->save( $book );

        //fire events, create a result object, etc...
    }
}
person jonathancardoso    schedule 09.09.2011

Я обдумывал твой вопрос несколько минут. Если вы хотите попробовать, и это в PHP, вы можете сделать некоторые волшебные методы формы

набор {свойств} {объекта}.

Вы можете сделать это с помощью волшебного метода __call.

Таким образом, вам не нужно было бы явно определять методы, но свойства все равно будут установлены.

Магический метод будет определять, какой тип объекта он должен создать и какое свойство установить для него, а затем сохранить.

Вам нужно будет найти какой-то способ различать удаленные вызовы и локальные вызовы, чтобы вы не упорствовали по ошибке, если вы делаете вызовы из локальной библиотеки и что вы упорствуете при удаленных вызовах.

Я чувствую, что как-то неправильно понял ваш вопрос, но я попытался.

person Jerry Saravia    schedule 09.06.2011

Хорошо,

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

В любом случае, вам, вероятно, придется сделать большую часть этого самостоятельно или просто вставить какой-то перехватчик во все вызовы методов, вот быстрый пример:

class Book {
  public $title = '';
  public $id = 0;
  public function setTitle($string) {
    $this->title = $string;
    echo "title called with $string\n";
  }
  public function twoArgs($arg1,$arg2) {
    echo "Multi-call: $arg1,$arg2\n";
  }
}
class Api {
  public function parse($json) {
    $jsonobj = json_decode($json,true);
    foreach ($jsonobj as $key=>$value) {
      $class_name = $key;
      $obj = new $class_name();
      foreach ($value as $vkey=>$vvalue) {
        if (method_exists($obj,$vkey)) {
          call_user_func_array(array($obj,$vkey),$vvalue);
        } else if (isset($obj->$vkey)) {
          echo "Setting $vkey\n";
          $obj->$vkey = $vvalue;
        }
      }
    }
  }
}

$json = json_encode(array('Book' => array('id' => 1234, 'setTitle' => array('The new title'))));
$api = new Api();
$api->parse($json);
$json = json_encode(array('Book' => array('id' => 1234, 'twoArgs' => array('arg1 :) ', 'arg2 :]'))));
$api->parse($json);

Очевидно, вы захотите добавить логику для обработки пометки сохранения и загрузки или разрешения им передавать конструктор: [args] и обрабатывать это. и т. д. Когда дело доходит до раскрытия функций, вы раскрываете их в документации, все они доступны, если с этого момента они являются общедоступными функциями и т. д.

person J. Martin    schedule 20.08.2011