Думаю, что многие, особенно небольшие, компании, при работе с одним и тем же фреймоворком постоянно пишут какие-то вещи/расширения и т.п., которые решают именно те задачи, с которыми они сталкиваются наиболее часто.
В нашем случае этим фреймворком является Yii, а одной из самых популярных проблем была одновременная разработка web-сервиса для приложений iOS/Android.
Сначала, как и всегда, просто разработчики договаривались между собой что и как, но если разработчик вдруг менялся — начинались проблемы. Далее — описание входных/выходных данных в wiki. При большом количестве мелких изменений возникала проблема синхронизации кода и форматов, описанных в wiki.
Как мы решили проблему — ниже.
Разработка контроллера web-сервиса
Как правило, мобильный сервис — это отдельный модуль проекта. Сделали базовый модуль и базовый контроллер для работы с web-сервисом.
Ключевые вещи — ниже.
class VMJsonServiceController extends CController
{
public $documentationMode = false;
protected $request = null;
...
public function init()
{
if ($this->documentationMode) {
return;
}
if (Yii::app()->request->isPostRequest) {
$json = CJSON::decode(file_get_contents("php://input"), false);
if (isset($json->request)) {
$this->request = $json->request;
} else {
$this->respondWithError(VMServiceResponseCode::SERVICE_ERROR, 'There is no request node');
}
} else {
...
}
}
...
public function checkInputParameters($params = array())
{
if ($this->documentationMode) {
$exception = new VMDocumentationException();
$exception->parameters = VMObjectUtils::fromArray($params);
throw $exception;
}
$this->checkObjectParameter($params, $this->request);
}
...
}
Самая «соль» заключается именно в параметре $documentationMode
, но об этом позже.
Теперь рассмотрим пример контроллера уже с реального проекта, а не из общей библиотеки.
class UsersController extends VMJsonServiceController
{
...
public function actionSignUp()
{
$this->checkInputParameters(array(
'user' => array(
'email' => 'test@test.com',
'password' => '12345',
'phone' => array('optional', 'value' => '+7 999 998 76 54',
'photos' => array('array', 'value' => array(
'file' => base64_encode('Image'),
)
)
));
$email = $this->request->user->email;
...
}
...
}
Метод checkInputParameters проверяет, что данные пришли в нужном для этого метода формате (email и password — обязательные, phone — необязательный, а photos — массив данных. Далее мы уверены, что все необходимые данные у нас есть и с ними можно работать.
Теперь, собственно, о чем шла речь в начале статьи — о документации. В принципе, массив в методе checkInputPаrameters — и есть формат входных данных, и, собственно, на основе него мы и генерируем документацию, но постоянно генерировать её ручками — неинтересно и долго. Поэтому, заставим модуль генерировать документацию о себе самому.
1. Делаем еще один контроллер
class VMDocumentationController extends CController
{
...
//Полный код можете посмотреть в репозитории, здесь покажу лишь один метод
//На вход он принимает объект "контроллер" и название метода
private function getDefinition($class, $method)
{
try {
$class->{$method}();
} catch (VMDocumentationException $e) {
return (object) array(
'parameters' => $e->parameters
);
}
}
...
}
2. Правим код базового модуля
class VMJsonServiceModule extends CWebModule {
public function init() {
...
$this->controllerMap = array(
'documentation' => array(
'class' => 'VMDocumentationController'
)
);
}
}
Что нам это дает? А то, что по одному и тому же url программист iOS/Android имеет документацию для любого проекта, меняется только baseUrl.
Как выглядит документация?
Пара скриншотов ниже.
В верхнем навбаре — список всех контроллеров модулей, в левом — список всех экшнов контроллера. Необязательные параметры можно удалить из запроса, кнопка Call — отправит запрос и покажет ответ сервера. То есть вы можете не писать ни строчки кода, но протестировать работу веб-сервиса на любых входных данных.
Ну и напоследок — все исходные коды можете найти здесь. Местами код ужасен, местами очень ужасен, многое из того, что есть в нашей библиотеке кроме мобильного сервиса — нафиг никому не надо, но если будет что вдруг предложить — pull-request вам в помощь.
Спасибо за внимание
Автор: Anexroid