Статьи

Создание вашего стартапа: импорт с помощью API контактов Google

Конечный продукт
Что вы будете создавать

Это руководство является частью серии « Создай свой стартап с помощью PHP» на Envato Tuts +. В этой серии я проведу вас через запуск стартапа от концепции до реальности, используя мое приложение Meeting Planner в качестве примера из реальной жизни. На каждом этапе я буду публиковать код Планировщика собраний в качестве примеров с открытым исходным кодом, из которых вы можете извлечь уроки. Я также буду решать вопросы, связанные с бизнесом по мере их возникновения.

Доброе утро. Сегодня я расскажу вам, как я использовал API Google для импорта контактов людей в Meeting Planner. Цель состоит в том, чтобы люди быстрее приглашали своих друзей на встречи.

Если вы еще не пытались запланировать встречу с Планировщиком совещаний , попробуйте. Если вы используете свою учетную запись Google для регистрации, вы можете посетить страницу друзей выше и импортировать ваши контакты Google . Поделитесь своими мыслями и отзывами в комментариях ниже.

Я участвую в обсуждениях, но вы также можете связаться со мной @reifman в Твиттере (недавно мой аккаунт был подтвержден, поэтому я должен быть таким же крутым, как Джастин Биберт (примечание для редакционных богов — я уверен в этом правописании. Я думаю, что это правильно Оставьте это. Я всегда открыт для новых идей для планировщика собраний, а также предложений для будущих серий.

Напоминаем, что весь код для Meeting Planner предоставляется с открытым исходным кодом и написан в Yii2 Framework для PHP. Если вы хотите узнать больше о Yii2, ознакомьтесь с моей параллельной серией Программирование с Yii2 .

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

Я считаю, что размер таблицы User в Meeting Planner влияет на общую производительность службы. Я не хотел импортировать контакты, которые могут быть неактуальными в таблицу пользователей.

Это создало некоторые сложности как в UX, так и в коде, когда люди ищут и получают доступ к своим друзьям в сервисе.

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

Создание стартапов Google Contacts API - список импортированных контактов на странице друзей

Все это приводит к тому, что людям будет проще добавлять друзей из своих контактов, просто набрав первые несколько символов. Я использую виджет Typeahead во всплывающем окне « Добавить участников» , показанном ниже:

Создание стартапов API контактов Google - добавление формы участника

После того, как я импортировал свои контакты Google, они интегрированы с моими друзьями (людьми, которых я уже пригласил на встречи или пригласил).

В этом случае я начал набирать sar, и появилась целая куча имен Sar-префиксов :

Создание стартапов API контактов Google - выпадающий список из таблицы друзей и адресов

Поиск кого-либо для приглашения на собрание из ваших контактов Google становится довольно быстрым и легким (пока вы не добавите их много, о чем я упомяну ниже).

Я также не хочу злоупотреблять доверием людей, злоупотребляя их тысячами контактов. В настоящее время мы даже не будем предлагать людям возможность пригласить всех своих контактов Google в Meeting Planner, хотя мы можем предложить это в будущем. Мы, конечно, не будем отправлять их по электронной почте без разрешения.

Если вы еще этого не сделали, загляните в Создание своего стартапа с помощью PHP: Упрощение Onramp с OAuth . Это эпизод, когда я впервые аутентифицировал API Google для входа и регистрации OAuth.

Google заботится о безопасности своих API. В свете того, что недавно произошло с хаки Yahoo , я ценю это более глубоко. Однако это делает их API более сложными для аутентификации и работы с другими.

На самом деле, я нашел поток API контактов Google одним из самых запутанных, разочаровывающих и сложных, которые мне пришлось написать. И программисты Google API отрицают PHP-программистов — мы последние получаем пример кода.

Давайте погрузимся в.

Поскольку уже существует таблица UserContact для телефона пользователя и адресов Skype, я решил позвонить в таблицу Address. Вот миграция для его создания:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
use yii\db\Schema;
use yii\db\Migration;
 
class m160904_001244_create_table_address extends Migration
{
    public function up()
    {
        $tableOptions = null;
        if ($this->db->driverName === ‘mysql’) {
            $tableOptions = ‘CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB’;
        }
 
        $this->createTable(‘{{%address}}’, [
            ‘id’ => Schema::TYPE_PK,
            ‘user_id’ => Schema::TYPE_BIGINT.’
            ‘status’ => Schema::TYPE_SMALLINT .
            ‘firstname’ =>Schema::TYPE_STRING.’
            ‘lastname’ =>Schema::TYPE_STRING.’
            ‘fullname’ =>Schema::TYPE_STRING.’
            ’email’ =>Schema::TYPE_STRING.’
            ‘created_at’ => Schema::TYPE_INTEGER .
            ‘updated_at’ => Schema::TYPE_INTEGER .
        ], $tableOptions);
        $this->addForeignKey(‘fk_address_user_id’, ‘{{%address}}’, ‘user_id’, ‘{{%user}}’, ‘id’, ‘CASCADE’, ‘CASCADE’);
    }
 
    public function down()
    {
      $this->dropForeignKey(‘fk_address_user_id’, ‘{{%address}}’);
      $this->dropTable(‘{{%address}}’);
    }
  }

Конечно, я использовал Gii от Yii, чтобы помочь мне с контроллером, моделью и представлениями. Это было рассмотрено ранее в серии запуска .

Возможно, вы помните страницу учетных данных Google в учебнике, упомянутом выше:

Создание стартапов Google Contacts API - форма учетных данных API Google

Вы можете найти его в консоли разработчиков Google .

Вы должны добавить URL-адреса для всех ваших сред — разработки, подготовки, производства и т. Д. — и для каждого контроллера и метода. Это усложняет усилия по работе с его API-интерфейсом контактов, но также, вероятно, лучше защищает данные людей.

Создание стартапов API контактов Google - нижняя часть формы учетных данных API Google

Вот API контактов Google v3.0 . Когда я начал писать код, я не заметил, что теперь они рекомендуют API People для доступа только для чтения. К сожалению, мой код использует API чтения / записи. Итак, я не гений. Предприниматели редко бывают — даже Билл Гейтс говорит, что ему просто повезло.

В общем, я нашел API контактов Google одним из самых запутанных и сложных API, которые я когда-либо использовал.

Если бы у меня был более высокий уровень знаний в разработке Google API или я потратил больше времени на работу над этим, я мог бы найти более простой подход. Но из того, что я мог сказать, было важно, чтобы вы делали все с помощью API с одного URL. В моем случае https://meetingplanner.io/address/import .

А Google возвращает ключи и повторно перенаправляет вас на этот URL-адрес, поэтому вам нужно следить за состоянием API и обходить его.

Я предполагаю, что все это сделано для повышения безопасности, но требует управления состоянием, встроенного в то, что в противном случае было бы простым запросом API. Управление состоянием может сэкономить время, но только если документация и пример кода хороши. В данном случае для PHP это не так.

Давайте посмотрим на AddressController.php actionImport() :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
public function actionImport() {
     // imports contacts from the google api
     $address = new Address();
     // create session cookies
     $session = Yii::$app->session;
     // if we request code reset, then remove the google_code cookie
     // ie if google_code expires
     if (isset($_GET[‘reset’]) && !$session->has(‘google_code_reset’)) {
       // prevent loops
       $session->set(‘google_code_reset’);
       // reset the google_code
       $session->remove(‘google_code’);
       $this->redirect([‘import’]);
     }

Выше я наблюдаю, хочет ли Google сбросить свои токены API. В этом случае я удаляю файлы cookie, в которых я их сохранил, и перенаправляю обратно на метод, чтобы начать сначала.

Ниже я делаю свой первый запрос к Google через его клиентскую библиотеку PHP :

01
02
03
04
05
06
07
08
09
10
11
12
13
// always remove the reset request cookie
     $session->remove(‘google_code_reset’);
     // build the API request
     $redirect_uri=Url::home(true).’address/import’;
     $session->open();
     $client = new \Google_Client();
     $client -> setApplicationName(‘Meeting Planner’);
     $client -> setClientid( Yii::$app->components[‘authClientCollection’][‘clients’][‘google’][‘clientId’]);
     $client -> setClientSecret(Yii::$app->components[‘authClientCollection’][‘clients’][‘google’][‘clientSecret’]);
     $client -> setRedirectUri($redirect_uri);
     $client -> setAccessType(‘online’);
     $client -> setScopes(‘https://www.google.com/m8/feeds’);
     $googleImportUrl = $client -> createAuthUrl();

Google PHP библиотека находится в бета-версии. На самом деле, PHP, как правило, запоздалая мысль для Google Так что не всегда легко работать в PHP с их API.

Обратите внимание, что $redirect_uri от Google снова является тем же методом: 'address/import' .

Далее мы пытаемся поместить токен из параметра запроса в cookie:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
// moves returned code to session variables and returns here
     if (isset($_GET[‘code’]))
       {
           $auth_code = $_GET[‘code’];
       $session->set(‘google_code’,$auth_code);
           header(‘Location: ‘.Url::home(true).’address/import’);
       // do not remove — breaks the API
       exit;
       // do not remove above exit
       } else {
       $session_code = $session->get(‘google_code’);
       if (!isset($session_code)) {
           $this->redirect( $googleImportUrl);
       }
     }

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

К счастью, я также обнаружил, что если я не создаю цикл назад, когда код отсутствует, — чтобы вернуться на ту же страницу снова, он не будет работать согласованно.

Затем мы создаем и просим Google предоставить пользователю диалоговое окно с разрешениями, чтобы предоставить Meeting Planner доступ к их контактам:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
if (isset($session_code)) {
       $auth_code = $session_code;
         $fields=array(
           ‘code’=> urlencode($auth_code),
           ‘client_id’=> urlencode(Yii::$app->components[‘authClientCollection’][‘clients’][‘google’][‘clientId’]),
           ‘client_secret’=> urlencode(Yii::$app->components[‘authClientCollection’][‘clients’][‘google’][‘clientSecret’]),
           ‘redirect_uri’=> urlencode($redirect_uri),
           ‘grant_type’=> urlencode(‘authorization_code’),
       );
     // Requests the access token
       $post = »;
       foreach($fields as $key=>$value)
       {
           $post .= $key.’=’.$value.’&’;
       }
       $post = rtrim($post,’&’);
       $result = $address->curl(‘https://accounts.google.com/o/oauth2/token’,$post);
       $response = json_decode($result);
     if (isset($response->error)) {
         if ($response->error_description == ‘Code was already redeemed.’) {
           $session->remove(‘google_code’);
           return $this->redirect([‘import’]);
         }
         if ($response->error_description == ‘Invalid code.’) {
           $session->remove(‘google_code’);
           return $this->redirect([‘import’]);
         }
         var_dump($response);
         echo Yii::t(‘frontend’,’There was an error. Please contact support.’);
     }
     if (isset($response->access_token) || empty($response->access_token)) {
         $accesstoken = $response->access_token;
     } else {
       echo Yii::t(‘frontend’,’There was an error. No access token. Please contact support.’);
     }
Создание стартапов Google Contacts API - Google запрашивает у пользователя разрешения

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

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

Когда все работает, забавный, легкий код обрабатывает данные. В настоящее время мы собираем 1000 записей пять раз, многократно создавая запросы на нумерацию страниц, которые, конечно, отправляются обратно на этот URL:

1
2
$url = ‘https://www.google.com/m8/feeds/contacts/default/full?max-results=’.$max_results.’&start-index=’.$startIndex.’&alt=json&v=3.0&oauth_token=’.$accesstoken;
$xmlresponse = $address->curl($url);

Перевод XML Google (который также является сложным, с нечетными именами переменных для разработчиков PHP, например, такими ключами, как $contact['gd$email'][0]['address'] со знаком доллара в середине.

Ниже мы делаем каждый запрос, выполняем данные JSON и собираем имена контактов для добавления в таблицу адресов:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// Requests the data
     $startIndex = 1;
     $request_data = true;
     $max_results = Address::CONTACTS_PAGE_SIZE;
     $numberPages = 0;
     while ($request_data && $numberPages <5) {
        //echo ‘calling with startIndex: ‘.$startIndex.'<br />’;
        $url = ‘https://www.google.com/m8/feeds/contacts/default/full?max-results=’.$max_results.’&start-index=’.$startIndex.’&alt=json&v=3.0&oauth_token=’.$accesstoken;
        $xmlresponse = $address->curl($url);
        $contacts = json_decode($xmlresponse,true);
        if (!isset($contacts[‘feed’][‘entry’])) {
          //var_dump ($url);
          //var_dump ($xmlresponse);
          exit;
        }
       $resultsCount =count($contacts[‘feed’][‘entry’]);
       //echo ‘count: ‘.$resultsCount.'<br />’;
        //var_dump (count($contacts[‘feed’][‘entry’]));
        // process out contacts without email addresses
        //$return = array();
        if ($resultsCount>0) {
          foreach($contacts[‘feed’][‘entry’] as $contact) {
            if (isset($contact[‘gd$email’])) {
              $temp = array (
                ‘firstname’ => (isset($contact[‘gd$name’][‘gd$givenName’][‘$t’])?$contact[‘gd$name’][‘gd$givenName’][‘$t’]:»),
                ‘lastname’ => (isset($contact[‘gd$name’][‘gd$familyName’][‘$t’])?$contact[‘gd$name’][‘gd$familyName’][‘$t’]:»),
                ‘fullname’=> $contact[‘title’][‘$t’],
                ’email’ => $contact[‘gd$email’][0][‘address’],
              );
              //$return[]=$temp;
              $address->add($temp);
            } else {
              continue;
            }
          }
          if ($resultsCount<$max_results) {
            Yii::$app->getSession()->setFlash(‘success’, Yii::t(‘backend’,’Your contacts have been imported.’));
            return $this->redirect([‘/friend’,’tab’=>’address’]);
          }
        }
        //var_dump($return);
        $numberPages++;
        $startIndex+=$max_results;
      }

Работа с API контактов Google была очень сложной, плохо документированной и потратила много времени для меня. Хотя я успешно работал со многими API, я знаю, что я не эксперт в этом. Из-за проблем с безопасностью я не хочу биться с Google из-за этого — во многих из этих случаев они, вероятно, знают, почему они делают вещи определенным образом.

Но можно немного повеселиться, верно?

Во-первых, все в Google — гениальны , и они еще раз доказали это, приобретя API.ai, замечательный сервис, который связывает свою кнопку регистрации с его формой входа. На самом деле они сделали:

Я уверен, что команда должной осмотрительности Google видела в этом гения, который остается за пределами смертных, как я. Должно быть, они сказали: «Ух ты, программисты API.ai такие гении, как наши команды AdSense и DFP! Давайте добавим их в алфавит!»

Поскольку я могу говорить с ангелом и инвесторами VC о Meeting Planner в будущем, я хочу быть скромным. Но я был бы в ужасе, если бы моя домашняя страница сделала это, и один из моих потенциальных инвесторов заметил.

Новый Алфавит (новая материнская компания Google) так простителен.

Наконец, давайте просто посмотрим на код за расширенной формой добавления участников. По сути, я получаю письма из таблицы друзей, а затем письма из таблицы адресов:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
$friendsEmail=[];
   $friendsId=[];
   $fq = Friend::find()->where([‘user_id’=>Yii::$app->user->getId()])->all();
   // to do — add a display name field for right side of input
   $fa = Address::find()
     ->select([‘id’,’email’])
     ->where([‘user_id’=>Yii::$app->user->getId()])
     ->limit(5000)
     ->all();
   foreach ($fq as $f) {
     $friendsEmail[]=$f->friend->email;
     $friendsId[]=$f->id;
   }
   foreach ($fa as $f) {
     $friendsEmail[]=$f->email;
     $friendsId[]=$f->id;
   }
   if (count($friendsEmail)>0) {
     ?>
     <p><strong>Choose From Your Friends</strong></p>
     <select class=»combobox input-large form-control» id=»participant-email» name=»Participant[email]»>
     <option value=»» selected=»selected»><?= Yii::t(‘frontend’,’type or click to choose friends’) // chg meetingjs if change string ?></option>
     <?php
     foreach ($friendsEmail as $email) {
     ?>
       <option value=»<?= $email;?>»><?= $email;?></option>
     <?php
       }
     ?>
     <?php
   }
   ?>
   </select>

Однако значение параметра — это просто электронная почта, поскольку было бы сложнее определить, какой это тип друзей (из списка друзей или из таблицы адресов).

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

С 5000 контактов Google в выпадающем списке моих друзей производительность снижается. Возможно, мне нужно лучше связать элемент управления с поиском в базе данных AJAX.

И я потратил много времени раньше, пытаясь расширить таблицу друзей, чтобы включить в нее людей, которых я пригласил, а также контакты Google. Однако это превратилось в беспорядок сложных запросов к базе данных. Связи таблицы User из таблицы Friends начали разрываться для строк контактов, где они будут нулевыми, и это на самом деле оказалось очень трудно разрешить. Управление удалением существующих внешних ключей вверх и вниз через миграцию также коварно.

Эти функции были отличными примерами проблем, связанных с использованием API с плохой документацией на выбранном вами языке и компромиссами в архитектуре кода на данный момент для запуска функций в соответствии с графиком выпуска (решив не сокращать их).

И, безусловно, все еще существуют проблемы с производительностью добавления участника и пользовательским интерфейсом страницы друзей, которые необходимо исправить.

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

Наконец, если вы еще этого не сделали, запланируйте свою первую встречу с Meeting Planner прямо сейчас! Дайте мне знать, что вы думаете в комментариях ниже. Вы также можете обратиться ко мне @reifman . Я всегда открыт для новых идей и тематических предложений для будущих уроков.

Учебное пособие по краудфандингу также находится в разработке, поэтому, пожалуйста, следуйте нашей странице WeFunder Meeting Planner .

Следите за всеми этими и другими учебниками, ознакомившись с серией « Построение стартапа на PHP» .