Wunderlist — одно из самых популярных приложений для управления задачами и хранения списков. Он позволяет вам создавать списки, в которые вы можете добавлять задачи, которые легко проверяются и имеют встроенные в них всевозможные функции. Он имеет встроенные приложения для всех основных ОС, и все ваши данные синхронизируются эффективно в облаке. С ним очень приятно работать, если вы пытаетесь сохранять самоорганизацию, и вы можете многое сделать даже с бесплатной версией.
В настоящее время большинство веб-приложений предоставляют разработчикам API-интерфейсы для интеграции со своими собственными приложениями (даже бесплатными, такими как Trello). В 2015 году это наконец-то произошло и с Wunderlist: их API был обнародован и тщательно документирован .
В этой статье мы рассмотрим их API и увидим, как мы можем программно взаимодействовать с приложением Wunderlist. С этой целью мы запустим очень маленькое PHP-приложение без какой-либо инфраструктуры, а только некоторые необработанные PHP-файлы, собранные Composer для тестирования. А поскольку нет официального PHP SDK, мы будем использовать Guzzle для выполнения HTTP-запросов к различным конечным точкам.
Кроме того, если вам интересно, ознакомьтесь с этим демонстрационным репозиторием, который содержит некоторый базовый код, который позволяет вам загружать все задачи из данного списка, а затем отмечать их по одному, используя Ajax. Подобно основной функциональности Wunderlist, но опять же без каких-либо рамок или стилей.
Игровая площадка
Внутри корневой папки нашего проекта у нас может быть файл composer.json, например:
{ "require": { "php": ">=5.5.0", "guzzlehttp/guzzle": "~6.0" }, "autoload": { "psr-4": { "Wunderlist\\": "src/" } }, "require-dev": { "symfony/var-dumper": "~2.7" } }
Как вы можете видеть, мы будем использовать Guzzle 6, совместимый с PSR-7, и любые классы, которые мы хотим создать, попадут в папку src/
которая находится в пространстве имен как Wunderlist
. Мы также можем пойти дальше и создать эту папку. Наконец, мы используем компонент Symfony VarDumper для вывода переменных на экран во время разработки.
Еще в корневой папке проекта, давайте добавим три файла: index.php
, keys.php
, .gitignore
.
Внутри index.php
мы будем играть с API. Мы будем использовать keys.php
для хранения любых токенов доступа, и на них будут ссылаться в файле .gitignore
чтобы они не фиксировались в хранилище.
.gitignore
должен выглядеть как стартер:
vendor/* keys.php
Не стесняйтесь расширять это с правилами от этого .
Приложение Wunderlist
Прежде чем приступить к работе с API, нам нужно зайти в нашу учетную запись Wunderlist и создать новое приложение. При этом нам нужно будет указать URL-адрес приложения и URL-адрес обратного вызова приложения, которые будут использоваться потоком OAuth, который позволяет пользователям предоставлять доступ к вашему приложению. Мы не будем вдаваться в подробности, поэтому мы можем просто добавить несколько фиктивных URL.
Однако для аутентификации в приложении нам нужно нажать кнопку « Создать токен доступа» , которая сгенерирует токен доступа администратора, который можно использовать для аутентификации с помощью API в качестве нашего собственного пользователя (фактически мы сейчас создаем Приложение Wunderlist для себя).
Класс Wunderlist
После сохранения приложения мы можем вернуться к нашему приложению PHP и вставить следующее в файл keys.php
:
<?php $client_id = 'your-client-id'; $access_token = 'the-access-token-you-generated-earlier';
Приведенную выше информацию можно получить из вашей учетной записи Wunderlist. Затем нам потребуется этот файл внутри index.php
и мы будем использовать эти переменные глобально. Это не лучшая практика, но она подойдет для нашей небольшой демонстрации, чтобы все было коротким и простым. Для лучшего решения проблемы загрузки учетных данных вы можете проверить этот подход вместо этого.
Далее, давайте создадим класс с именем WunderlistClient
внутри папки src/
нашего приложения:
<?php namespace Wunderlist; use GuzzleHttp\ClientInterface; use Psr\Http\Message\ResponseInterface; class WunderlistClient { /** * @var ClientInterface */ private $client; /** * Constructor * * @param ClientInterface $client */ public function __construct(ClientInterface $client) { $this->client = $client; }
Все, что сейчас делает наш класс, — это принимает клиент Guzzle в качестве параметра и устанавливает его в качестве закрытой переменной. Затем мы будем использовать этот класс для вызова API Wunderlist, добавляя методы, отвечающие за различные конечные точки и задачи.
Вернувшись в наш файл index.php
мы можем иметь это сейчас:
<?php require 'vendor/autoload.php'; require_once 'keys.php'; use GuzzleHttp\Client; use Wunderlist\WunderlistClient as Wunderlist; $guzzle = new Client( [ 'base_uri' => 'https://a.wunderlist.com/api/v1/', 'headers' => [ 'Content-Type' => 'application/json', 'X-Client-ID' => $client_id, 'X-Access-Token' => $access_token, ] ] ); $wunderlist = new Wunderlist($guzzle);
Мы начнем с создания нового клиента Guzzle. Мы устанавливаем base_uri
API и некоторые необходимые заголовки (что наиболее важно для аутентификации). Заголовок типа содержимого JSON необходим при отправке данных в виде JSON в теле запроса. Затем мы создаем новый экземпляр нашего класса WunderlistClient
и передаем ему клиент Guzzle.
Далее мы будем использовать объект $wunderlist
для выполнения запросов и возврата ответов для проверки.
Возврат данных с помощью API Wunderlist
Для начала давайте запросим все списки на нашем аккаунте. Внутри класса WunderlistClient
мы можем добавить этот публичный метод:
/** * Returns all the lists * * @return array */ public function getLists() { $response = $this->client->get('lists'); $this->checkResponseStatusCode($response, 200); return json_decode($response->getBody(), true); }
Мы используем Guzzle, чтобы сделать запрос к соответствующей конечной точке, и используем закрытый метод checkResponseStatusCode()
чтобы убедиться в его успешности. Последний выдаст исключение, если это не тот случай, который может быть пойман вызывающим абонентом (мы увидим через минуту).
Кроме того, вы заметите, что в Guzzle 6 у нас больше нет метода для декодирования ответа от JSON, но мы скорее имеем дело с Stream в качестве тела ответа. А поскольку ожидаемые данные не так велики, мы можем просто вывести их в строку с помощью собственного метода __toString()
и затем декодировать.
Давайте также checkResponseStatusCode()
рассмотрим наш checkResponseStatusCode()
:
/** * Check the response status code. * * @param ResponseInterface $response * @param int $expectedStatusCode * * @throws \RuntimeException on unexpected status code */ private function checkResponseStatusCode(ResponseInterface $response, $expectedStatusCode) { $statusCode = $response->getStatusCode(); if ($statusCode !== $expectedStatusCode) { throw new \RuntimeException('Wunderlist API returned status code ' . $statusCode . ' expected ' . $expectedStatusCode); } }
Как только все это будет getLists()
, мы можем вызвать getLists()
из index.php
и выгрузить все содержимое результата:
try { $lists = $wunderlist->getLists(); dump($lists); } catch(\Exception $exception) { dump($exception); }
Загрузка файла в браузер должна позволить нам проверить всю возвращаемую информацию обо всех списках в нашей учетной записи. Ухоженная.
Как только у нас будут все наши списки и мы определим идентификатор того, который нам нужен, мы сможем вернуть больше информации о нем:
/** * Returns a specific list * * @param int $id * * @return mixed */ public function getList($id) { if (!is_numeric($id)) { throw new \InvalidArgumentException('The list id must be numeric.'); } $response = $this->client->get('lists/' . $id); $this->checkResponseStatusCode($response, 200); return json_decode($response->getBody(), true); }
Здесь мы делаем базовую проверку, чтобы увидеть, является ли идентификатор числовым, убедитесь, что запрос был успешным, и возвращаем декодированный ответ JSON из API Wunderlist. Теперь внутри index.php
:
try { $list = $wunderlist->getList($id); dump($list); } catch(\Exception $exception) { dump($exception); }
Мы должны увидеть некоторую информацию о самом списке.
Но что, если мы хотим загрузить все задачи из этого списка? Мы можем реализовать что-то вроде этого:
/** * Return all the tasks of a given list * * @param int $list_id * * @return array() */ public function getListTasks($list_id) { if (!is_numeric($list_id)) { throw new \InvalidArgumentException('The list id must be numeric.'); } $response = $this->client->get('tasks', ['query' => ['list_id' => $list_id]]); $this->checkResponseStatusCode($response, 200); return json_decode($response->getBody()); }
Чтобы указать, для какого списка мы хотели бы, чтобы задачи были возвращены, нам нужно передать его идентификатор в качестве параметра запроса в запрос GET. Остальное примерно так же, как и раньше. Внутри index.php
мы можем отлаживать все данные, относящиеся к (незавершенным) задачам из этого списка:
try { $tasks = $wunderlist->getListTasks($id); dump($tasks); } catch(\Exception $exception) { dump($exception); }
Создание и обновление данных с помощью Wunderlist API
Все запросы, которые мы сделали до сих пор, были GET-вызовами для загрузки информации. Давайте попробуем сделать несколько, с помощью которых мы создаем и обновляем ресурсы с помощью API. Для начала мы можем создать такую задачу:
/** * Creates a new task * * @param string $name * @param int $list_id * @param array $task * * @return mixed */ public function createTask($name, $list_id, $task = []) { if (!is_numeric($list_id)) { throw new \InvalidArgumentException('The list id must be numeric.'); } $task['name'] = $name; $task['list_id'] = $list_id; $response = $this->client->post('tasks', ['body' => json_encode($task)]); $this->checkResponseStatusCode($response, 201); return json_decode($response->getBody()); }
Данные новой задачи должны быть отправлены в теле запроса POST в виде JSON. Чтобы использовать этот метод внутри index.php
, мы можем сделать что-то вроде этого:
try { $created = $wunderlist->createTask('My new task', 'the-list-id'); dump($created); } catch(\Exception $exception) { dump($exception); }
Это только два обязательных параметра, которые должны существовать в задаче, которую мы отправляем в Wunderlist, но мы, конечно, можем добавить больше в массив, передаваемый в качестве третьего параметра нашему методу. Не стесняйтесь проверить соответствующую страницу документации для получения дополнительной информации.
Если все идет хорошо, Wunderlist возвращает статус 201 ( Создан ). И что мы здесь возвращаем, это массив информации, связанной с только что созданной задачей. Это то, что мы можем проверить в браузере, если загрузим index.php
.
Чтобы теперь обновить задачу, которую мы создали, нам нужно отправить запрос PATCH на его конечную точку только с теми данными, которые мы хотим изменить, и текущей версией (чтобы гарантировать, что разные приложения не перезаписывают данные друг друга). Таким образом, отправляя логическое ИСТИНА для completed
свойства задачи, мы, по сути, помечаем его как проверенный. Мы можем создать этот метод, предназначенный для выполнения задач:
/** * Completes a task * * @param int $task_id * @param int $revision * @return mixed */ public function completeTask($task_id, $revision) { if (!is_numeric($task_id)) { throw new \InvalidArgumentException('The list id must be numeric.'); } elseif (!is_numeric($revision)) { throw new \InvalidArgumentException('The revision must be numeric.'); } $response = $this->client->patch('tasks/' . $task_id, ['body' => json_encode(['revision' => (int) $revision, 'completed' => true])]); $this->checkResponseStatusCode($response, 200); return json_decode($response->getBody()); }
Конечная точка, которую мы PATCHING, содержит идентификатор задачи, и мы отправляем в качестве тела запроса JSON-представление значений, которые мы хотим изменить в задаче. Как упоминалось ранее, это должно содержать последнюю ревизию ресурса, которую мы можем узнать из getListTasks()
который мы написали ранее.
Предполагая, что мы знаем последнюю ревизию и идентификатор задачи в index.php
, мы можем выполнить задачу следующим образом:
try { $response = $wunderlist->completeTask($task_id, $revision); dump($response) } catch(\Exception $exception) { dump($exception); }
Если все идет хорошо, ответ, который мы получаем в теле, — это JSON-представление нашей задачи, которое мы можем декодировать в массив.
Вывод
Конечно, есть еще много конечных точек и гораздо больше, которые вы можете сделать с каждой конечной точкой. Идите вперед и проверьте страницы документации, чтобы получить обзор всего, что вы можете сделать.
Очень важным аспектом интеграции с Wunderlist является также предоставление пользователям доступа к своей учетной записи Wunderlist. Вы можете сделать это, используя поток OAuth, который мы упоминали ранее — вот несколько уроков о том, как этого добиться.
Наконец, если вам интересно, взгляните на хранилище, где вы можете найти супер крошечное расширение того, что мы обсуждали в этой статье, с помощью которого вы можете предоставить идентификатор списка, распечатать его задачи на экране, а затем отметьте один из них. по одному через Ajax. Выглядит ужасно, но вы должны быть в состоянии понять суть
Вы уже написали приложение Wunderlist? Показать нам!