Advertisement
  1. Code
  2. Yii

Construire votre démarrage: Importer avec l'API Google Contacts

Scroll to top
Read Time: 14 min
This post is part of a series called Building Your Startup With PHP.
Building Your Startup: Completing Group Scheduling
Building Your Startup: Error Logging

() translation by (you can also view the original English article)

Final product imageFinal product imageFinal product image
What You'll Be Creating

Ce tutoriel fait partie de la série Construire votre démarrage avec PHP sur Envato Tuts +. Dans cette série, je vous guide dans le lancement d'une startup du concept à la réalité, en utilisant mon application Meeting Planner comme exemple concret. À chaque étape du processus, je publierai le code de Meeting Planner sous forme d’exemples open source à partir desquels vous pourrez apprendre. J'aborderai également les problèmes commerciaux liés au démarrage à mesure qu'ils surviennent.

introduction

Bonjour. Aujourd'hui, je vais vous expliquer comment j'ai utilisé l'API Google pour importer les contacts des personnes dans Meeting Planner. L'objectif est de permettre aux personnes d'inviter leurs amis à des réunions plus rapidement.

Si vous n'avez pas essayé de planifier une réunion avec Meeting Planner, essayez-le. Si vous utilisez votre compte Google pour vous inscrire, vous pouvez visiter la page Amis ci-dessus et importer vos contacts Google. Partagez vos opinions et vos commentaires dans les commentaires ci-dessous.

Je participe aux discussions, mais vous pouvez également me joindre à @reifman sur Twitter (mon compte a récemment été vérifié. Je dois donc être aussi cool que Justin Beebert (note aux dieux de la rédaction: j'ai confiance en cette orthographe. Je pense que c'est juste. Laissez-le.) Je suis toujours ouvert aux nouvelles idées de fonctionnalités de Meeting Planner, ainsi qu’aux suggestions d’épisodes de séries futures.

Pour rappel, tout le code de Meeting Planner est fourni en source ouverte et écrit dans le framework Yii2 pour PHP. Si vous souhaitez en savoir plus sur Yii2, consultez mes séries parallèles Programming With Yii2.

Penser à l'intégration de Google Contacts

La page des amis

De nombreuses personnes ont des milliers de contacts dans leur compte Google et peu d’entre elles sont importantes pour elles. Mais, pour la plupart, il n'y a aucun moyen de discerner lesquels sont et lesquels ne le sont pas.

Je pense que la taille de la table des utilisateurs dans Meeting Planner influe sur les performances globales du service. Je ne voulais pas importer des contacts qui pourraient ne jamais être pertinents dans la table des utilisateurs.

Cela a créé des complications à la fois dans le code UX et dans le code, les utilisateurs cherchant à accéder à leurs amis via le service.

J'ai finalement décidé de créer une table séparée pour les contacts et de l'afficher séparément dans l'interface utilisateur pour le moment.

Building Startups Google Contacts API - List of Imported Contacts on Friends PageBuilding Startups Google Contacts API - List of Imported Contacts on Friends PageBuilding Startups Google Contacts API - List of Imported Contacts on Friends Page

Choix des participants pour les réunions

Tout cela conduit à dire qu'il sera plus facile pour les gens d'ajouter des amis à partir de leurs contacts en tapant simplement les premiers caractères. J'utilise un widget Typeahead dans la fenêtre Ajouter des participants ci-dessous:

Building Startups Google Contacts API - Add a Participant FormBuilding Startups Google Contacts API - Add a Participant FormBuilding Startups Google Contacts API - Add a Participant Form

Après avoir importé mes contacts Google, ils sont intégrés à mes amis (personnes déjà invitées à des réunions ou invitées par).

Dans ce cas, j’ai commencé à taper sar et beaucoup de noms de préfixes Sar apparaissent:

Building Startups Google Contacts API - Dropdown from Friend and Address TableBuilding Startups Google Contacts API - Dropdown from Friend and Address TableBuilding Startups Google Contacts API - Dropdown from Friend and Address Table

Il est très simple et rapide de trouver quelqu'un à inviter à une réunion à partir de vos contacts Google (jusqu'à ce que vous en ajoutiez beaucoup, ce que je mentionnerai plus bas).

Problèmes de confidentialité

Je ne veux pas non plus abuser des fiducies des gens en abusant de leurs milliers de contacts. Pour le moment, nous n'offrirons même pas aux utilisateurs la possibilité d'inviter tous leurs contacts Google dans Meeting Planner, bien que nous puissions le proposer ultérieurement. Nous ne leur enverrons certainement pas d'e-mails en masse sans autorisation.

Écrire le code

Si vous ne l'avez pas encore fait, jetez un coup d'œil à Construire votre startup avec PHP: Simplifier Onramp avec OAuth. C'est l'épisode où j'ai authentifié pour la première fois les API Google pour la connexion et l'inscription OAuth.

Google fait très attention à la sécurité avec ses API. À la lumière de ce qui est arrivé récemment avec les hacks de Yahoo, je l’apprécie plus profondément. Cependant, cela rend leurs API plus difficiles que d’autres à s’authentifier et à travailler avec.

En fait, j’ai constaté que l’API Google Contacts présentait certaines des opérations les plus déroutantes, frustrantes et difficiles que j’ai eu à écrire. Et les API de Google défavorisent les programmeurs PHP - nous sommes les derniers à obtenir un exemple de code.

Plongeons dedans.

Création de la table d'adresses

Puisqu'il existe déjà une table UserContact pour les adresses de téléphone et Skype de l'utilisateur, j'ai décidé d'appeler l'adresse de table. Voici la migration pour le créer:

1
<?php
2
use yii\db\Schema;
3
use yii\db\Migration;
4
5
class m160904_001244_create_table_address extends Migration
6
{
7
    public function up()
8
    {
9
        $tableOptions = null;
10
        if ($this->db->driverName === 'mysql') {
11
            $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
12
        }
13
14
        $this->createTable('{{%address}}', [
15
            'id' => Schema::TYPE_PK,
16
            'user_id' => Schema::TYPE_BIGINT.' NOT NULL',
17
            'status' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0',
18
            'firstname' =>Schema::TYPE_STRING.' NOT NULL',
19
            'lastname' =>Schema::TYPE_STRING.' NOT NULL',
20
            'fullname' =>Schema::TYPE_STRING.' NOT NULL',
21
            'email' =>Schema::TYPE_STRING.' NOT NULL',
22
            'created_at' => Schema::TYPE_INTEGER . ' NOT NULL',
23
            'updated_at' => Schema::TYPE_INTEGER . ' NOT NULL',
24
        ], $tableOptions);
25
        $this->addForeignKey('fk_address_user_id', '{{%address}}', 'user_id', '{{%user}}', 'id', 'CASCADE', 'CASCADE');
26
    }
27
28
    public function down()
29
    {
30
      $this->dropForeignKey('fk_address_user_id', '{{%address}}');
31
      $this->dropTable('{{%address}}');
32
    }
33
  }

Bien sûr, j'ai utilisé Giii de Yii pour m'aider avec le contrôleur, le modèle et les vues. Cela a été couvert plus tôt dans la série de démarrage.

Extension de l'authentification Google API

Vous vous souvenez peut-être de la page d'informations d'identification Google dans le tutoriel mentionné ci-dessus:

Building Startups Google Contacts API - Google API Credentials FormBuilding Startups Google Contacts API - Google API Credentials FormBuilding Startups Google Contacts API - Google API Credentials Form

Vous pouvez le trouver dans Google Developers Console.

Vous devez ajouter des URL pour tous vos environnements (développement, transfert, production, etc.) et pour chaque contrôleur et méthode. Cela complique les efforts pour travailler avec son API de contact, mais sécurise probablement mieux les données des personnes.

Building Startups Google Contacts API - Google API Credentials Form lower partBuilding Startups Google Contacts API - Google API Credentials Form lower partBuilding Startups Google Contacts API - Google API Credentials Form lower part

Importer des contacts Google

Voici l'API Google Contacts v3.0. Lorsque j'ai commencé à écrire le code, je n'avais pas remarqué qu'ils recommandaient maintenant l'API People pour un accès en lecture seule. Malheureusement, mon code utilise l'API en lecture / écriture. Ok, alors je ne suis pas un génie. Les entrepreneurs le sont rarement - même Bill Gates dit qu'il a eu de la chance.

En général, j'ai trouvé que l'API Google Contacts était l'une des API les plus confuses et difficiles que j'aie jamais utilisées.

Si j'avais un niveau d'expertise supérieur dans le développement d'API Google ou si je passais plus de temps à travailler dessus, j'aurais peut-être trouvé une approche plus simple. Mais, à ce que je sache, il était important de tout faire avec l'API à partir d'une URL. Dans mon cas, https://meetingplanner.io/address/import.

De plus, Google renvoie les clés et vous redirige de manière répétée vers cette URL. Vous devez donc surveiller l'état de l'API et vous en occuper.

Je suppose que tout est fait pour renforcer la sécurité, mais cela nécessite une gestion des états intégrée à ce qui serait autrement des demandes d'API simples. La gestion des états permet de gagner du temps, mais uniquement si la documentation et l'exemple de code sont corrects. Dans ce cas, pour PHP, ils ne le sont pas.

Commencer

Regardons AddressController.php actionImport():

1
public function actionImport() {
2
      // imports contacts from the google api

3
      $address = new Address();
4
      // create session cookies

5
      $session = Yii::$app->session;
6
      // if we request code reset, then remove the google_code cookie

7
      // i.e. if google_code expires

8
      if (isset($_GET['reset']) && !$session->has('google_code_reset')) {
9
        // prevent loops

10
        $session->set('google_code_reset');
11
        // reset the google_code

12
        $session->remove('google_code');
13
        $this->redirect(['import']);
14
      }

Ci-dessus, je recherche si Google souhaite réinitialiser ses jetons API. Dans ce cas, je supprime les cookies dans lesquels je les ai stockés et je les redirige vers la méthode pour recommencer.

Faire la demande d'un jeton

Ci-dessous, je fais ma première demande à Google via sa bibliothèque cliente PHP:

1
// always remove the reset request cookie

2
      $session->remove('google_code_reset');
3
      // build the API request

4
      $redirect_uri=Url::home(true).'address/import';
5
      $session->open();
6
      $client = new \Google_Client();
7
      $client -> setApplicationName('Meeting Planner');
8
      $client -> setClientid( Yii::$app->components['authClientCollection']['clients']['google']['clientId']);
9
      $client -> setClientSecret(Yii::$app->components['authClientCollection']['clients']['google']['clientSecret']);
10
      $client -> setRedirectUri($redirect_uri);
11
      $client -> setAccessType('online');
12
      $client -> setScopes('https://www.google.com/m8/feeds');
13
      $googleImportUrl = $client -> createAuthUrl();

La bibliothèque Google PHP est en version bêta. En fait, PHP est généralement une réflexion après coup pour Google. Il n'est donc pas toujours facile de travailler en PHP avec leurs API.

Notez ci-dessus que $redirect_uri de Google est encore la même méthode: 'adresse/importer'

Ensuite, nous essayons de placer le jeton du paramètre de requête dans un cookie:

1
// moves returned code to session variables and returns here

2
      if (isset($_GET['code']))
3
        {
4
      	$auth_code = $_GET['code'];
5
        $session->set('google_code',$auth_code);
6
    		header('Location: '.Url::home(true).'address/import');
7
        // do not remove - breaks the API

8
        exit; // do not replace with yii app end

9
        // do not remove above exit

10
    	} else {
11
        $session_code = $session->get('google_code');
12
        if (!isset($session_code)) {
13
            $this->redirect( $googleImportUrl);
14
        }
15
      }

Si vous obtenez le code de Google, vous devez le définir dans le cookie et rediriger à la page. C'est étrange pour moi.

Heureusement, j’ai également constaté que si je ne créais pas de boucle lorsque le code était manquant - pour revenir à la même page, cela ne fonctionnerait pas toujours.

Nous construisons ensuite et demandons à Google de présenter à l'utilisateur une boîte de dialogue d'autorisation permettant à Meeting Planner d'accéder à ses contacts:

1
if (isset($session_code)) {
2
        $auth_code = $session_code;
3
          $fields=array(
4
	        'code'=>  urlencode($auth_code),
5
	        'client_id'=>  urlencode(Yii::$app->components['authClientCollection']['clients']['google']['clientId']),
6
	        'client_secret'=>  urlencode(Yii::$app->components['authClientCollection']['clients']['google']['clientSecret']),
7
	        'redirect_uri'=>  urlencode($redirect_uri),
8
	        'grant_type'=>  urlencode('authorization_code'),
9
	    );
10
      // Requests the access token

11
	    $post = '';
12
	    foreach($fields as $key=>$value)
13
	    {
14
	        $post .= $key.'='.$value.'&';
15
	    }
16
	    $post = rtrim($post,'&');
17
	    $result = $address->curl('https://accounts.google.com/o/oauth2/token',$post);
18
	    $response =  json_decode($result);
19
      if (isset($response->error)) {
20
          if ($response->error_description == 'Code was already redeemed.') {
21
            $session->remove('google_code');
22
            return $this->redirect(['import']);
23
          }
24
          if ($response->error_description == 'Invalid code.') {
25
            $session->remove('google_code');
26
            return $this->redirect(['import']);
27
          }
28
          var_dump($response);
29
          echo Yii::t('frontend','There was an error. Please contact support.');
30
      }
31
      if (isset($response->access_token) || empty($response->access_token)) {
32
          $accesstoken = $response->access_token;
33
      } else {
34
        echo Yii::t('frontend','There was an error. No access token. Please contact support.');
35
      }
Building Startups Google Contacts API - Google Asks User for PermissionsBuilding Startups Google Contacts API - Google Asks User for PermissionsBuilding Startups Google Contacts API - Google Asks User for Permissions

J'ai dû ajouter beaucoup de gestion des erreurs pour comprendre pourquoi cela ne fonctionnait pas et pour que tout cela fonctionne de manière cohérente.

Vous l'avez deviné, s'il y a une condition d'erreur, je redirigerais souvent vers cette même méthode de contrôleur.

Traitement des données renvoyées

Lorsque tout fonctionne, le code simple et amusant permet de traiter les données. Actuellement, nous récupérons cinq fois 1 000 entrées, en construisant à plusieurs reprises des demandes de pagination qui, bien sûr, sont renvoyées à cette URL:

1
$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;
2
$xmlresponse =  $address->curl($url);

Traduire le XML de Google (également complexe, avec des noms de variable impairs pour les développeurs PHP, par exemple des clés telles que $contact['gd$email'][0] ['adresse'] avec un signe dollar au milieu.

Ci-dessous, nous effectuons chaque demande, analysons les données JSON et récupérons les noms des contacts à ajouter à la table Address:

1
// Requests the data

2
      $startIndex = 1;
3
      $request_data = true;
4
      $max_results = Address::CONTACTS_PAGE_SIZE;
5
      $numberPages = 0;
6
      while ($request_data && $numberPages <5) {
7
         //echo 'calling with startIndex: '.$startIndex.'<br />';

8
         $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;
9
         $xmlresponse =  $address->curl($url);
10
         $contacts = json_decode($xmlresponse,true);
11
         if (!isset($contacts['feed']['entry'])) {
12
           //var_dump ($url);

13
           //var_dump ($xmlresponse);

14
           exit;
15
         }
16
        $resultsCount =count($contacts['feed']['entry']);
17
        //echo 'count: '.$resultsCount.'<br />';

18
         //var_dump (count($contacts['feed']['entry']));

19
         // process out contacts without email addresses

20
         //$return = array();

21
         if ($resultsCount>0) {
22
           foreach($contacts['feed']['entry'] as $contact) {
23
             if (isset($contact['gd$email'])) {
24
               $temp = array (
25
                 'firstname' => (isset($contact['gd$name']['gd$givenName']['$t'])?$contact['gd$name']['gd$givenName']['$t']:''),
26
                 'lastname' => (isset($contact['gd$name']['gd$familyName']['$t'])?$contact['gd$name']['gd$familyName']['$t']:''),
27
                 'fullname'=> $contact['title']['$t'],
28
                 'email' => $contact['gd$email'][0]['address'],
29
               );
30
               //$return[]=$temp;

31
               $address->add($temp);
32
             } else {
33
               continue;
34
             }
35
           }
36
           if ($resultsCount<$max_results) {
37
             Yii::$app->getSession()->setFlash('success', Yii::t('backend','Your contacts have been imported.'));
38
             return $this->redirect(['/friend','tab'=>'address']);
39
           }
40
         }
41
         //var_dump($return);

42
         $numberPages++;
43
         $startIndex+=$max_results;
44
       }

Travailler avec l'API Google Contacts était très difficile, pas bien documenté et m'a fait perdre beaucoup de temps. Bien que j'ai travaillé avec beaucoup d'API avec succès, je sais que je ne suis pas un expert dans ce domaine. Pour des raisons de sécurité, je ne veux pas vraiment critiquer Google à ce sujet. Dans de nombreux cas, ils savent probablement pourquoi ils agissent de différentes manières.

Mais c'est correct de s'amuser un peu, non?

Tout d’abord, tout le monde chez Google est un génie, et l’a encore prouvé en acquérant API.ai, un service génial qui relie son bouton d’enregistrement à son formulaire de connexion. Vraiment, ils ont fait:

Je suis sûr que l'équipe de Google chargée de la diligence raisonnable a constaté un génie qui reste au-delà des mortels comme moi. Ils ont dû dire: "Waouh, les programmeurs API.ai sont des génies comme nos équipes AdSense et DFP! Ajoutons-les à l'alphabet!"

Puisqu'il est possible que je parle de Meeting Planner à des investisseurs providentiels et de capital-risque à l'avenir, je veux être humble. Mais je serais horrifié si ma page d'accueil le faisait et qu'un de mes investisseurs potentiels le remarquait.

Le nouvel Alphabet (la nouvelle société mère de Google) pardonne tellement.

Extension du formulaire Ajouter des participants

Pour finir, examinons simplement le code derrière le formulaire Ajouter un participant étendu. Fondamentalement, je récupère des courriels de la table Friends, puis de la table Address:

1
$friendsEmail=[];
2
    $friendsId=[];
3
    $fq = Friend::find()->where(['user_id'=>Yii::$app->user->getId()])->all();
4
    // to do - add a display name field for right side of input

5
    $fa = Address::find()
6
      ->select(['id','email'])
7
      ->where(['user_id'=>Yii::$app->user->getId()])
8
      ->limit(5000)
9
      ->all();
10
    foreach ($fq as $f) {
11
      $friendsEmail[]=$f->friend->email; // get constructed name fields

12
      $friendsId[]=$f->id;
13
    }
14
    foreach ($fa as $f) {
15
      $friendsEmail[]=$f->email; // get constructed name fields

16
      $friendsId[]=$f->id;
17
    }
18
    if (count($friendsEmail)>0) {
19
      ?>
20
      <p><strong>Choose From Your Friends</strong></p>
21
      <select class="combobox input-large form-control" id="participant-email" name="Participant[email]">
22
      <option value="" selected="selected"><?= Yii::t('frontend','type or click to choose friends') // chg meetingjs if change string ?></option>

23
      <?php
24
      foreach ($friendsEmail as $email) {
25
      ?>
26
        <option value="<?= $email;?>"><?= $email;?></option>
27
      <?php
28
        }
29
      ?>
30
      <?php
31
    }
32
    ?>
33
    </select>

Cependant, la valeur de l’option n’est que l’e-mail, car il serait devenu plus compliqué de désigner le type d’ami utilisé (de la table Friend ou de la table Address).

Je ne suis pas très fier du code et de l'approche ci-dessus, mais je me suis précipité vers la version bêta, j'ai fait des compromis pour y arriver.

Avec 5 000 contacts Google dans la liste déroulante de mes amis, les performances sont plus lentes. J'ai probablement besoin de mieux lier le contrôle à une recherche de base de données AJAX bientôt.

Et j’ai perdu beaucoup de temps au début à essayer d’étendre la table des amis aux personnes que j’avais invitées et à Google Contacts. Cependant, cela s'est transformé en un fouillis de requêtes de base de données connexes difficiles à gérer. Les relations de la table User de la table Friends ont commencé à se rompre pour les lignes Contacts où elles seraient nulles, ce qui s'est avéré très difficile à résoudre. La gestion de la suppression des clés étrangères existantes de haut en bas lors des migrations est également déloyale.

Pensées finales

Ces fonctionnalités sont d'excellents exemples des défis que représentent l'utilisation d'API avec une documentation médiocre dans la langue de votre choix et des compromis d'architecture de code pour le moment, afin de lancer les fonctionnalités selon un calendrier de publication (en choisissant de ne pas les couper).

Et il reste certainement des problèmes avec la performance d’ajout du participant et la page d’enregistrement Amis qui doivent être résolus.

Honnêtement, la portée de Meeting Planner a atteint un point où il est difficile de le faire en une seule personne. Et il serait utile d’avoir plus de ressources (par exemple, les membres de l’équipe).

Enfin, si ce n'est déjà fait, rendez-vous dès maintenant pour votre première réunion avec Meeting Planner! Faites-moi savoir ce que vous pensez dans les commentaires ci-dessous. Vous pouvez également me contacter @reifman. Je suis toujours ouvert aux nouvelles idées de fonctionnalités et suggestions de sujets pour les prochains tutoriels.

Un tutoriel sur le financement participatif est également en préparation. Veuillez suivre notre page WeFunder Meeting Planner.

Restez à l'affût de tout cela et de plusieurs autres tutoriels à venir en consultant la série Construire son démarrage avec PHP.

Liens connexes

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.