Using Google Contacts API as server to server PHP application

You've got a website for your business and most of your new customers are reaching you via your "Contact us" form page. You're probably manually adding your customers' phone number, email, their preferred product, etc. in your Google Contacts. Would it be nice if your "Contact us" form page has the ability to save a visitor's contact and other details to the database of your account's Google Contacts app automatically? With this kind of application, server to server authentication is needed. Where your application has own Google service account instead of to an individual visitor's Google account. The application calls Google Contacts API on behalf of the Google service account, so your visitors aren't directly involved. Unfortunately, you will need Google's suite of intelligent apps (formerly Google Apps) here, as one of the main step in this article is to delegate domain-wide authority to your application's Google service account.

As of this writing, Google Contacts API don't have support for PHP yet. We will use the Google APIs Client Library for PHP as base code and we will use composer to get it.

Create a Google service account

  1. Login to your Google account.

  2. Create a project here.

    Create project

  3. Populate the "Project name" and "Project ID" fields and click "CREATE" button:

    Create project window

  4. Wait until Google finished creating the project, you will be redirected to a new page (like the one shown below) once it is completed. Under "Google Apps API", click "Contacts API" link:

    Google API list

  5. You should be redirected to "Contacts API" page. Click the "ENABLE" button:

    Contacts API enable

  6. Still on "Contacts API" page, click "Go to Credentials" button at the right side:

    Create credetials

  7. You will be redirected to "Credentials" page. Select "Web server (e.g. node.js, Tomcat)" option from "Where will you be calling the API from?" field. Choose "Application data" from "What data will you be accessing?" field. Select "No, I’m not using them" option from "Are you using Google App Engine or Google Compute Engine?" field. Then click "What credentials do I need?" button:

    Create credentials page

  8. Populate the "Service account name" field. Click the "Role" dropdown menu, from the selection choose "Project" then "Editor". Click "Continue" button:

    Create credentials account

  9. You should be receiving a JSON file that contains private key of Google service account you've just created. Click "Ok" button of the popped alert box. And you will be redirected to "Credentials" main page. Click the "Manage service accounts" link:

    Manage service account

  10. On "Service Accounts" page, click the "More actions" icon at the right of your Google service account and select "Edit" from the dropdown menu:

    Manage service account options

  11. A window will pop up. Tick the "Enable G Suite Domain-wide Delegation" checkbox and populate the "Product name for the consent screen" text field:

    Manage service account edit

Delegate domain-wide authority to Google service account

  1. Login to your Google's suite or Google Apps with your account that has administrator role here.

  2. Click "Security" from your "Admin Console" page:

    Manage security

  3. You will be redirected to "Security" page, click "Show more" to show the "Advanced settings":

    Manage security page

  4. Still on "Security" page under "Advanced settings", click "Manage API client access" link:

    Manage security advanced settings

  5. You will be redirected to "Manage API client access" page. Open the JSON file that you received from creating your Google service account and copy the value of client_id:

    Client ID

  6. Paste the client_id that you copied to "Client Name" field and populate the "One or More API Scopes" with "https://www.google.com/m8/feeds":

    Manage security client access

    Note: If you want to add more API scopes check https://developers.google.com/identity/protocols/googlescopes.

  7. Click the "Authorize" button:

    Manage security client access saved

Google Contacts API PHP application

  1. Go to your web page root path:

          
    cd /var/www/html
          
        
  2. Create a directory for our Google Contacts application:

          
    mkdir googlecontacts
    cd googlecontacts
          
        
  3. Copy the JSON file that you received from creating your Google service account to /var/www/html/googlecontacts and rename it to "google_auth.json":

  4. Install composer:

          
    curl -sS https://getcomposer.org/installer | php
    mv composer.phar /usr/local/bin/composer
          
        

    Follow this instructions installing composer in Windows.

  5. Execute the following to download the Google APIs Client Library for PHP:

          
    composer require google/apiclient:^2.0
          
        
  6. Create a PHP file named "GoogleServiceAuthApplication.php" and copy the following codes in to it:

          
    <?php
    
    namespace GoogleCustom;
    
    use Google_Client;
    
    class GoogleServiceAuthApplication {
      /**
       * Google client.
       *
       * @var array
       */
      protected $client;
    
      public function __construct($conf) {
        if (isset($conf['google_auth_file'])) {
          putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $conf['google_auth_file']);
        }
        $this->client = new Google_Client();
        $this->client->useApplicationDefaultCredentials();
        $this->client->setScopes($conf['gcontacts_scopes']);
        $this->client->setSubject($conf['gcontacts_account']);
      }
    
      /**
       * @return array
       */
      public function getClient() {
        return $this->client;
      }
    }
          
        

    The above "GoogleServiceAuthApplication" class is responsible for authenting Google service account.

  7. Lets create PHP class that will query, create, update and delete Google Contacts entry. Create a PHP file named "GoogleContacts.php" and copy the following codes in to it:

          
    <?php
    
    namespace GoogleCustom;
    
    include_once 'GoogleServiceAuthApplication.php';
    
    
    class GoogleContacts {
      /**
       * Request object.
       *
       * @var \GuzzleHttp\ClientInterface
       */
      protected $httpClient;
    
      protected $baseUrl;
    
      protected $etag;
    
      protected $userId;
    
      protected $xmlString;
    
      protected $xmlData;
    
      protected $conf;
    
      public function __construct($conf) {
        $this->conf = $conf;
        $this->baseUrl = $conf['gcontacts_base_url'];
        $auth = new GoogleServiceAuthApplication($conf);
        $client = $auth->getClient();
        $this->httpClient = $client->authorize();
      }
    
      /**
       * Create new Google Contacts entry
       * 
       * @param array $data
       *   Contains flat array of new details to update a Google Contacts entry.
       * @return bool
       *   Flat array Google Contacts entry details if successful and FALSE if failed.
       */
      public function create($data) {
        extract($data);
        $xmlString = <<<XML
      <atom:entry xmlns:atom="http://www.w3.org/2005/Atom" xmlns:gd="http://schemas.google.com/g/2005">
        <atom:category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/contact/2008#contact"/>
        <gd:name>
          <gd:fullName>$fullName</gd:fullName>
          <gd:namePrefix>$namePrefix</gd:namePrefix>
          <gd:givenName>$givenName</gd:givenName>
          <gd:additionalName>$additionalName</gd:additionalName>
          <gd:familyName>$familyName</gd:familyName>
          <gd:nameSuffix>$nameSuffix</gd:nameSuffix>
        </gd:name>
        <gContact:birthday when="$birthday"/>
        <atom:content type="text">$content</atom:content>
        <gd:email rel="http://schemas.google.com/g/2005#home" address="$emailHome" />
        <gd:email rel="http://schemas.google.com/g/2005#work" primary="true" address="$emailWork" />
        <gd:phoneNumber rel="http://schemas.google.com/g/2005#home">$phoneNumberHome</gd:phoneNumber>
        <gd:phoneNumber rel="http://schemas.google.com/g/2005#mobile">$phoneNumberMobile</gd:phoneNumber>
        <gd:phoneNumber rel="http://schemas.google.com/g/2005#work">$phoneNumberWork</gd:phoneNumber>
        <gd:structuredPostalAddress rel="http://schemas.google.com/g/2005#home" primary="true">
          <gd:formattedAddress>$formattedAddress</gd:formattedAddress>
          <gd:street>$street</gd:street>
          <gd:pobox>$pobox</gd:pobox>
          <gd:neighborhood>$neighborhood</gd:neighborhood>
          <gd:city>$city</gd:city>
          <gd:region>$region</gd:region>
          <gd:postcode>$postcode</gd:postcode>
          <gd:country>$country</gd:country>
        </gd:structuredPostalAddress>
        <gContact:userDefinedField key="Most convenient time to call" value="$mostConvenientTimeToCall"/>
        <gContact:userDefinedField key="Preferred product" value="$preferredProduct"/>
        <gContact:groupMembershipInfo deleted="false" href="$group"/>
      </atom:entry>
    XML;
        $options = [
          'headers' => [
            'Content-Type' => 'application/atom+xml',
            'GData-Version' => '3.0',
          ],
          'body' => $xmlString,
        ];
        $response = $this->httpClient->post($this->baseUrl, $options);
        if ($response->getReasonPhrase() == 'Created' && $response->getStatusCode() == 201) {
          // Successful
          $xmlString = $response->getBody()->getContents();
          $data = new \SimpleXMLElement($xmlString);
          $this->xmlString = $xmlString;
          $this->xmlData = $data;
          return $this->flatData($data);
        }
        else {
          // Failed
          return FALSE;
        }
      }
    
      /**
       * Update a Google Contacts entry
       * 
       * @param sting $query
       *   Fulltext query on contacts data fields.
       * @param array $data
       *   Contains flat array of new details to update a Google Contacts entry.
       * @return mixed
       *   Flat array Google Contacts entry details if successful and FALSE if failed.
       */
      public function update($query, $data) {
        $this->query($query);
        if (!empty($data['content'])) {
          $this->xmlData->content = $data['content'];
        }
        if (!empty($data['fullName'])) {
          $this->xmlData->children('gd', TRUE)->name->children('gd', TRUE)->fullName = $data['fullName'];
        }
        if (!empty($data['namePrefix'])) {
          $this->xmlData->children('gd', TRUE)->name->children('gd', TRUE)->namePrefix = $data['namePrefix'];
        }
        if (!empty($data['givenName'])) {
          $this->xmlData->children('gd', TRUE)->name->children('gd', TRUE)->givenName = $data['givenName'];
        }
        if (!empty($data['additionalName'])) {
          $this->xmlData->children('gd', TRUE)->name->children('gd', TRUE)->additionalName = $data['additionalName'];
        }
        if (!empty($data['familyName'])) {
          $this->xmlData->children('gd', TRUE)->name->children('gd', TRUE)->familyName = $data['familyName'];
        }
        if (!empty($data['nameSuffix'])) {
          $this->xmlData->children('gd', TRUE)->name->children('gd', TRUE)->nameSuffix = $data['nameSuffix'];
        }
        if (!empty($data['birthday'])) {
          $this->xmlData->children('gContact', TRUE)->birthday->attributes()->when = $data['birthday'];
        }
        $index = 0;
        foreach ($this->xmlData->children('gd', TRUE)->email as $email) {
          $rel = (string) $email->attributes()->rel;
          if (!empty($data['emailWork']) && strstr($rel, 'work') !== FALSE) {
            $this->xmlData->children('gd', TRUE)->email[$index]->attributes()->address = $data['emailWork'];
          }
          elseif (!empty($data['emailHome']) && strstr($rel, 'home') !== FALSE) {
            $this->xmlData->children('gd', TRUE)->email[$index]->attributes()->address = $data['emailHome'];
          }
          ++$index;
        }
        $index = 0;
        foreach ($this->xmlData->children('gd', TRUE)->phoneNumber as $phoneNumber) {
          $rel = (string) $phoneNumber->attributes()->rel;
          if (!empty($data['phoneNumberMobile']) && strstr($rel, 'mobile') !== FALSE) {
            $this->xmlData->children('gd', TRUE)->phoneNumber[$index] = $data['phoneNumberMobile'];
          }
          elseif (!empty($data['phoneNumberWork']) && strstr($rel, 'work') !== FALSE) {
            $this->xmlData->children('gd', TRUE)->phoneNumber[$index] = $data['phoneNumberWork'];
          }
          elseif (!empty($data['phoneNumberHome']) && strstr($rel, 'home') !== FALSE) {
            $this->xmlData->children('gd', TRUE)->phoneNumber[$index] = $data['phoneNumberHome'];
          }
          ++$index;
        }
        if (!empty($data['formattedAddress'])) {
          $this->xmlData->children('gd', TRUE)->structuredPostalAddress->children('gd', TRUE)->formattedAddress = $data['formattedAddress'];    
        }
        if (!empty($data['street'])) {
          $this->xmlData->children('gd', TRUE)->structuredPostalAddress->children('gd', TRUE)->street = $data['street'];    
        }
        if (!empty($data['pobox'])) {
          $this->xmlData->children('gd', TRUE)->structuredPostalAddress->children('gd', TRUE)->pobox = $data['pobox'];    
        }
        if (!empty($data['neighborhood'])) {
          $this->xmlData->children('gd', TRUE)->structuredPostalAddress->children('gd', TRUE)->neighborhood = $data['neighborhood'];    
        }
        if (!empty($data['city'])) {
          $this->xmlData->children('gd', TRUE)->structuredPostalAddress->children('gd', TRUE)->city = $data['city'];    
        }
        if (!empty($data['region'])) {
          $this->xmlData->children('gd', TRUE)->structuredPostalAddress->children('gd', TRUE)->region = $data['region'];    
        }
        if (!empty($data['postcode'])) {
          $this->xmlData->children('gd', TRUE)->structuredPostalAddress->children('gd', TRUE)->postcode = $data['postcode'];    
        }
        if (!empty($data['country'])) {
          $this->xmlData->children('gd', TRUE)->structuredPostalAddress->children('gd', TRUE)->country = $data['country'];
        }
        $index = 0;
        foreach ($this->xmlData->children('gContact', TRUE)->userDefinedField as $userDefinedField) {
          $key = (string) $userDefinedField->attributes()->key;
          if (!empty($data['preferredProduct']) && $key == 'Preferred product') {
            $this->xmlData->children('gContact', TRUE)->userDefinedField[$index]->attributes()->value = $data['preferredProduct'];
          }
          elseif (!empty($data['mostConvenientTimeToCall']) && $key == 'Most convenient time to call') {
            $this->xmlData->children('gContact', TRUE)->userDefinedField[$index]->attributes()->value = $data['mostConvenientTimeToCall'];
          }
          ++$index;
        }
        $this->xmlData->children('gContact', TRUE)->groupMembershipInfo->attributes()->href = $data['group'];
        $options = [
          'headers' => [
            'If-Match' => $this->etag,
            'Content-Type' => 'application/atom+xml',
            'GData-Version' => '3.0',
          ],
          'body' => $this->xmlData->asXML(),
        ];
        $response = $this->httpClient->put("$this->baseUrl/$this->userId", $options);
        if ($response->getReasonPhrase() == 'OK' && $response->getStatusCode() == 200) {
          // Successful
          $xmlString = $response->getBody()->getContents();
          $data = new \SimpleXMLElement($xmlString);
          $this->xmlString = $xmlString;
          $this->xmlData = $data;
          return $this->flatData($data);
        }
        else {
          // Failed
          return FALSE;
        }
      }
    
      protected function requestContact() {
        $targetUrl = "$this->baseUrl/$this->userId?v=3.0";
        $response  = $this->httpClient->get($targetUrl);
        $xmlString = $response->getBody()->getContents();
        $data = new \SimpleXMLElement($xmlString);
        $this->etag = (string) $data->attributes('gd', TRUE)->etag;
        $this->xmlData = $data;
        $this->xmlString = $xmlString;
        return $this->flatData($data);
      }
    
      /**
       * Query Google Contacts
       * 
       * @param sting $query
       *   Fulltext query on contacts data fields.
       * @return mixed
       *   Flat array Google Contacts entry details if successful and FALSE if failed.
       */
      public function query($query) {
        if (!empty($query)) {
          $response   = $this->httpClient->get("$this->baseUrl?v=3.0&q=$query");
          $xmlString  = $response->getBody()->getContents();
          if ($response->getReasonPhrase() == 'OK' && $response->getStatusCode() == 200) {
            $data = new \SimpleXMLElement($xmlString);
            if (isset($data->entry[0]->id)) {
              $this->userId = basename((string) $data->entry[0]->id);
              return $this->requestContact();
            }
          }
        }
        return FALSE;
      }
    
      /**
       * Delete a Google Contacts entry
       * 
       * @param sting $query
       *   Fulltext query on contacts data fields.
       * @return mixed
       *   TRUE if successful and FALSE if failed.
       */
      public function delete($query) {
        if (!empty($query)) {
          $this->query($query);
        }
        if (!empty($this->etag)) {
          $options = [
            'headers' => [
              'If-Match' => $this->etag,
            ],
          ];
          $response =  $this->httpClient->delete("$this->baseUrl/$this->userId", $options);
          if ($response->getReasonPhrase() == 'Precondition Failed' && $response->getStatusCode() == 412) {
            $options = [
              'headers' => [
                'If-Match' => $this->etag,
                'X-HTTP-Method-Override' => 'DELETE',
              ],
            ];
            $response =  $this->httpClient->post("$this->baseUrl/$this->userId", $options);
          }
          if ($response->getReasonPhrase() == 'Precondition Failed' && $response->getStatusCode() == 412) {
            $options = [
              'headers' => [
                'If-Match' => '*',
                'X-HTTP-Method-Override' => 'DELETE',
              ],
            ];
            $response =  $this->httpClient->post("$this->baseUrl/$this->userId", $options);
          }
          if ($response->getReasonPhrase() == 'OK' && $response->getStatusCode() == 200) {
            return TRUE;
          }
        }
        return FALSE;
      }
    
      protected function flatData($data) {
        $flatData = [];
        $flatData['userId'] = basename((string) $data->id);
        $flatData['etag'] = (string) $data->attributes('gd', TRUE)->etag;
        // Convert data to flat array
        if (isset($data->content)) {
          $flatData['content'] = (string) $data->content;
        }
        if (isset($data->children('gd', TRUE)->name->children('gd', TRUE)->fullName)) {
          $flatData['fullName'] = (string) $data->children('gd', TRUE)->name->children('gd', TRUE)->fullName;
        }
        if (isset($data->children('gd', TRUE)->name->children('gd', TRUE)->namePrefix)) {
          $flatData['namePrefix'] = (string) $data->children('gd', TRUE)->name->children('gd', TRUE)->namePrefix;
        }
        if (isset($data->children('gd', TRUE)->name->children('gd', TRUE)->givenName)) {
          $flatData['givenName'] = (string) $data->children('gd', TRUE)->name->children('gd', TRUE)->givenName;
        }
        if (isset($data->children('gd', TRUE)->name->children('gd', TRUE)->additionalName)) {
          $flatData['additionalName'] = (string) $data->children('gd', TRUE)->name->children('gd', TRUE)->additionalName;
        }
        if (isset($data->children('gd', TRUE)->name->children('gd', TRUE)->familyName)) {
          $flatData['familyName'] = (string) $data->children('gd', TRUE)->name->children('gd', TRUE)->familyName;
        }
        if (isset($data->children('gd', TRUE)->name->children('gd', TRUE)->nameSuffix)) {
          $flatData['nameSuffix'] = (string) $data->children('gd', TRUE)->name->children('gd', TRUE)->nameSuffix;
        }
        if (isset($data->children('gContact', TRUE)->birthday)) {
          $flatData['birthday'] = (string) $data->children('gContact', TRUE)->birthday;
        }
        if (isset($data->children('gd', TRUE)->email)) {
          $index = 0;
          foreach ($data->children('gd', TRUE)->email as $email) {
            $rel = (string) $email->attributes()->rel;
            if (strstr($rel, 'work') !== FALSE && isset($data->children('gd', TRUE)->email[$index]->attributes()->address)) {
              $flatData['emailWork'] = (string) $data->children('gd', TRUE)->email[$index]->attributes()->address;
            }
            elseif (strstr($rel, 'home') !== FALSE && isset($data->children('gd', TRUE)->email[$index]->attributes()->address)) {
              $flatData['emailHome'] = (string) $data->children('gd', TRUE)->email[$index]->attributes()->address;
            }
            ++$index;
          }
        }
        if (isset($data->children('gd', TRUE)->phoneNumber)) {
          $index = 0;
          foreach ($data->children('gd', TRUE)->phoneNumber as $phoneNumber) {
            $rel = (string) $phoneNumber->attributes()->rel;
            if (strstr($rel, 'mobile') !== FALSE && isset($data->children('gd', TRUE)->phoneNumber[$index])) {
              $flatData['phoneNumberMobile'] = (string) $data->children('gd', TRUE)->phoneNumber[$index];
            }
            elseif (strstr($rel, 'work') !== FALSE && isset($data->children('gd', TRUE)->phoneNumber[$index])) {
              $flatData['phoneNumberWork'] = (string) $data->children('gd', TRUE)->phoneNumber[$index];
            }
            elseif (strstr($rel, 'home') !== FALSE && isset($data->children('gd', TRUE)->phoneNumber[$index])) {
              $flatData['phoneNumberHome'] = (string) $data->children('gd', TRUE)->phoneNumber[$index];
            }
            ++$index;
          }
        }
        if (isset($data->children('gd', TRUE)->structuredPostalAddress->children('gd', TRUE)->formattedAddress)) {
          $flatData['formattedAddress'] = (string) $data->children('gd', TRUE)->structuredPostalAddress->children('gd', TRUE)->formattedAddress;
        }
        if (isset($data->children('gd', TRUE)->structuredPostalAddress->children('gd', TRUE)->street)) {
          $flatData['street'] = (string) $data->children('gd', TRUE)->structuredPostalAddress->children('gd', TRUE)->street;
        }
        if (isset($data->children('gd', TRUE)->structuredPostalAddress->children('gd', TRUE)->pobox)) {
          $flatData['pobox'] = (string) $data->children('gd', TRUE)->structuredPostalAddress->children('gd', TRUE)->pobox;
        }
        if (isset($data->children('gd', TRUE)->structuredPostalAddress->children('gd', TRUE)->neighborhood)) {
          $flatData['neighborhood'] = (string) $data->children('gd', TRUE)->structuredPostalAddress->children('gd', TRUE)->neighborhood;
        }
        if (isset($data->children('gd', TRUE)->structuredPostalAddress->children('gd', TRUE)->city)) {
          $flatData['city'] = (string) $data->children('gd', TRUE)->structuredPostalAddress->children('gd', TRUE)->city;
        }
        if (isset($data->children('gd', TRUE)->structuredPostalAddress->children('gd', TRUE)->region)) {
          $flatData['region'] = (string) $data->children('gd', TRUE)->structuredPostalAddress->children('gd', TRUE)->region;
        }
        if (isset($data->children('gd', TRUE)->structuredPostalAddress->children('gd', TRUE)->postcode)) {
          $flatData['postcode'] = (string) $data->children('gd', TRUE)->structuredPostalAddress->children('gd', TRUE)->postcode;
        }
        if (isset($data->children('gd', TRUE)->structuredPostalAddress->children('gd', TRUE)->country)) {
          $flatData['country'] = (string) $data->children('gd', TRUE)->structuredPostalAddress->children('gd', TRUE)->country;
        }
        if (isset($data->children('gContact', TRUE)->userDefinedField)) {
          $index = 0;
          foreach ($data->children('gContact', TRUE)->userDefinedField as $userDefinedField) {
            $key = (string) $userDefinedField->attributes()->key;
            if ($key == 'Preferred product' && isset($data->children('gContact', TRUE)->userDefinedField[$index]->attributes()->value)) {
              $flatData['preferredProduct'] = (string) $data->children('gContact', TRUE)->userDefinedField[$index]->attributes()->value;
            }
            elseif ($key == 'Time Zone') {
              $flatData['timeZone'] = (string) $data->children('gContact', TRUE)->userDefinedField[$index]->attributes()->value;
            }
            elseif ($key == 'Most convenient time to call' && isset($data->children('gContact', TRUE)->userDefinedField[$index]->attributes()->value)) {
              $flatData['mostConvenientTimeToCall'] = (string) $data->children('gContact', TRUE)->userDefinedField[$index]->attributes()->value;
            }
            ++$index;
          }
        }
        if (isset($data->children('gContact', TRUE)->groupMembershipInfo->attributes()->href)) {
          $flatData['group'] = (string) $data->children('gContact', TRUE)->groupMembershipInfo->attributes()->href;
        }
        $flatData['timestamp'] = time();
        return $flatData;
      }
    }
          
        
  8. The following script is a simple application of Google Contacts API that demonstrate create, update, query and delete Google Contacts entry. It contains a working HTML form asking information based on basic Google Contacts fields: Name (Prefix, First, Middle, Last and Suffix), Phone number (Home, Mobile and Work), Email (Home and Work), Address (Street, PO Box, Neighborhood, City, State/Province, ZIP/Postal, Country/Region), Birthday and also includes two Google custom fields "Most convenient time to call" and "Preferred product". Create a PHP file named "app.php" and copy the following codes in to it (Note: the variable $googleSubjectAccount in the script below should have a value of your Google email from Google Apps, this email will receive and store the contact details of your visitor in its Google Contacts database):

          
    <?php
    if (!empty($_POST)) {
      include_once 'vendor/autoload.php';
      include_once 'GoogleContacts.php';
      
      $googleSubjectAccount = '';
      $defaultGroup = "http://www.google.com/m8/feeds/groups/$googleSubjectAccount/base/6";
      $conf = [
        'google_auth_file' => 'google_auth.json',
        'gcontacts_account' => $googleSubjectAccount,
        'gcontacts_base_url' => 'https://www.google.com/m8/feeds/contacts/default/full',
        'gcontacts_scopes' => [
          'https://www.googleapis.com/auth/contacts',
          'https://www.google.com/m8/feeds',
        ],
      ];
      $gcontacts = new \GoogleCustom\GoogleContacts($conf);
      $data = $_POST;
      if (empty($data['fullName'])) {
        $data['fullName'] = $data['namePrefix'] . ' ' . $data['givenName'] . ' ' . $data['additionalName'] . ' ' . $data['familyName'] . ' ' . $data['nameSuffix'];
      }
      $street = empty($data['street']) ? '' : $data['street'] . ', ';
      $neighborhood = empty($data['neighborhood']) ? '' : $data['neighborhood'] . ', ';
      $city = empty($data['city']) ? '' : $data['city'] . ', ';
      $postcode = empty($data['postcode']) ? '' : $data['postcode'] . ' ';
      $region = empty($data['region']) ? '' : $data['region'] . ', ';
      $data['formattedAddress'] = $street . $neighborhood . $city . $postcode . $region . $data['country'];
      $data['birthday'] = date('Y-m-d', strtotime($data['birthday']));
      $data['group'] = $defaultGroup;  
      // Escape the values to XML safe
      foreach ($data as $param => $value) {
        $data[$param] = htmlspecialchars($value, ENT_XML1, 'UTF-8');
      }
      if (isset($_POST['create'])) {
        $response  = $gcontacts->create($data);
        if (!$response) {
          echo '<p style="color: red;">Creation of Google Contacts entry failed.</p>';
        }
        else {
          echo '<p style="color: green;">Creation of Google Contacts entry successful.</p>';
        }
      }
      elseif (isset($_POST['query'])) {
        $response  = $gcontacts->query($_POST['term']);
        if (!$response) {
          echo '<p style="color: red;">Query of Google Contacts entry failed.</p>';
        }
        else {
          echo '<p style="color: green;">Query of Google Contacts entry successful. Below are the data returned from Google:</p>';
          print_r($response);
        }
      }
      elseif (isset($_POST['update'])) {
        $response  = $gcontacts->update($_POST['term'], $data);
        if (!$response) {
          echo '<p style="color: red;">Update of Google Contacts entry failed.</p>';
        }
        else {
          echo '<p style="color: green;">Update of Google Contacts entry successful. Below are the data returned from Google:</p>';
          print_r($response);
        }
      }
      elseif (isset($_POST['delete'])) {
        $response  = $gcontacts->delete($_POST['term']);
        if (!$response) {
          echo '<p style="color: red;">Deletion of Google Contacts entry failed.</p>';
        }
        else {
          echo '<p style="color: green;">Deletion of Google Contacts entry successful.</p>';
        }
      }
    }
    ?>
    <!DOCTYPE html>
    <html>
      <body>
        <form action="app.php" method="post">
          <h3>Name:</h3>
          <div>
            <label for="namePrefix">Prefix:</label>
            <input type="text" name="namePrefix" value="Mr.">
          </div>
          <div>
            <label for="givenName">First:</label>
            <input type="text" name="givenName" value="John">
          </div>
          <div>
            <label for="additionalName">Middle:</label>
            <input type="text" name="additionalName" value="D.">
          </div>
          <div>
            <label for="familyName">Last:</label>
            <input type="text" name="familyName" value="Doe">
          </div>
          <div>
            <label for="nameSuffix">Suffix:</label>
            <input type="text" name="nameSuffix" value="Sr.">
          </div>
          <h3>Phone number:</h3>
          <div>
            <label for="phoneNumberHome">Home:</label>
            <input type="text" name="phoneNumberHome" value="+6328888888">
          </div>
          <div>
            <label for="phoneNumberMobile">Mobile:</label>
            <input type="text" name="phoneNumberMobile" value="+639198888888">
          </div>
          <div>
            <label for="phoneNumberWork">Work:</label>
            <input type="text" name="phoneNumberWork" value="+632777777">
          </div>
          <h3>Email:</h3>
          <div>
            <label for="emailHome">Home:</label>
            <input type="text" name="emailHome" value="john@yahoo.com">
          </div>
          <div>
            <label for="emailWork">Work:
            <input type="text" name="emailWork" value="john@drupal.org"></label>
          </div>
          <h3>Address:</h3>
          <div>
            <label for="street">Street:</label>
            <textarea rows="4" cols="50" name="street" style="display: block;">#101 Arca Drive</textarea>
          </div>
          <div>
            <label for="pobox">PO Box:</label>
            <input type="text" name="pobox" value="">
          </div>
          <div>
            <label for="neighborhood">Neighborhood:</label>
            <input type="text" name="neighborhood" value="">
          </div>
          <div>
            <label for="city">City:</label>
            <input type="text" name="city" value="Manila">
          </div>
          <div>
            <label for="region">State/Province:</label>
            <input type="text" name="region" value="NCR">
          </div>
          <div>
            <label for="postcode">ZIP/Postal Code:</label>
            <input type="text" name="postcode" value="1900">
          </div>
          <div>
            <label for="country">Country/Region:</label>
            <input type="text" name="country" value="Philippines">
          </div>
          <h3>Other:</h3>
          <div>
            <label for="birthday">Birthday:</label>
            <input type="text" name="birthday" value="January 1, 1970">
          </div>
          <div>
            <label for="mostConvenientTimeToCall">Most convenient time to call:</label>
            <input type="text" name="mostConvenientTimeToCall" value="after lunch">
          </div>
          <div>
            <label for="preferredProduct">Preferred product:</label>
            <input type="text" name="preferredProduct" value="Drupal 8 Module development ebook">
          </div>
          <h3>Note:</h3>
          <div>
             <textarea rows="4" cols="50" name="content">
    Hi,
    
    I would like to request more details about Drupal 8 Module development ebook.
    
    Thanks.
             </textarea>
          </div>
          <input type="submit" name="create" value="Create">
          <div>
            <label for="term">Enter term(s) fulltext query on any contacts data fields separated by space (please populate this field for Update, Query and Delete):</label><br />
            <input type="text" name="term" value="john@yahoo.com Doe">
          </div>
          <input type="submit" name="query" value="Query"><br />
          <input type="submit" name="update" value="Update"><br />
          <input type="submit" name="delete" value="Delete"><br />
        </form>
      </body>
    </html>
          
        
  9. Now access the Google Contacts PHP script application to test it. The script should render:

    Google Contacts sample application

Comments

Best tutorial I've seen in a few weeks struggling with Contacts API and Service Accounts! Congrats!
Unfortunately it won't help me as I'm using Python.

Have you successfully updated Suffix and Prefix in Name element?
My batch update silently removes those attributes.

I just tested the codes I shared above. Yes, it successfully updated Suffix and Prefix in Name element. I suggest to try the codes I shared above then modify it little by little.

HP Parse error: syntax error, unexpected '<' in /var/www/html/googlecontacts/GoogleContacts.php on line 47

I'm sorry, there should be at line 47:

  
$xmlString = <<<XML
  

Actually, there is already <<<XML in my original post but my WYSIWYG interpret it as a HTML tag so it does not display correctly.

Thank you for reporting it.

Google wrote: mistake client (4xx). How I can find mistakes? G-Sute should be paied?

Anton, you will need G Suite account and this is not free (https://gsuite.google.com/pricing.html).

Anton, the response messages you received is came from Google server, please read the Google Contacts API documentation (https://developers.google.com/google-apps/contacts/v3/) to learn more about the response messages you received.

Add new comment

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.