При создании современного веб проекта вам не обойтись без работы с внешними сервисами. Недавно у нас возникла задача по работе с календарями. В этой статье я бы хотел рассказать о некоторых моментах интеграции проекта на Symfony2 с Google Calendar.

Получение ключа


Для обмена данными с google нужно получить ключ. Наш сайт будет обращаться за информацией незаметно для пользователя, поэтому нам нужен ключ для сервис аккаунта. Для получения ключа нужно отправиться в консоль для разработчиков. Добавить приложение, зайти во вкладку Api и активировать календарь. Теперь вы сможете получить ключ на вкладке Credentials.
Есть вариант json или p12. Особой разницы нету, зависит от того, что вам удобнее.

Модуль Google Api


Для работы с сервисами google есть api клиент. Установим его через composer
php composer.phar require google/apiclient

Сделаем небольшую обертку и объявить ее как сервис.
class Google
{
    /* @var \Google_Client */
    private $client;

    /* @var \Google_Service_Calendar */
    private $calendar;

    private $scope;

    public function __construct($scope)
    {
        $this->client = new \Google_Client();
        $this->scope = $scope;
    }

    public function setCredentialsP12($p12Path, $email)
    {
        $credentials = new \Google_Auth_AssertionCredentials(
            $email,
            $this->scope,
            file_get_contents($p12Path)
        );

        $this->client->setAssertionCredentials($credentials);
    }

    public function setCredentialsJson($jsonPath)
    {
        $this->client->loadServiceAccountJson($jsonPath, $this->scope);
    }

    /**
     * @return \Google_Service_Calendar
     */
    public function getCalendar()
    {
        if (!$this->calendar) {
            $this->calendar = new \Google_Service_Calendar($this->client);
        }

        return $this->calendar;
    }
}


services:
    google.client:
        class: MyBundle\Google
        arguments: ['https://www.googleapis.com/auth/calendar.readonly'] # Сейчас нам нужны только права на чтение календаря

Методы setCredentialsP12 и setCredentialsJson нужны для того чтобы передавать ключи внутрь нашего сервиса. Метод getCalendar возвращает подготовленный объект календаря и нам не нужно как-то его дополнять вне нашего сервиса.

Подключение ключа


У нас могут быть разные ключи в разных окружениях, а также у каждого разработчика может быть свой ключ. Поэтому ключ в сервис будем передавать в зависимости от наличия определенных параметров, которые могут задаваться в локальном конфиге. Для этого напишем и подключим CompilerPass.
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

class CredentialsPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        $googleClient = $container->getDefinition('google.client'); //Имя сервиса который мы объявили в предыдущем абзаце

        if ($container->hasParameter('google_p12_path') && $container->hasParameter('google_p12_email')) {
            $path = $container->getParameter('google_p12_path');
            $email = $container->getParameter('google_p12_email');

            $googleClient->addMethodCall('setCredentialsP12', [$path, $email]);
        } elseif ($container->hasParameter('google_json_path')) {
            $googleClient->addMethodCall('setCredentialsJson', [$container->getParameter('google_json_path')]);
        }
    }
}

И теперь нам достаточно написать в локальном конфиге.
parameters:
    google_p12_path: my-certificate.p12
    google_p12_email: my-email@mail.com
или
parameters:
    google_json_path: my-certificate.json


Получение списка событий


Теперь у нас есть сервис и мы можем запросить список событий.
try {
    /* @var $start \DateTime */
    $start;

    /* @var $end \DateTime */
    $end;

    $events = $google->getCalendar()->events->listEvents('example@mail.com', [
        'timeMin' => $start->format('c'),
        'timeMax' => $end->format('c'),
    ]);
} catch (\Google_Service_Exception $exception) {
    //TODO implement error catching
}

Для каждого календаря есть свой уникальный email (example@mail.com в примере выше). С помощью метода listEvents здесь мы получаем все события из нашего календаря за определенный промежуток времени. Гугл использует стандарт ISO 8601 для временных параметров (timeMin, timeMax), так что для форматирования времени достаточно использовать букву c.

Вместо заключения


Потратив немного времени, мы получаем доступ из нашего проекта к google calendar. Дальше можно развивать эту интеграцию, например можно организовать более сложный поиск или добавление событий из нашего проекта. И если вы готовы доверить свои календари (а может и не свои) гуглу, то вы можете получить за дешево очень мощный инструмент, который при этом можно гибко подстроить под ваши нужды.

Комментарии (1)


  1. michael_vostrikov
    14.06.2015 14:47
    +2

    Добавлю информацию по работе с календарями пользователей.

    Например, example@mail.com — это email пользователя, имя основного календаря обычно совпадает с email. Просто так поставить событие пользователю в календарь не получится. Надо предоставить доступ для сервисного аккаунта. Есть 2 варианта — предоставление доступа вручную и domain-wide authority.

    Когда генерируем API key, там же генерируются 2 параметра:

    Client ID:
    123456789012-abcdef1g2hijkl34mn56opqrstuvwxyz.apps.googleusercontent.com
    Email address:
    123456789012-abcdef1g2hijkl34mn56opqrstuvwxyz@developer.gserviceaccount.com

    Предоставление доступа вручную:
    Надо чтобы пользователь сам зашел в календарь и добавил разрешение вносить изменения для сервисного аккаунта (Email address).
    Настройки — Календари — [Название календаря] — Открытие общего доступа к этому календарю — Общий доступ для отдельных пользователей
    Пользователь: 123456789012-abcdef1g2hijkl34mn56opqrstuvwxyz@developer.gserviceaccount.com
    Настройки разрешений: Вносить изменения

    Domain-wide authority:
    Если в компании используется свой домен для рабочей почты пользователей, можно настроить авторизацию по всему домену. Приложение при этом совершает действия как бы от имени пользователя (impersonate).
    Это делается через консоль администратора домена: admin.google.com. Для предоставления доступа используется Client ID.
    Подробно написано здесь developers.google.com/identity/protocols/OAuth2ServiceAccount в разделе Delegating domain-wide authority to the service account.

    В коде надо добавить установку свойства sub (email пользователя, которым мы прикидываемся):

    $credentials = new Google_Auth_AssertionCredentials(
        $service_email,  // 123456789012-abcdef1g2hijkl34mn56opqrstuvwxyz@developer.gserviceaccount.com
        $scope,          // https://www.googleapis.com/auth/calendar
        file_get_contents($p12Path)
    );
    $credentials->sub = $user_email;  // example@mail.com