autoprotect-group/php-dynamodb-odm

PHP 对象文档映射器,用于 AWS DynamoDB

v1.1.3 2024-08-27 14:51 UTC

README

Code style, unit and functional tests

这是一个库,也是一个对象文档映射器,可以更方便地与 AWS DynamoDB 一起使用。

用法

配置 ODM

设置原生客户端

$dynamoDbClient = new DynamoDbClient(array_merge(
    [
        'region' => 'eu-west-2',
        'version' => 'latest',
    ]
));

设置主要操作库客户端

$client = new DynamodbOperationsClient($dynamoDbClient);

设置序列化器。原生 AWS 序列化器可能被采用

$marshaler = new Marshaler();

设置查询构建器

$queryBuilder = new QueryBuilder($marshaler, new ExpressionFactory($marshaler));

设置注解读取器和注解管理器

// annotation reader
$annotationReader = new AnnotationReader();

// annotation manager
$annotationManager = new AnnotationManager($annotationReader);

模型的填充器

$newModelHydrator = new Hydrator(NewModel::class, $annotationManager);
$sortKeyModelHydrator = new Hydrator(SortKeyModel::class, $annotationManager);

用于将记录插入数据库的序列化器

// serializer for
$serializer = new Serializer($annotationManager);

完整的示例在这里

模型

模型字段类型

库使用模型进行操作。每个模型可能有各种支持的字段类型。以下是与 PHP 和 DynamoDb 类型相关联的类型列表

  • BooleanType:DynamoDb 和 PHP 中的布尔值
  • CollectionType:DynamoDb 中的列表,在 PHP 中是特定模型项的数组列表
  • DateType:DynamoDb 中的字符串,PHP 中的 DateTime
  • EnumType:DynamoDb 中的字符串,PHP 中的枚举
  • FloatType:DynamoDb 中的数字,PHP 中的浮点数
  • HashMapType:DynamoDb 中的映射,在 PHP 中是特定模型项的关联数组
  • IntegerType:DynamoDb 中的数字,PHP 中的整型
  • ModelType:DynamoDb 中的映射,PHP 中的模型实例
  • Money:DynamoDb 中的映射,PHP 中的特殊 MoneyObject。作为Money 值的概念
  • NumberType:DynamoDb 中的数字。一个抽象类型,不是特别方便。偶尔可能会用到
  • ScalarCollectionType:DynamoDb 中的映射。在 PHP 中是任何 DynamoDb 兼容类型的关联数组,但不包括 CollectionTypeHashMapTypeModelType
  • StringType:DynamoDb 和 PHP 中的字符串

以下是模型示例

class ExampleDemoModel extends Model
{
    protected const TABLE_NAME = 'test-table';
    
    // Primary means that this is a partition key for the DynamoDb table
    #[StringType, Primary]  protected string $id;
    #[StringType]           protected string $name;
    #[FloatType]            protected float $price;
    #[Money]                protected Money $priceNet;
    #[FloatType]            protected float $percent;
    #[IntegerType]          protected int $itemsAmount;
    #[DateType]             protected DateTime $createdAt;
    #[BooleanType]          protected bool $isDeleted;
    #[BooleanType]          protected bool $isPhoneNumber;
    #[ModelType([ModelType::MODEL_CLASS_NAME => RelatedModel::class])]
    protected RelatedModel $buyer;
    #[CollectionType([CollectionType::MODEL_CLASS_NAME => RelatedModel::class])]
    protected array $buyers;
    #[ModelType([Asset::MODEL_CLASS_NAME => Asset::class])]
    protected Asset $asset;
    #[HashMapType([HashMapType::MODEL_CLASS_NAME => RelatedModel::class])]
    protected array $buyersMap;
    
    // getter and setters should be here    
 }

完整的模型示例在这里:model.php

枚举示例

枚举也受到支持。以下是具有枚举字段的模型示例

class ModelWithEnumeration extends Model
{
    #[Primary, StringType]
    protected string $id;

    #[EnumType]
    protected OrderStatus $orderStatus;
    
    // union types
    #[EnumType]
    protected OrderStatus|ApplicationStatus $unionStatus;

    // union types with null
    #[EnumType]
    protected OrderStatus|ApplicationStatus|null $unionNullableStatus;

    // isStrict means the value will be null in case wrong value comes from the DB
    #[EnumType(isStrict: false)]
    protected ?OrderStatus $orderStatusAdditional = null;

    #[EnumType]
    protected CustomerType $customerType;
}

字段加密

某些类型的自定义加密受到支持。如果有某些字段需要加密。

首先我们需要创建一个自定义加密器

MyEncryptor implements EncryptorInterface {
    protected const ENCRYPTION_KEY = 'def000008053addc0f94b14c0e480a10631a0a970b3565e5a7a2aeaeeb51a39e2d139a8977bc02be0195f0036a29aefff9df6d2ddb81432d14b4dce82b83b3a95c6d0205';

    public function decrypt(string|array $encryptedData, array $options = []): string|array
    {
        // any decryption way may be implemented
        if (is_array($encryptedData)) {
            // ...specific property decryption operations...
            return $encryptedData;
        }
        
        return Crypto::decrypt(
            $encryptedData,
            Key::loadFromAsciiSafeString(static::ENCRYPTION_KEY)
        );
    }
}

然后应该将解密器传递给填充器

$newModelHydrator = new Hydrator(
    EncryptionDemoModel::class,
    $annotationManager,
    new MyEncryptor(),
);

模型可能如下所示

class EncryptionDemoModel extends Model
{
    #[Key\Primary, Types\StringType]
    protected string $id;

    // ability to encrypt a specific property in a scalar associative  array 
    #[Types\ScalarCollectionType, Encrypted(["encryptedProperty" => "secretProperty"])]
    protected array $encryptedArray;

    #[Types\StringType, Encrypted]
    protected string $encryptedName;
}

为您的模型设置存储库

操作记录的最佳方式是创建一个存储库。已经内置了一个

$newModelDynamoDbRepository = new DynamoDBRepository(
    NewModel::class,
    $client,
    $queryBuilder,
    $newModelHydrator,
    $annotationManager,
    $marshaler,
    $serializer
);

默认存储库中包含内置操作。

按分区 ID 获取模型

仅通过分区键
$foundModel = $newModelDynamoDbRepository->get($id);
通过分区键和排序键
$foundModel = $newModelDynamoDbRepository->get($id, $sortKey);
非一致读取
$foundModel = $newModelDynamoDbRepository->get($id, $sortKey, false);
获取一个项目
$foundModel = $newModelDynamoDbRepository->getOneById($id, $sortKey, false);

插入模型

$newModelDynamoDbRepository->save($model);

删除项

$newModelDynamoDbRepository->delete($model);

文档存储库

有时我们需要获取的不是整个模型,而只是它的一部分。为此,存在所谓的 DocumentRepository。可以使用原生 DynamoDb 投影表达式技术获取文档的一部分。

设置 DocumentRepository

$documentRepository = new DocumentRepository(
    NewModelNested::class,
    $client,
    $this->queryBuilder,
    $this->newModelHydrator,
    $annotationManager,
    $marshaler,
    $serializer
);

通过投影表达式获取模型

$projectionExpression = "property.subPropertyModel";

$model = $documentRepository->getDocument()
    ->setConsistentRead(true)
    ->withAttrPath($projectionExpression)
    ->withPrKey($keyValue)
    ->execute()
;

通过投影表达式获取特定的标量属性

$projectionExpression = "property.subPropertyModel.name";

$name = $this->documentRepository->getDocumentProperty()
    ->setConsistentRead(true)
    ->withAttrPath($projection)
    ->withPrKey($keyValue)
    ->execute()
;

获取/创建/更新/删除操作

文档存储库支持特定的属性获取/创建/更新/删除操作

  • createDocument()
  • updateDocument()
  • removeDocument()
  • getDocumentCollection()
  • updateDocumentCollection()
  • createDocumentCollection()

查询构建器

另一个强大的功能是查询构建器。这增加了通过DynamoDB支持的特定标准检索项的灵活性。

这是使用原始查询和结果与DynamoDB交互的一种方式。

获取查询构建器

检索项

$getItemQuery = $queryBuilder
    ->getItem(self::DB_TABLE)
    ->itemKey([$itemKey => $keyValue])
    ->getQuery();
    
$item = $this->dynamoDbClient
    ->getItem($getItemQuery)->get('Item');

更新查询构建器

能够更新特定属性。

$attributesForUpdate = [
   "numberProp" => 2, 
   "stringProp" => "updated string value", 
   "hashMapProp.map-id-1.type" => "updated map-type-1", 
   "hashMapProp.map-id-1.mapProp" => "updated mapProp", 
   "listProp" => [
         "updated listProp 1", 
         "updated listProp 2" 
      ] 
]; 

$getItemQuery = $queryBuilder
    ->updateItem(self::DB_TABLE)
    ->itemKey([$itemKey => $keyValue])
    ->attributes($attributesForUpdate)
    ->getQuery();

$dynamoDbClient->updateItem($getItemQuery);

本地开发环境安装

  1. 为了构建开发镜像,请运行以下命令:
docker-compose build
  1. 然后运行以下命令安装依赖项:
docker-compose run --no-deps dynamodb-odm composer install

运行测试

单元测试

此包使用phpspec运行单元测试。

使用以下方式运行它们:

docker-compose run --no-deps dynamodb-odm vendor/bin/phpspec run

可以在.env.local文件中使用环境变量来调试库。为此,只需将文件.env.local.sample复制到.env.local,并根据您的操作系统设置变量。

然后使用以下命令运行测试:

docker-compose --env-file ./.env.local run  --no-deps dynamodb-odm vendor/bin/phpspec run

功能测试

此包使用behat运行功能测试。

然后直接运行测试

docker-compose run dynamodb-odm vendor/bin/behat -c behat.yml --stop-on-failure

语法检查测试

您需要通过运行以下命令来检查代码风格是否正确:

docker-compose run --no-deps dynamodb-odm vendor/bin/phpcs --standard=/application/phpcs.xml