PHP(Laravel) Graph SDKでの会議予定差分取得方法と検証結果

Microsoft Graph
この記事は約9分で読めます。

記事執筆時(2025年3月21日)時点でPHPのGraph SDKバージョンはv2.29.0が最新です。
このバージョンv2.29.0では会議予定に対する差分取得の際にGraph Explorerが推奨するリクエストオブジェクトでは正常に取得できないために、工夫が必要でした。
その際に行った取得手順を備忘も兼ねてこの記事を執筆します。

取得結果の検証

Microsoft Graphに於いて、差分取得が許可されたリソースはサポートされているリソース | デルタ クエリを使用して、Microsoft Graph データの変更を追跡するで紹介されています。
今回は、[events]リソースに対する差分取得に限定して取得結果の検証を行います。

次の表はユーザーAが通常のエンドポイントとDeltaエンドポイントで指定期間を2025/3/11 09:00~2025/3/12 18:00に指定して取得した場合の抽出結果を示します、通常取得には[top=3]を指定しているとします。

リソース主催者参加者登録日時開始日時終了日時通常Delta
会議AユーザーAユーザーB2025/02/03 15:202025/03/10 11:002025/03/10 12:00
会議BユーザーBユーザーC2025/02/05 09:282025/03/11 09:002025/03/11 09:30
会議CユーザーBユーザーA,ユーザーC2025/03/11 09:292025/03/14 13:002025/03/14 15:002
会議DユーザーAユーザーB,ユーザーC2025/03/10 16:022025/03/11 18:002025/03/11 18:3031
会議EユーザーCユーザーB2025/03/03 14:512025/03/12 18:002025/03/12 18:30
会議FユーザーAユーザーC2025/03/11 16:092025/03/12 09302025/03/12 10:0012

ソート順

取得結果の数値はソート順になっています。今回エンドポイントに対するクエリパラメータに[orderBy]は指定していませんので通常取得でのソート順は規定値の登録が新しい順となっています、Delta取得の場合ソート順を指定すると「ErrorInvalidUrlQuery」エラーとなります。

ユーザー視点

ユーザー視点で結果を見ると、「通常取得」「Delta取得」共に主催者に関係なく結果を得られています。これは会議予定のインスタンスを参加者も保持しているためです。

指定期間

視点を指定範囲に切り替えてみると、「Delta取得」に於いて登録日時が指定範囲内である『会議C』は除外されています。これにより、指定期間とは会議の開始日時から終了日時の範囲であるということが解ります。また、指定期間内である『会議B』については、ユーザーAがインスタンスを保持していないため除外されていることが確認できます。

変更を加えて再取得

いくつかの変更を加え、再取得を実施します。検証のため指定期間は変更しません。

リソース主催者参加者登録日時開始日時終了日時通常Delta
会議AユーザーAユーザーB2025/02/03 15:202025/03/10 11:002025/03/10 12:00
会議BユーザーBユーザーC2025/02/05 09:282025/03/11 09:002025/03/11 09:30
会議C(変更)ユーザーBユーザーA,ユーザーC2025/03/11 09:292025/03/12 13:002025/03/12 15:001
会議DユーザーAユーザーB,ユーザーC2025/03/10 16:022025/03/11 18:002025/03/11 18:3032
会議EユーザーCユーザーB2025/03/03 14:512025/03/12 18:002025/03/12 18:30
会議F(キャンセル)ユーザーAユーザーC2025/03/11 16:092025/03/12 09302025/03/12 10:00
会議G(追加)ユーザーBユーザーA,ユーザーC2025/03/12 09:582025/03/12 10:002025/03/12 10:3023
会議H(追加)ユーザーAユーザーB,ユーザーC2025/03/12 13:422025/04/01 10:002025/04/01 10:301
  • 『会議C』の会議日時を変更しました
  • 『会議G』を追加しました
  • 『会議H』を追加しました

期待した結果になっているのか?

『会議D』に着目します、1度目の差分取得で最初の取得リソースとして登場しています。
期待としては2度目の差分取得には登場してほしくないはずです。
何度実施しても全く同じ結果が得られます。

取得順&指定期間を変更して取得

今までの取得方法は、「通常取得」「Delta取得」を個別に行っていました、結果「通常取得」と「Delta取得」に重複が見られました。
検証するため取得処理をフローとして実施するよう修正します。

  1. 「通常取得」を実施
  2. 1.で得られた結果から終了日時が最も遅い会議を特定
  3. 指定期間の開始日時を現在日時に変更
  4. 指定期間の終了日時を2.で取得した終了日時に変更
  5. 3.,4.の条件を基にDelta取得を実施

リソース主催者参加者登録日時開始日時終了日時通常Delta
会議AユーザーAユーザーB2025/02/03 15:202025/03/10 11:002025/03/10 12:00
会議BユーザーBユーザーC2025/02/05 09:282025/03/11 09:002025/03/11 09:30
会議CユーザーBユーザーA,ユーザーC2025/03/11 09:292025/03/12 13:002025/03/12 15:00
会議DユーザーAユーザーB,ユーザーC2025/03/10 16:022025/03/11 18:002025/03/11 18:303
会議EユーザーCユーザーB2025/03/03 14:512025/03/12 18:002025/03/12 18:30
会議GユーザーBユーザーA,ユーザーC2025/03/12 09:582025/03/12 10:002025/03/12 10:3021
会議HユーザーAユーザーB,ユーザーC2025/03/12 13:422025/04/01 10:002025/04/01 10:3012
※現在時刻は[2025-03-12T09:00]として取得しています

「通常取得」後に「Delta取得」を行ったのに、変更されていない『会議G』と『会議H』が差分として取得されました。これは果たして期待通りの結果なのでしょうか?
期待値としては「なにも取得されない」だったのではないでしょうか?

差分とは?

「差分」とは?という疑問が浮かび上がりました。
差分取得用のエンドポイントを確認します。

GET https://graph.microsoft.com/v1.0/me/calendarView/delta/?startDateTime={開始日時}&endDateTime={終了日時}

通常取得用のエンドポイントとは全く異なるものであることが解ります。
エンドポイントが異なるので、重複したデータが取得されるのは「仕様」であることが確認できます。

差分取得用のエンドポイントでは1度目のリクエストで指定期間内の会議すべてを取得します。取得結果に[@OData.deltaLink]というさらなる差分取得用のURLデータが含まれます。この[@OData.deltaLink]にGETアクセスすることで、変更の追跡が可能になります。

DeltaLinkを利用してPHPで差分取得を行う

<?php
use Microsoft\Graph\GraphServiceClient;
use Microsoft\Graph\Generated\Users\Item\CalendarView\Delta\DeltaRequestBuilderGetRequestConfiguration;

$graphServiceClient = new GraphServiceClient($tokenRequestContext, $scopes);

// 現在日時~6か月後までの予定を指定
$requestConfiguration = new DeltaRequestBuilderGetRequestConfiguration();
$queryParameters = DeltaRequestBuilderGetRequestConfiguration::createQueryParameters();
$queryParameters->startDateTime = date('c', strtotime('now'));
$queryParameters->endDateTime = date('c', strtotime('6 months'));
$requestConfiguration->queryParameters = $queryParameters;
// 1回目のリクエスト(期間内のすべての予定を取得)
$result = $graphServiceClient->me()->calendarView()->delta()->get($requestConfiguration)->wait();

// deltaTokenを正規表現で取得
preg_match('/(?<=deltatoken=)(?P<deltaToken>.*)/', $result->getODataDeltaLink(), $matches);

// deltaTokenでリクエスト(Graph Explorerのコードスニペットが生成)
$requestConfiguration = new DeltaRequestBuilderGetRequestConfiguration();
$queryParameters = DeltaRequestBuilderGetRequestConfiguration::createQueryParameters();
$queryParameters->deltatoken = $matches['deltaToken'];
$requestConfiguration->queryParameters = $queryParameters;

$result = $graphServiceClient->me()->calendarView()->delta()->get($requestConfiguration)->wait();

このコードを実行すると、1回目のリクエストに含まれる[deltaToken]の取得までは成功します。
ですが、赤字の行でクエリパラメータの生成エラーが発生します。
調べた結果、現時点でライブラリには[deltaToken]をJSONに変換する機能が含まれていないようです。

問題の解決

RequestAdapterを利用したトークンリクエスト

この問題に関してBug: No $deltatoken or $skiptoken in DeltaRequestBuilderGetQueryParameters #1503で対処法が提示されていますので、参考に修正を行い解決しました。

URLを利用したGETリクエスト

GraphServiseClientに対しては[withUrl('url')]メソッドを利用したリクエストも可能です。
[@OData.tokenLink]を引数に渡すことで差分データの取得が可能です。

$response = $graphServiceClient->me()->calendarView()->withUrl('tokenLink')->get()->wait();

参考

delta取得時にリクエストヘッダーにページング要求を追加することが可能です。
ページングを指定した際は[PageIterator]クラスを利用して各レコードに対して処理を行うことが可能です。

この記事を書いた人

岩本敏彦

コメント

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