PHP项目dataverse与存储

wen PHP项目 9

本文目录导读:

PHP项目dataverse与存储

  1. 与 Dataverse(结构化数据)的整合
  2. 与存储(非结构化数据/文件)的整合
  3. 架构模式建议:Dataverse + 存储的联动
  4. 环境考虑与部署注意事项

针对PHP项目与Dataverse(微软的云数据服务)以及存储的整合,这是一个典型的云数据库 + 对象存储(或文件存储)的混合架构需求。

鉴于你问的是“与存储”,这通常包含两个层面:

  1. 结构化数据存储:使用 Dataverse(通过 Dataverse Web API 或 OData 协议)。
  2. 非结构化/文件存储:使用 Azure Blob Storage(Dataverse 的附件/备注默认使用的后端),或直接使用本地/第三方存储(S3, MinIO)。

以下为你梳理最核心的整合方案、代码示例和关键注意事项。


与 Dataverse(结构化数据)的整合

Dataverse 本质是一个基于 Dynamics 365 和 Power Platform 的、具有强关系的商业数据库,PHP 与其交互的唯一标准方式是 RESTful OData v4 API

核心通信方式

  • 协议:HTTPS + OData v4.0 JSON。
  • 认证:必须使用 OAuth 2.0(客户端凭证流 或 授权码流)。绝不能使用用户名/密码(因为它需要 Azure AD 条件访问)。
  • 依赖库
    • php-http/guzzle6-adapterguzzlehttp/guzzle(HTTP 客户端)。
    • microsoft/microsoft-graph(虽然主要是 Graph API,但可辅助认证)。
    • 推荐自行封装或使用轻量的 OData 客户端库,如 sainsburys/guzzle-oauth2-subscriber

关键代码流程(创建记录示例)

<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\Psr7\Request;
// --- 1. 获取 OAuth 2.0 Token (Client Credentials) ---
function getAccessToken($tenantId, $clientId, $clientSecret) {
    $url = "https://login.microsoftonline.com/{$tenantId}/oauth2/v2.0/token";
    $client = new Client();
    $response = $client->post($url, [
        'form_params' => [
            'grant_type' => 'client_credentials',
            'client_id' => $clientId,
            'client_secret' => $clientSecret,
            'scope' => 'https://orgxxxxxx.api.crm.dynamics.com/.default' // 替换为你的环境URL
        ]
    ]);
    $data = json_decode($response->getBody(), true);
    return $data['access_token'];
}
// --- 2. 配置 Dataverse 环境 ---
$orgUrl = 'https://orgxxxxxx.api.crm.dynamics.com'; // 你的 Dataverse 组织URL
$accessToken = getAccessToken('你的TenantID', '你的ClientID', '你的ClientSecret');
// --- 3. 使用 Guzzle 调用 Dataverse API ---
$client = new Client([
    'base_uri' => $orgUrl,
    'headers' => [
        'Authorization' => 'Bearer ' . $accessToken,
        'Accept' => 'application/json',
        'OData-MaxVersion' => '4.0',
        'OData-Version' => '4.0',
        'Content-Type' => 'application/json; charset=utf-8',
    ]
]);
// --- 4. 示例:创建一条客户记录 (Account实体) ---
$data = [
    'name' => '示例客户名称',
    'telephone1' => '010-12345678',
    'creditlimit' => 100000.00,
];
try {
    $response = $client->post('/api/data/v9.2/accounts', [
        'json' => $data
    ]);
    if ($response->getStatusCode() == 204) {
        // 成功,获取记录ID (从响应头 Location 获取)
        $locationHeader = $response->getHeaderLine('Location');
        preg_match('/accounts\\(([^)]+)\\)/', $locationHeader, $matches);
        $accountId = $matches[1] ?? null;
        echo "创建成功! Account ID: " . $accountId;
    } else {
        echo "创建失败: " . $response->getBody();
    }
} catch (\GuzzleHttp\Exception\ClientException $e) {
    echo "HTTP Error: " . $e->getResponse()->getStatusCode() . "\n";
    echo $e->getResponse()->getBody(); // 通常包含更详细的错误信息
}

关键注意事项

  • API 版本:使用 v9.2(当前最新稳定版)。
  • 字段映射:Dataverse 的字段名通常是逻辑名(new_fieldname),需要确认。
  • 分页:使用 $top$skipnextLink(对于超过5000条的数据)。
  • Batch 操作:对于大量写入,使用 $batch 请求(支持事务)。
  • 错误处理:Dataverse 返回的 @Microsoft.PowerApps.CDS.ErrorDetails 非常有用。

与存储(非结构化数据/文件)的整合

Dataverse 本身不直接暴露文件存储,但有两种主要方式:

方案 A:使用 Dataverse 的“注解/附件”机制(推荐)

  • 原理:通过 Dataverse API 的文件列类型(File Datatype)或 Annotation 实体。
  • 优点:数据与业务记录硬关联,无需自己管理存储路径。
  • 缺点:文件大小和类型有限制(128MB 以内),且需要走 Dataverse API 的中转。

上传文件到 Dataverse 实体(伪代码思路):

// 需要先创建记录,然后上传文件到该记录的指定文件列
// 1. 创建记录 (省略,同上)
// 2. 获取文件上传URL
$fileColumnName = 'myfile'; // 实体中定义的文件列名称
$recordId = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx';
$entityName = 'accounts';
$response = $client->post("/api/data/v9.2/{$entityName}({$recordId})/{$fileColumnName}", [
    'headers' => ['Content-Type' => 'application/octet-stream'],
    'body' => fopen('/path/to/large/file.pdf', 'r') // 直接流式上传
]);
// 实际需要更复杂的两步操作(初始化上传+上传块),参考 Microsoft 官方文档

方案 B:直接使用外部对象存储(Azure Blob / AWS S3 / MinIO)

  • 适用场景:大文件(> 128MB)、高并发、或希望存储解耦。
  • 方法
    1. PHP 生成签名 URL(SAS Token 或 Presigned URL)。
    2. 前端/客户端直接上传到存储服务。
    3. Dataverse 中只存储文件 URL 或路径(例如自定义字段)。
  • 关键库
    • Azure Blob:microsoft/azure-storage-blob
    • AWS S3:aws/aws-sdk-php
    • MinIO:aws/aws-sdk-php (兼容 S3 API)

PHP 生成 Azure Blob SAS Token 示例:

use MicrosoftAzure\Storage\Blob\BlobRestProxy;
use MicrosoftAzure\Storage\Common\SharedAccessSignatureHelper;
use MicrosoftAzure\Storage\Blob\Models\CreateContainerOptions;
use MicrosoftAzure\Storage\Blob\Models\PublicAccessType;
// 1. 获取 Blob 客户端
$connectionString = "DefaultEndpointsProtocol=https;AccountName=你的存储账户;AccountKey=你的密钥";
$blobClient = BlobRestProxy::createBlobService($connectionString);
// 2. 生成 SAS Token (1 小时有效)
$sasHelper = new SharedAccessSignatureHelper(
    '你的存储账户',
    '你的密钥'
);
$sasToken = $sasHelper->generateAccountSharedAccessSignatureToken(
    '2023-01-01T00:00:00Z',  // 起始时间
    '2023-12-31T23:59:59Z',  // 过期时间
    'rw',                    // 权限 (read, write)
    'hb',                    // 资源类型 (blob, container)
    'https'                  // 允许协议
);
// 3. 生成可上传的完整 URL
$containerName = 'mycontainer';
$blobName = 'user_upload_' . time() . '.pdf';
$uploadUrl = "https://你的存储账户.blob.core.windows.net/{$containerName}/{$blobName}?{$sasToken}";
// 返回给前端:前端直接使用这个URL进行PUT上传

架构模式建议:Dataverse + 存储的联动

在实际项目中,典型的“Dataverse 与存储”协作模式如下:

  1. 关键数据:客户信息、订单、产品定义 -> 存储于 Dataverse(结构化)。
  2. 用户文件:合同、PDF、图片 -> 存储于 Azure Blob(或 S3/MinIO)。
  3. 关联方式:在 Dataverse 的实体中(如合同实体)添加一个 CustomURLFieldFileUrl 字段,指向 Blob 中的文件。
  4. 安全控制
    • 直接访问存储由 SAS Token 控制失效时间。
    • 读取业务数据由 Dataverse OAuth Token 控制。

环境考虑与部署注意事项

  1. PHP 版本:推荐 PHP 8.1+,因为 Dataverse API 返回强类型 JSON 和自定义 OData 序列化。
  2. 时区:Dataverse 存储的是 UTC 时间,PHP 端需要处理 DateTimeZone 转换。
  3. 性能:避免在 PHP 中循环调用 Dataverse API,尽量使用 $batch 批量操作。
  4. 存储优化:如果使用 Dataverse 的附件功能,务必定期清理 annotationbase 表,避免 Dataverse 数据库膨胀。
  • 已包含的存储(Dataverse 自带的附件/文件列):通过标准的 REST API + OAuth 认证,适合<128MB的文件,与业务记录强绑定。
  • 独立的存储(外部 Blob/S3):适用于大型文件、高并发,PHP 负责生成凭证(SAS/Pre-signed URL),并负责更新 Dataverse 中的引用字段。

建议优先使用外部存储(方案 B),因为 Dataverse 的 I/O 性能不是为高吞吐文件服务设计的,且成本较高,如果你的场景是记录几十MB的PDF合同,且数量不大,可以使用方案 A(注解),如果涉及大文件、视频或海量小文件,必选方案 B。

抱歉,评论功能暂时关闭!