You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 20:40:57