[Laravel] Googleカレンダーを使ってみる

PHP

Googleのカレンダーを連携するってなってなかなか大変だったメモ。色々と端折ってますが
ローカルだと公開URIがないからうまく出来なくてなかなか吐きそうだった・・・
にしても、PHPて変数のあたまの$を打つのがめんど

<?php
namespace App\Http\Controllers;

use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\DB;
use Google_Client;
use Google_Service_Calendar;
use Google_Service_Calendar_Channel;
use Google_Service_Exception;

class SampleControoler
{

  public function __construct()
  {
    $this->user = ユーザー情報;
    //  Googleオブジェクト作成
    $googleClient = new Google_Client();
    $googleClient->setAccessType('offline');
    $googleClient->setApprovalPrompt('force');
    //  スコープの設定
    $googleClient->setScope(Google_Service_Calendar::Calendar);
    //  storageに認証ファイル配置
    $googleClient->setAuthConfig(storage_path('app/client_secret.json'));
    $this->googleClient = $googleClient;
  }

  //  Google認証
  public function GoogleAuth()
  {
    //  DBにアクセストークンがあるか
    if(!empty($this->user->access_token)) {
      //  トークンの発行に失敗してるか
      if(is_string($this->user->access_token)) {
        //  JSONを連想配列に
        $ass_arr = json_decode($this->user->access_token, true);
        //  失敗してたらG認証画面へ
        if ($ass_arr && array_key_exists('error', $ass_arr)) return Redirect::to($this->googleClient->createAuthUrl())->send();
      }
      try {
        //  アクセストークンの有効期限をチェック。切れてたらrefreshトークンを使って再取得
        if ($this->googleClient->isAccessTokenExpired()) {
          if (!empty($this->user->refresh_token)) {
            $access_token = $this->googleClient->fetchAccessTokenWithRefreshToken($this->user->refresh_token);
            $access_token_json = json_encode($access_token);
            //  トークン更新
            $this->user->fill(['access_token' => $access_token_json])->save();
            $this->googleClient->setAccessToken($access_token);
          }
        }
      //  無効なトークンはG認証画面へ
      } catch(Google_Service_Exception $e ) {
        Redirect::to($this->googleClient->createAuthUrl())->send();
      }
    //  DBにトークンがなかったらG認証画面へ
    } else {
      Redirect::to($this->googleClient->createAuthUrl())->send();
    }
  }

  //  GoogleAuth()でGoogleの認証に行った後に動かすメソッド
  public function oauth(Request $request)
  {
    try {
      $this->_getUser($request);

      // キャンセルされた場合
      if ($request->has('error') && $request->error === 'access_denied') {
        return Redirect::to($this->app_uri)->send();
      }

      $data = $this->googleClient->fetchAccessTokenWithAuthCode($request->input('code'));
      $refresh_token = $data['refresh_token'];
      //  JSONごと保存(isAccessTokenExpiredで必要となるため)
      $this->googleClient->setAccessToken(json_encode($data));

      $user = User::find($this->user->id);
      $user->fill([
        'access_token' => $data,
        'refresh_token' => $refresh_token,
      ])->save();

      //  チャンネル作成
      //  スケジュールの監視をする。ユーザー毎作成?
      $this->create_channel();

      //  GoogleカレンダーとDBの同期
      //  TODOここもメソッドで分ける
      $result_arr = array();
      // 7日前までのデータを取得
      $timeMin = (new \DateTime(date("Y-m-d H:i:s",strtotime(-7." day"))))->format(DATE_RFC3339); 
      // 7日後までのデータを取得
      $timeMax = (new \DateTime(date("Y-m-d H:i:s",strtotime(7." day"))))->format(DATE_RFC3339); 
      $optParams = [
        'timeMax' => $timeMax, // イベントの開始時間の上限
        'timeMin' => $timeMin, // イベントの終了時間の下限
        'timeZone' => 'Asia/Tokyo',
        'singleEvents' => true, // 定期イベントを展開
      ];

      DB::beginTransaction();
      try {
        $client_user_client = $this->getOAuth($client_user);
        $client_user_service = new Google_Service_Calendar($client_user_client);
        $client_user_events = $client_user_service->events->listEvents('primary', $optParams);  //カレンダー情報一覧取得
        $exists_calendar_id = [];

        while (true) {
          foreach ($client_user_events->getItems() as $key => $event) {
            //  この中でカレンダー情報をごにょごにょ
          }

          $pageToken = $client_user_events->getNextPageToken();

          //  スケジュール情報の次のページがあるとき
          if ($pageToken) {
            $optParams['pageToken'] = $pageToken;
            $client_user_events = $client_user_service->events->listEvents('primary', $optParams);
          //  ないとき
          } else {
            $nextSyncToken = $client_user_events->getNextSyncToken();
            $channel_model = Channel::where('user_id', $client_user->id)->first();

            if ($channel_model && !empty($nextSyncToken)) {
              $channel_model->fill(['nextSyncToken' => $nextSyncToken]);
              $channel_model->save();
            }
            //  なかったら終わり
            break;
          }
        }
      } catch (Exception $e) {
        DB::rollback();
        Log::error($e->getMessage());
      }
      return Redirect::to($this->app_uri)->send();
    } catch (Exception $e) {
      Log::error('oauth2: '.$e->getMessage());
    }
  }

  //  チャンネル作成
  public function create_channel()
  {
    DB::beginTransaction();
    try {
      $user = $this->user;

      $service = new Google_Service_Calendar($this->googleClient);
      $calendarListEntry = $service->calendarList->get('primary');
      $primary_id = $calendarListEntry->getId();
      $primary_channel_model = Channel::where('calendar_id', $primary_id)->first();

      //  プライマリチャンネルが存在している場合
      if ($primary_channel_model) {
        //  有効期限チェック(24時間以内の場合、チャンネルの更新)
        //  expiration = 有効期限(ミリ秒)
        $channel_expiration = $primary_channel_model->expiration / 1000; 
        $timestamp = time();
        $hours = 1 * 60 * 60;
        $diff_hours = ($channel_expiration - $timestamp) / $hours;
        DB::rollback();
        if ($diff_hours <= 25) {
          $this->delete_channel($user, $primary_channel_model);  // チャンネル削除
          $this->create_channel();  // チャンネル追加
        }
        return;
      }

      //  チャンネル登録
      $channel = new Google_Service_Calendar_Channel($this->googleClient);
      $channel->setId(time());
      $channel->setType('web_hook');
      //  スケジュールを更新などしたときにwebhookが動く
      //  そのときに投げられるURIを設定する
      $channel->setAddress($this->app_uri . 'api/webhook');
      $watchEvent = $service->events->watch('primary', $channel);

      //  DB登録
      $channel_model = new Channel;
      foreach ($watchEvent as $key => $data) {
        //リネーム id → channel_id
        if ($key === 'id') {
            $key = 'channel_id';
        }
        $channel_model->fill([$key => $data]);
      }
      $channel_model->fill([
        'calendar_id' => $primary_id,
        'user_id' => $user->id
      ]);
      $channel_model->save();
      DB::commit();
    } catch (InvalidArgumentException $e) {
      DB::rollback();
    } catch (Google_Service_Exception $e) {
      DB::rollback();
      return false;
    } catch (Exception $e) {
      DB::rollback();
      Log::error($e->getMessage());
    }
  }
}

コメント

タイトルとURLをコピーしました