Google App Engine for PHPからGoogleスプレッドシートに読み書きする


『[Google App Engine for PHPでGoogle Drive APIを使う](http://qiita.com/hikoalpha/items/04ef84cd5f035ff64f23)』の続きです。Google App Engine for PHP(GAE/PHP)からGoogle Driveに保存してあるスプレッドシートに読み書きする方法です。これによってスプレッドシートをデータベースのように使用することができます。またGoogleスプレッドシートはGoogle Driveの容量に[カウントされない](https://support.google.com/drive/answer/6558?hl=ja)ため、好きな分だけ使用することができます。

## 1. Googleスプレッドシートの作成
[Google Drive](https://drive.google.com)へアクセスしてスプレッドシートを作成します。

![sheet.jpg](https://qiita-image-store.s3.amazonaws.com/0/70925/754dddba-b61c-68cc-213d-0e638dd797eb.jpeg)

ブラウザ上でスプレッドシートを開きます。1行目にid、name、ageと入力し、2行目以降は適当なデータを入力します。

![sheet2.jpg](https://qiita-image-store.s3.amazonaws.com/0/70925/e80fff5f-215c-2c05-af26-d0b2f67247f5.jpeg)

スプレッドシートを開いた状態でのブラウザのURL欄は
`https://docs.google.com/spreadsheets/d/{スプレッドシートID}/edit#gid=0`
となっているので、スプレッドシートIDをメモしておきます。このIDを使ってプログラムからアクセスします。

## 2. プログラム
『[Google App Engine for PHPでGoogle Drive APIを使う](http://qiita.com/hikoalpha/items/04ef84cd5f035ff64f23)』のGoogle APIs Client Library for PHPのダウンロードまで済ませてください。(プロジェクト作成、API設定、OAuth設定)

```php:sample.php
<?php
require_once('./google-api-php-client/src/Google/autoload.php');

session_start();
$client = new Google_Client();
$client->setClientId('クライアントID');
$client->setClientSecret('クライアントシークレット');
$client->setRedirectUri('リダイレクトURI');

// 許可されてリダイレクトされると URL に code が付加されている
// code があったら受け取って、認証する
if (isset($_GET['code'])) {
    // 認証
    $client->authenticate($_GET['code']);
    $_SESSION['token'] = $client->getAccessToken();
    // リダイレクト GETパラメータを見えなくするため（しなくてもOK）
    header('Location: http://'.$_SERVER['HTTP_HOST']."/");
    exit;
}

// セッションからアクセストークンを取得
if (isset($_SESSION['token'])) {
    // トークンセット
    $client->setAccessToken($_SESSION['token']);
}

// トークンがセットされていたら
if ($client->getAccessToken()) {
    try {
        echo "Google Drive Api 連携完了！<br>";
        $obj = json_decode($client->getAccessToken());
        $token = $obj->{'access_token'};
        write($token);
        read($token);
    } catch (Google_Exception $e) {
        echo $e->getMessage();
    }
} else {
    // 認証スコープ(範囲)の設定
    $client->setScopes(Google_Service_Drive::DRIVE);
    // 一覧を取得する場合はhttps://spreadsheets.google.com/feedsが必要
    $client->addScope('https://spreadsheets.google.com/feeds');

    $authUrl = $client->createAuthUrl();
    echo '<a href="'.$authUrl.'">アプリケーションのアクセスを許可してください。</a>';
}


function write($token) {
    $key = '{スプレッドシートID}';
    $url = "https://spreadsheets.google.com/feeds/list/{$key}/od6/private/full";

    // 書き込みデータ
    $fields = '<entry xmlns="http://www.w3.org/2005/Atom" xmlns:gsx="http://schemas.google.com/spreadsheets/2006/extended">
        <gsx:id>0004</gsx:id>
        <gsx:name>umi</gsx:name>
        <gsx:age>16</gsx:age>
        </entry>';

    $context = stream_context_create(
        array(
            'http' => array(
                'method'=> 'POST',
                'header'=> "Content-Type: application/atom+xml\r\n"."Authorization: Bearer ".$token,
                'content' => $fields
            )
        )
    );

    $response = file_get_contents($url, false, $context);
    // ステータスコードがHTTP/1.1 201なら書き込みOK
    echo 'http status is ' . $http_response_header[0];
}


function read($token) {
    $key = '{スプレッドシートID}';
    $url = "https://spreadsheets.google.com/feeds/list/{$key}/od6/private/full";
    // age>16条件
    //$url = "https://spreadsheets.google.com/feeds/list/{$key}/od6/private/full?sq=age>16";
    // 一覧取得
    //$url = 'https://spreadsheets.google.com/feeds/spreadsheets/private/full';

    $context = stream_context_create(
        array(
            'http' => array(
                'method'=> 'GET',
                'header'=> "Authorization: Bearer ".$token
            )
        )
    );

    $response = file_get_contents($url, false, $context);
    echo 'http status is ' . $http_response_header[0];
    $obj = simplexml_load_string($response);

    /* 取得データ表示
    echo "<pre>";
    print_r($obj);
    echo "</pre>";
    */

    foreach($obj->entry as $data) {
        $gsxNode = $data->children('gsx', true);
        echo 'USER ID:'.$gsxNode->id.' Name:'.$gsxNode->name.' Age:'.$gsxNode->age.'<br>';
    }
}
```
## 3. 実行結果
![result.jpg](https://qiita-image-store.s3.amazonaws.com/0/70925/19b60bf9-edc1-322d-d3a5-45924af0df3d.jpeg)

![result2.jpg](https://qiita-image-store.s3.amazonaws.com/0/70925/60921320-a3e6-5f6b-a223-3ab3e934b3cf.jpeg)

## 4. 注意点と補足
### 4.1 GAE/PHPではcURLが使えない
ローカルサーバーでは設定を変更することでcURLが使えるようになりますが、GAEサーバーではcURLが使えません。サンプルプログラムではfile_get_contents()を使ってhttpリクエストを送信しています。

ローカルサーバーでのcURL使用方法
1. app.yamlを開いてruntime: php55へ変更
2.プロジェクトフォルダのルートにphp.iniを配置しiniファイルにextension = "curl.so"を記述

### 4.2 スプレッドシートに複数行書き込みされる
GAEでは自動でfaviconへのアクセスが入ります。app.yamlで設定していないとfaviconのアクセスのたびにurlに`.*`が設定されているscript(phpファイル)が実行されてしまいます。これを回避するにはapp.yamlにfaviconの記述をするか、scriptファイル(sample.php)のurlをデフォルトの`.*`から`/`へ変更します。(ルートへのアクセスのみ実行)


### 4.3 クエリ
スプレッドシート読み取り時にURLにクエリを付け加えることで条件にマッチする行を取得することができます。
[Google Sheets API version 3.0 - Sending a structured query for rows](https://developers.google.com/google-apps/spreadsheets/#sending_a_structured_query_for_rows)
例：age>16の行を取得
`$url = "https://spreadsheets.google.com/feeds/list/{$key}/od6/private/full?sq=age>16`

## 参考
[PHPでGoogle Spreadsheetにデータ投入してみる - Qiita](http://qiita.com/kenji0302/items/efbdb159130302a90143)
[Google Sheets API version 3.0 - Google Apps — Google Developers](https://developers.google.com/google-apps/spreadsheets/)
[URL Fetch PHP API Overview - PHP — Google Cloud Platform](https://cloud.google.com/appengine/docs/php/urlfetch/)
[The php.ini File - PHP — Google Cloud Platform](https://cloud.google.com/appengine/docs/php/config/php_ini)