外部サーバーとkintoneを連携することで、kintoneの運用の幅が大きく広がります。 今回はkintone-PHP連携の初歩として、レコードを他のアプリにバックアップする例を紹介します。
サンプル
backup.phpを実行すると、アプリAからアプリBにレコードを差分バックアップします。 アプリA側で削除されたレコードについては、アプリB側で「deleted」フィールドに削除を確認した日時を書き込むことで論理削除とします。
※サンプルではコマンドラインで実行していますが、cron等を利用すればスケジュール実行できます。
コード
kintone API SDK for PHPを利用しない例と、利用した例を作成しました。
「kintone API SDK for PHP」利用しない版
・backup.php
\<?phprequire 'RecordsApi.php';$recordsApi = new RecordsApi(['domain' =\> 'cybozu.com','subdomain' =\> '\*\*\*\*','login' =\> '\*\*\*\*','password' =\> '\*\*\*\*','use\_basic' =\> true,'basic\_login' =\> '\*\*\*\*','basic\_password' =\> '\*\*\*\*',]);define('ORIGIN\_APP\_ID', \*\*\*\*);define('COPY\_APP\_ID', \*\*\*\*);function numberedRecords($records){$numberedRecords = [];foreach($records as $record){$numberedRecords[$record['レコード番号']['value']] = $record; }return $numberedRecords;}function unsetUnnecessaryKeys($record){foreach(['レコード番号', '$id', '$revision', '作成者', '更新者', '作成日時', '更新日時'] as $fieldCode){unset($record[$fieldCode]); }return $record;}$originRecords = numberedRecords($recordsApi-\>get(ORIGIN\_APP\_ID, 'order by レコード番号 asc'));$copyRecords = numberedRecords($recordsApi-\>get(COPY\_APP\_ID, 'order by レコード番号 asc'));for($id=1; $id\<=max(end($originRecords)['レコード番号']['value'], end($copyRecords)['レコード番号']['value']); $id++){if(isset($originRecords[$id])){if(isset($copyRecords[$id])){if($originRecords[$id]['更新日時']['value']\>$copyRecords[$id]['更新日時']['value']){$putRecords[] = ['id' =\> $id,'record' =\> unsetUnnecessaryKeys($originRecords[$id]) ]; } }else{$postRecords[] = unsetUnnecessaryKeys($originRecords[$id]); } }else{if(isset($copyRecords[$id])){if(!$copyRecords[$id]['deleted']['value']){$putRecords[] = ['id' =\> $id,'record' =\> ['deleted' =\> ['value' =\> date("Y-m-d\TH:i:sP")]] ]; } }else{$postRecords[] = ['deleted' =\> ['value' =\> date("Y-m-d\TH:i:sP")]]; } }}if(isset($postRecords)) $recordsApi-\>post(COPY\_APP\_ID, $postRecords);if(isset($putRecords)) $recordsApi-\>put(COPY\_APP\_ID, $putRecords);echo 'updated!';
・RecordsApi.php
\<?phpclass RecordsApi{private $url, $header;public function \_\_construct($parameter){$this-\>url = 'https://'.$parameter['subdomain'].'.'.$parameter['domain'].'/k/v1/records.json';$this-\>header[] = 'X-Cybozu-Authorization: '.base64\_encode($parameter['login'].':'.$parameter['password']);if($parameter['use\_basic']){$this-\>header[] = 'Authorization: Basic '.base64\_encode($parameter['basic\_login'].':'.$parameter['basic\_password']); } }public function get($appId, $query='', $records=[]){$limit = 500;$response = json\_decode(file\_get\_contents($this-\>url."?app={$appId}&query=".urlencode("{$query} limit {$limit} offset ".count($records)), false, stream\_context\_create(['http' =\> ['method' =\> 'GET','header' =\> implode("\r", $this-\>header),] ])), true );$records = array\_merge($records, $response['records']);return (count($response['records']) === $limit) ? $this-\>get($appId, $query, $records) : $records; }public function post($appId, $records){$limit = 100;foreach(array\_chunk($records, $limit) as $recordChunk){file\_get\_contents($this-\>url, false, stream\_context\_create(['http' =\> ['method' =\> 'POST','header' =\> implode("\r", array\_merge(['Content-Type: application/json'], $this-\>header)),'content' =\> json\_encode(['app' =\> $appId,'records' =\> $recordChunk], JSON\_UNESCAPED\_UNICODE) ] ])); } }public function put($appId, $records){$limit = 100;foreach(array\_chunk($records, $limit) as $recordChunk){file\_get\_contents($this-\>url, false, stream\_context\_create(['http' =\> ['method' =\> 'PUT','header' =\> implode("\r", array\_merge(['Content-Type: application/json'], $this-\>header)),'content' =\> json\_encode(['app' =\> $appId,'records' =\> $recordChunk], JSON\_UNESCAPED\_UNICODE) ] ])); } }}
「kintone API SDK for PHP」利用版
・backup.php
\<?phprequire 'vendor/autoload.php';$api = new \CybozuHttp\Api\KintoneApi(new \CybozuHttp\Client(['domain' =\> 'cybozu.com','subdomain' =\> '\*\*\*\*','login' =\> '\*\*\*\*','password' =\> '\*\*\*\*','use\_basic' =\> true,'basic\_login' =\> '\*\*\*\*','basic\_password' =\> '\*\*\*\*',]));define('ORIGIN\_APP\_ID', \*\*\*\*);define('COPY\_APP\_ID', \*\*\*\*);function numberedRecords($records){$numberedRecords = [];foreach($records as $record){$numberedRecords[$record['レコード番号']['value']] = $record; }return $numberedRecords;}function unsetUnnecessaryKeys($record){foreach(['レコード番号', '$id', '$revision', '作成者', '更新者', '作成日時', '更新日時'] as $fieldCode){unset($record[$fieldCode]); }return $record;}$originRecords = numberedRecords($api-\>records()-\>all(ORIGIN\_APP\_ID, 'order by レコード番号 asc'));$copyRecords = numberedRecords($api-\>records()-\>all(COPY\_APP\_ID, 'order by レコード番号 asc'));for($id=1; $id\<=max(end($originRecords)['レコード番号']['value'], end($copyRecords)['レコード番号']['value']); $id++){if(isset($originRecords[$id])){if(isset($copyRecords[$id])){if($originRecords[$id]['更新日時']['value']\>$copyRecords[$id]['更新日時']['value']){$putRecords[] = ['id' =\> $id,'record' =\> unsetUnnecessaryKeys($originRecords[$id]) ]; } }else{$postRecords[] = unsetUnnecessaryKeys($originRecords[$id]); } }else{if(isset($copyRecords[$id])){if(!$copyRecords[$id]['deleted']['value']){$putRecords[] = ['id' =\> $id,'record' =\> ['deleted' =\> ['value' =\> date("Y-m-d\TH:i:sP")]] ]; } }else{$postRecords[] = ['deleted' =\> ['value' =\> date("Y-m-d\TH:i:sP")]]; } }}if(isset($postRecords)){foreach(array\_chunk($postRecords, 100) as $recordChunk){$api-\>records()-\>post(COPY\_APP\_ID, $recordChunk); }}if(isset($putRecords)){foreach(array\_chunk($putRecords, 100) as $recordChunk){$api-\>records()-\>put(COPY\_APP\_ID, $recordChunk); }}echo 'updated!';
注記
1.アプリBには、アプリAにあるフィールドに加え、フィールドコード「deleted」のフィールドをあらかじめ用意する必要があります。アプリBのフィールドに不足がある場合は正しく動作しません。この場合についてのエラー処理は実装しておりません。
2.アプリBへのレコードの追加、変更はスクリプト(backup.php)からのみ実行されるという想定で作成しております。手動でアプリBにレコードを追加、変更、削除などを行うとその後スクリプトが正しく動作しなくなる可能性があります。この場合についてもエラー処理は実装しておりません。
3.アプリBへバックアップする前にアプリA側でレコードが削除された場合、レコード番号のみバックアップされます。