Это руководство является частью серии « Создай свой стартап с помощью PHP» на Envato Tuts +. В этой серии я проведу вас через запуск стартапа от концепции до реальности, используя мое приложение Meeting Planner в качестве примера из реальной жизни. На каждом этапе я буду публиковать код Планировщика собраний в качестве примеров с открытым исходным кодом, из которых вы можете извлечь уроки. Я также буду решать вопросы, связанные с бизнесом по мере их возникновения.
Вступление
Доброе утро. Сегодня я расскажу вам, как я использовал API Google для импорта контактов людей в Meeting Planner. Цель состоит в том, чтобы люди быстрее приглашали своих друзей на встречи.
Если вы еще не пытались запланировать встречу с Планировщиком совещаний , попробуйте. Если вы используете свою учетную запись Google для регистрации, вы можете посетить страницу друзей выше и импортировать ваши контакты Google . Поделитесь своими мыслями и отзывами в комментариях ниже.
Я участвую в обсуждениях, но вы также можете связаться со мной @reifman в Твиттере (недавно мой аккаунт был подтвержден, поэтому я должен быть таким же крутым, как Джастин Биберт (примечание для редакционных богов — я уверен в этом правописании. Я думаю, что это правильно Оставьте это. Я всегда открыт для новых идей для планировщика собраний, а также предложений для будущих серий.
Напоминаем, что весь код для Meeting Planner предоставляется с открытым исходным кодом и написан в Yii2 Framework для PHP. Если вы хотите узнать больше о Yii2, ознакомьтесь с моей параллельной серией Программирование с Yii2 .
Думая об интеграции контактов Google
Страница друзей
Многие люди имеют тысячи контактов в своей учетной записи Google, и немногие из них важны для них. Но, для большинства, нет никакого способа определить, какие есть, а какие нет.
Я считаю, что размер таблицы User в Meeting Planner влияет на общую производительность службы. Я не хотел импортировать контакты, которые могут быть неактуальными в таблицу пользователей.
Это создало некоторые сложности как в UX, так и в коде, когда люди ищут и получают доступ к своим друзьям в сервисе.
В конце концов я решил создать отдельную таблицу для контактов и показать ее отдельно в пользовательском интерфейсе.
Выбор участников для встреч
Все это приводит к тому, что людям будет проще добавлять друзей из своих контактов, просто набрав первые несколько символов. Я использую виджет Typeahead во всплывающем окне « Добавить участников» , показанном ниже:
После того, как я импортировал свои контакты Google, они интегрированы с моими друзьями (людьми, которых я уже пригласил на встречи или пригласил).
В этом случае я начал набирать sar, и появилась целая куча имен Sar-префиксов :
Поиск кого-либо для приглашения на собрание из ваших контактов 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 API
Возможно, вы помните страницу учетных данных Google в учебнике, упомянутом выше:
Вы можете найти его в консоли разработчиков Google .
Вы должны добавить URL-адреса для всех ваших сред — разработки, подготовки, производства и т. Д. — и для каждого контроллера и метода. Это усложняет усилия по работе с его 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.’);
}
|
Мне пришлось добавить много ошибок управления, чтобы выяснить, почему это не работает, и заставить все это работать последовательно.
Как вы уже догадались, в случае возникновения ошибки я часто перенаправляю на тот же метод контроллера.
Обработка возвращенных данных
Когда все работает, забавный, легкий код обрабатывает данные. В настоящее время мы собираем 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» .