Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Google API How to connect to receive values from spreadsheet

I embarked on this project thinking it would be simple. Many hours later I'm realizing the Google API is a bit of a labyrinth with multiple APIs and libraries. I really need please clear directions on how to go about doing this.

I created several Google Doc spreadsheets which I granted permission to other users to edit.

All I need is to programatically retrieve the info from these spreadsheets using PHP. However, I can't figure out how to connect to even start the retrieval.

Here is what I did so far:

1 - Installed the Google PHP API libraries.

2 - Created a Google API project in the same account. I have no idea which API I need and which oAuth keys I need.

3 - Installed the Google API Spreadsheet client from https://github.com/asimlqt/php-google-spreadsheet-client.

Well, now what? How do I send the API command to retrieve the spreadsheet I want. I'm unsure how to authenticate and how to retrieve. So far, I tried the below using the API Server Key for Google Drive....this was just a guess. I copied and pasted the below from the example in the Google API Spreadsheet client:

<?php
require_once 'php-google-spreadsheet-client-master\src\Google\Spreadsheet\Autoloader.php';

$accessToken = 'xxxxxxxxxxxxxxxxxxxxxxx';
$request = new Google\Spreadsheet\Request($accessToken);
$serviceRequest = new Google\Spreadsheet\DefaultServiceRequest($request);
Google\Spreadsheet\ServiceRequestFactory::setInstance($serviceRequest);


$spreadsheetService = new Google\Spreadsheet\SpreadsheetService();
$spreadsheetFeed = $spreadsheetService->getSpreadsheets();
?>

I receive the following error:

Fatal error: Uncaught exception 'Exception' with message 'String could not be parsed as XML' in C:\php\php-google-spreadsheet-client-master\src\Google\Spreadsheet\SpreadsheetFeed.php:43 Stack trace: #0 C:\php\php-google-spreadsheet-client-master\src\Google\Spreadsheet\SpreadsheetFeed.php(43): SimpleXMLElement->__construct('') #1 C:\php\php-google-spreadsheet-client-master\src\Google\Spreadsheet\SpreadsheetService.php(39): Google\Spreadsheet\SpreadsheetFeed->__construct(false) #2 C:\php\google_docd.php(11): Google\Spreadsheet\SpreadsheetService->getSpreadsheets() #3 {main} thrown in C:\php\php-google-spreadsheet-client-master\src\Google\Spreadsheet\SpreadsheetFeed.php on line 43  

Please, please. Clear instructions. I'm a complete Google API newbie. thanks. Examples on how to test in SOAPUI or via bash would also be helpful as I can then use that to figure out how to issue Curl request. Many thanks!

like image 472
user2029890 Avatar asked Jan 31 '14 00:01

user2029890


2 Answers

I finally got it working, despite all attempts from the documents to make it look like impossible, here is my setup:

  • google api for OAuth2 php lib; here: https://code.google.com/p/google-api-php-client/
  • google spreadsheet api php lib; here: https://github.com/asimlqt/php-google-spreadsheet-client

You will need to create the credentials on the API Console: https://console.developers.google.com/ There you will need to first create a project and create set of authentified information for your app: in the left menu, click on API & Auth, then Credentials. Click on Create New Client ID (red button), then select Service Account. You will download a file, keep it safe. you will upload it with your script.

Also, please note: it never worked unless: my document was the 'old spreadsheet'. I also needed to share the spreadsheet document with the user ID (the one that could be an email) generated on Google's console. The document contains a top row that is frozen with the appropriate columns name (name, age).

Here is the php script I used in combination with the above:

<?php
require_once 'php-google-spreadsheet/src/Google/Spreadsheet/Autoloader.php';
require_once 'google-api-php-client/src/Google_Client.php';

const G_CLIENT_ID       = 'fill_with_info_from_console.apps.googleusercontent.com';
const G_CLIENT_EMAIL    = '[email protected]';
const G_CLIENT_KEY_PATH = 'key/keep_the_complex_filename_here_privatekey.p12';
const G_CLIENT_KEY_PW   = 'notasecret';

$obj_client_auth  = new Google_Client ();
$obj_client_auth -> setApplicationName ('test_or_whatever_you_like');
$obj_client_auth -> setClientId (G_CLIENT_ID);
$obj_client_auth -> setAssertionCredentials (new Google_AssertionCredentials (
    G_CLIENT_EMAIL, 
    array('https://spreadsheets.google.com/feeds','https://docs.google.com/feeds'), 
    file_get_contents (G_CLIENT_KEY_PATH), 
    G_CLIENT_KEY_PW
));
$obj_client_auth -> getAuth () -> refreshTokenWithAssertion ();
$obj_token  = json_decode ($obj_client_auth -> getAccessToken ());
$accessToken = $obj_token->access_token;

$request = new Google\Spreadsheet\Request($accessToken);
$serviceRequest = new Google\Spreadsheet\DefaultServiceRequest($request);
Google\Spreadsheet\ServiceRequestFactory::setInstance($serviceRequest);
$spreadsheetService = new Google\Spreadsheet\SpreadsheetService();
$spreadsheetFeed = $spreadsheetService->getSpreadsheets();
$spreadsheet = $spreadsheetFeed->getByTitle('title_of_the_spreadsheet_doc');
$worksheetFeed = $spreadsheet->getWorksheets();
$worksheet = $worksheetFeed->getByTitle('title_of_the_tab');
$listFeed = $worksheet->getListFeed();

// this bit below will create a new row, only if you have a frozen first row adequatly labelled

$row = array('name'=>'John', 'age'=>25);
$listFeed->insert($row);

?>

I should also note:

  • All this is still very much work in progress, but hopefully will help someone write amazing instructions for anyone to better understand the nitty gritty

  • It's a compilation of bits and pieces from both the google documentations, some answers on stackoverflow and info from the 2 api libraries

  • It has been an awfully painful to get this working and it really shouldn't; i'm assuming this is because google is transitioning authentification, console interface and api versions all at the same time.

  • EDIT: It seems the columns names in the google doc are restricted: no space allowed(?), no underscore allowed (?), CamelCase seems troublesome. I only managed to get the dashes to work, like in "placement-other", otherwise the api throws some "Uncaught exception"

  • EDIT: I used the exact same setup for a new project and still does work, with the new Spreadsheet model introduced by Google recently. What hold me that i had forgotten: no blank cell allowed, headers must be frozen without spaces, and they are lowercase when queried from PHP.

Hope this helps!

like image 125
jrgd Avatar answered Oct 14 '22 15:10

jrgd


This answer is meant to be an add-on to jrgd`s answer, and I post it here, as an answer, because it contains code.

The connection problems themselves hung me up. Here is what I had to do to resolve that: Trying to Get Google accessToken . Also, the spreadsheet has to be shared with the Google-generated e-mail that you find in your code.

Also, jrgd, to answer your question about that Request object: it doesn't exist with the Google Spreadsheet API; /* I couldn't find it when I used Composer to download the library */ Instead, I ended up doing what GingerDog did:

$serviceRequest = new Google\Spreadsheet\DefaultServiceRequest($accessToken); Google\Spreadsheet\ServiceRequestFactory::setInstance($serviceRequest);

There is also a significant probability of the code throwing an Exception when using getSpreadsheets(), due to an HTTP error code being returned that is higher than 300 . The SpreadsheetService class has that method (here is its code: /** * Fetches a list of spreadhsheet spreadsheets from google drive. * * @return \Google\Spreadsheet\SpreadsheetFeed */ public function getSpreadsheets() { return new SpreadsheetFeed( ServiceRequestFactory::getInstance()->get('feeds/spreadsheets/private/full') ); } Notice that there is another class that is "doing the dirty work here": the DefaultServiceRequest class. Here is the get() that is being used:

/**
     * Perform a get request
     * 
     * @param string $url
     * 
     * @return string
     */
    public function get($url)
    {
        $ch = $this->initRequest($url);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
        return $this->execute($ch);
    }
    /**
     * Executes the api request.
     * 
     * @return string the xml response
     *
     * @throws \Google\Spreadsheet\Exception If the was a problem with the request.
     *                                       Will throw an exception if the response
     *                                       code is 300 or greater
     *                                       
     * @throws \Google\Spreadsheet\UnauthorizedException
     */
    protected function execute($ch)
    {
        $ret = curl_exec($ch);

        $info = curl_getinfo($ch);
        $httpCode = (int)$info['http_code'];

        if($httpCode > 299) {
            if($httpCode === 401) {
                throw new UnauthorizedException('Access token is invalid', 401);
            } else {
                throw new Exception('Error in Google Request', $info['http_code']);
            }
        }

        return $ret;
    }

Notice that the function, from its innermost helper, has a chance to give back an http_code that will cause the code to throw an exception. Not good for business.

The Solution

The way I remedied that is to change the following line of code: $spreadsheetFeed = $spreadsheetService->getSpreadsheets();

to this while-loop:

/* my way of "making" it work; // I just getSpreadsheets() until there stops being an exception thrown */
    $googleException = new Exception();
    while ($googleException != null)
    {
        try
        {
            $spreadsheetFeed = $spreadsheetService->getSpreadsheets();  # This line randomly throws exception, for some reason. 
            $googleException = null;
        }
        catch (Exception $e)
        {
            $googleException = $e;
        }
    }
    //var_dump($spreadsheetFeed->getArrayCopy());   // test line
like image 39
Mike Warren Avatar answered Oct 14 '22 17:10

Mike Warren