如何使用Microsoft Graph SDK for PHP读取指定文件夹邮件并标记已读、移动
需求实现修正与优化
针对你用Microsoft Graph SDK for PHP实现的「从指定文件夹读邮件、标记已读、移动至目标文件夹」需求,这里给出修正和优化后的方案:
1. 从指定文件夹读取邮件
现有代码默认读取用户所有邮件,要精准读取指定文件夹内的邮件,需先定位源文件夹(比如收件箱或自定义文件夹),再调用该文件夹下的messages端点:
// 定位源文件夹(示例:读取收件箱邮件) $sourceFolderName = '收件箱'; $sourceFolder = $graphServiceClient->users()->byUserId($userPrincipalName) ->mailFolders()->get(new MailFoldersRequestBuilderGetRequestConfiguration( queryParameters: MailFoldersRequestBuilderGetRequestConfiguration::createQueryParameters( filter: "displayName eq '$sourceFolderName'" ) ))->wait()->getValue()[0]; // 从指定文件夹读取邮件 $requestConfig = new MessagesRequestBuilderGetRequestConfiguration( queryParameters: MessagesRequestBuilderGetRequestConfiguration::createQueryParameters( select: ['subject', 'body', 'from', 'isRead'], top: 2 ), headers: ['Prefer' => 'outlook.body-content-type=text'] ); $messages = $graphServiceClient->users()->byUserId($userPrincipalName) ->mailFolders()->byMailFolderId($sourceFolder->getId()) ->messages()->get($requestConfig)->wait();
2. 优化目标文件夹查找逻辑
现有嵌套遍历文件夹的方式效率极低,直接用Graph API的filter或search参数一次请求定位目标文件夹:
$targetFolderName = 'Portico_Leverandor-meldinger'; $targetFolderId = null; // 优先用search查找全层级文件夹 $targetFolders = $graphServiceClient->users()->byUserId($userPrincipalName) ->mailFolders()->get(new MailFoldersRequestBuilderGetRequestConfiguration( queryParameters: MailFoldersRequestBuilderGetRequestConfiguration::createQueryParameters( search: "displayName:$targetFolderName" ) ))->wait()->getValue(); foreach ($targetFolders as $folder) { if ($folder->getDisplayName() === $targetFolderName) { $targetFolderId = $folder->getId(); break; } } // 若search未找到,递归查找子文件夹(可选) if (!$targetFolderId) { $rootFolders = $graphServiceClient->users()->byUserId($userPrincipalName)->mailFolders()->get()->wait()->getValue(); $findFolder = function($folders) use (&$findFolder, $targetFolderName, $userPrincipalName, $graphServiceClient) { foreach ($folders as $folder) { if ($folder->getDisplayName() === $targetFolderName) { return $folder->getId(); } $childFolders = $graphServiceClient->users()->byUserId($userPrincipalName) ->mailFolders()->byMailFolderId($folder->getId())->childFolders()->get()->wait()->getValue(); $childId = $findFolder($childFolders); if ($childId) return $childId; } return null; }; $targetFolderId = $findFolder($rootFolders); } if (!$targetFolderId) { throw new Exception("目标文件夹 $targetFolderName 未找到"); }
3. 标记已读与移动的批量优化
现有单条处理逻辑可行,但大量邮件场景下会产生过多HTTP请求,推荐用Graph批量请求合并操作:
// 创建批量请求容器 $batchContent = new BatchRequestContent($requestAdapter); foreach ($messages->getValue() as $message) { $messageId = $message->getId(); // 添加「标记已读」请求 $patchBody = new Message(); $patchBody->setIsRead(true); $patchRequest = $graphServiceClient->users()->byUserId($userPrincipalName) ->messages()->byMessageId($messageId)->toPatchRequestInformation($patchBody); $batchContent->addRequest($patchRequest, "mark-read-$messageId"); // 添加「移动邮件」请求 $moveBody = new MovePostRequestBody(); $moveBody->setDestinationId($targetFolderId); $moveRequest = $graphServiceClient->users()->byUserId($userPrincipalName) ->messages()->byMessageId($messageId)->move()->toPostRequestInformation($moveBody); $batchContent->addRequest($moveRequest, "move-$messageId"); } // 执行批量请求 $batchResponse = $requestAdapter->sendAsync($batchContent, new BatchResponseContentFactory())->wait(); // 处理响应结果 foreach ($batchResponse->getResponses() as $requestKey => $response) { if ($response->getStatus()->isSuccess()) { echo "请求 $requestKey 执行成功\n"; } else { echo "请求 $requestKey 失败: " . $response->getBody()->getContents() . "\n"; } }
完整修正代码
$httpClient = GraphClientFactory::createWithConfig($guzzleConfig); $requestAdapter = new GraphRequestAdapter($authProvider, $httpClient); $graphServiceClient = GraphServiceClient::createWithRequestAdapter($requestAdapter); // 1. 定位源文件夹 $sourceFolderName = '收件箱'; $sourceFolder = $graphServiceClient->users()->byUserId($userPrincipalName) ->mailFolders()->get(new MailFoldersRequestBuilderGetRequestConfiguration( queryParameters: MailFoldersRequestBuilderGetRequestConfiguration::createQueryParameters( filter: "displayName eq '$sourceFolderName'" ) ))->wait()->getValue()[0]; // 2. 从指定文件夹读取邮件 $requestConfig = new MessagesRequestBuilderGetRequestConfiguration( queryParameters: MessagesRequestBuilderGetRequestConfiguration::createQueryParameters( select: ['subject', 'body', 'from', 'isRead'], top: 2 ), headers: ['Prefer' => 'outlook.body-content-type=text'] ); $messages = $graphServiceClient->users()->byUserId($userPrincipalName) ->mailFolders()->byMailFolderId($sourceFolder->getId()) ->messages()->get($requestConfig)->wait(); // 3. 定位目标文件夹 $targetFolderName = 'Portico_Leverandor-meldinger'; $targetFolderId = null; $targetFolders = $graphServiceClient->users()->byUserId($userPrincipalName) ->mailFolders()->get(new MailFoldersRequestBuilderGetRequestConfiguration( queryParameters: MailFoldersRequestBuilderGetRequestConfiguration::createQueryParameters( search: "displayName:$targetFolderName" ) ))->wait()->getValue(); foreach ($targetFolders as $folder) { if ($folder->getDisplayName() === $targetFolderName) { $targetFolderId = $folder->getId(); break; } } if (!$targetFolderId) { $rootFolders = $graphServiceClient->users()->byUserId($userPrincipalName)->mailFolders()->get()->wait()->getValue(); $findFolder = function($folders) use (&$findFolder, $targetFolderName, $userPrincipalName, $graphServiceClient) { foreach ($folders as $folder) { if ($folder->getDisplayName() === $targetFolderName) { return $folder->getId(); } $childFolders = $graphServiceClient->users()->byUserId($userPrincipalName) ->mailFolders()->byMailFolderId($folder->getId())->childFolders()->get()->wait()->getValue(); $childId = $findFolder($childFolders); if ($childId) return $childId; } return null; }; $targetFolderId = $findFolder($rootFolders); } if (!$targetFolderId) { throw new Exception("目标文件夹 $targetFolderName 未找到"); } // 4. 批量处理邮件 $batchContent = new BatchRequestContent($requestAdapter); foreach ($messages->getValue() as $message) { $messageId = $message->getId(); $patchBody = new Message(); $patchBody->setIsRead(true); $patchRequest = $graphServiceClient->users()->byUserId($userPrincipalName) ->messages()->byMessageId($messageId)->toPatchRequestInformation($patchBody); $batchContent->addRequest($patchRequest, "mark-read-$messageId"); $moveBody = new MovePostRequestBody(); $moveBody->setDestinationId($targetFolderId); $moveRequest = $graphServiceClient->users()->byUserId($userPrincipalName) ->messages()->byMessageId($messageId)->move()->toPostRequestInformation($moveBody); $batchContent->addRequest($moveRequest, "move-$messageId"); } $batchResponse = $requestAdapter->sendAsync($batchContent, new BatchResponseContentFactory())->wait(); foreach ($batchResponse->getResponses() as $requestKey => $response) { if ($response->getStatus()->isSuccess()) { echo "请求 $requestKey 执行成功\n"; } else { echo "请求 $requestKey 失败: " . $response->getBody()->getContents() . "\n"; } }
关键注意事项
- 权限配置:确保应用已获得
Mail.ReadWrite权限(根据认证方式选择Delegated或Application权限) - 批量请求限制:Graph API批量请求最多包含20个操作,大量邮件需分页分批处理
- 错误处理:生产环境需添加异常捕获,处理网络错误、权限不足等场景
内容的提问来源于stack exchange,提问作者Sigurd Nes
相关产品推荐
相关产品推荐

