justraviga/laravel-dynamodb-extreme

JustRaviga DynamoDb 访问包

v1.0.0 2023-06-08 12:07 UTC

This package is auto-updated.

Last update: 2024-09-09 18:30:04 UTC


README

这是一个用于 DynamoDb 访问的查询构建器包。受其他不够优雅版本(看我在这里做了什么?)的启发,这个版本具有以下特性,就像 Laravel 一样

从 DynamoDb 获取单个 Model 实例

$model = Model::find($partitionKey, $sortKey);

或者,如果没有返回结果,则抛出 Illuminate\Database\Eloquent\ModelNotFoundException

$model = Model::findOrFail($partitionKey, $sortKey);

从 DynamoDb 获取 Model 实例的集合

注意

  1. 这里只支持单个 Model 类型。你必须确保你的查询只会返回相同类型的模型。
  2. 如果数据库中找到的属性不在模型的 $fillable 数组中,将抛出异常。
  3. DynamoDb 只支持 Partition 键的精确匹配,以及 Sort 键上的 <<==>=>begins_withbetween 匹配。
/** @var DynamoDbResult $result */
$result = Model::where('partitionKey', $partitionKey)
    ->where('sortKey', 'begins_with', 'MODEL#')
    ->get();

/** @var \Illuminate\Support\Collection<DynamoDbModel> $models */
$models = $result->results;

你可以选择性地根据 sortKey 排序并限制结果数量

/** @var DynamoDbResult $models */
$models = Model::where('partitionKey', $partitionKey)
    ->sortDescending()
    ->limit(10)
    ->get();

如果你知道你将要超出 DynamoDb 的 1MB 查询大小限制,你可以使用 getAll() 方法进行多次查询,直到返回整个结果集

/** @var DynamoDbResult $models */
$models = Model::where('partitionKey', $partitionKey)
    ->getAll();

你也可以通过组合 limit()after() 来获取特定的一组结果

$twoResults = Model::where('partitionKey', $partitionKey)
    ->limit(2)
    ->get();

$twoMoreResults = Model::where('partitionKey', $partitionKey)
    ->after($twoResults->lastEvaluatedKey)
    ->limit(2)
    ->get();

这是 paginate 方法中内置的快捷方式

$twoResults = Model::where('partitionKey', $partitionKey)
    ->limit(2)
    ->paginate();

$twoMoreResults = Model::where('partitionKey', $partitionKey)
    ->limit(2)
    ->paginate($twoResults->lastEvaluatedKey());

使用辅助索引获取模型集合

只要辅助索引配置正确(见下文),它就会从你正在查询的域中检测到。

/** @var \Illuminate\Support\Collection<Model> $models */
$models = Model::where('index_partition_key', $partitionKey)
    ->where('index_sort_key', $sortKey)
    ->get();

在 DynamoDb 中创建 Model 实例

立即使用 create 持久化模型

$model = Model::create([
    'partitionKey' => 'value',
    'sortKey' => 'value',
    // other attributes...
]);

使用 make 在内存中构建模型而不进行数据库查询

$model = Model::make([
    'partitionKey' => 'value',
    'sortKey' => 'sortKey',
    // other attributes...
]);

// persist the model
$model->save();

或者使用 new Model() 语法

$model = new Model([
    'partitionKey' => 'value',
    'sortKey' => 'value',
    // other attributes
]);

// persist the model
$model->save();

访问模型属性

创建你的模型后,属性访问方式与 Laravel 相同,使用对象属性语法

$model = Model::find($partitionKey, $sortKey);

$model->someAttribute = 'value';

你可以使用 fill 方法同时更新多个属性

$model = Model::find($partitionKey, $sortKey);
$model->fill([
    'someAttribute' => 'value',
    // other attributes
]);

这些更改不会自动持久化回 Dynamo,你需要手动保存它们

$model = Model::find($partitionKey, $sortKey);

$model->someAttribute = 'value';
$model->save();

或者

$model = Model::find($partitionKey, $sortKey);

$model->fill([
    'someAttribute' => 'value',
])->save();

或者,甚至可以将 fillsave 结合起来

$model = Model::find($partitionKey, $sortKey);

$model->update([
    'someAttribute' => 'value',
]);

设置和全局配置

环境变量

  • DYNAMODB_REGION 默认为 localhost,应设置为你的主 DynamoDb 实例区域(例如 eu-west-2)
  • DYNAMODB_VERSION 默认为 latest,如果需要,应设置为你的 DynamoDb 实例版本
  • DYNAMODB_KEY 你的 DynamoDb 访问密钥(用户名)
  • DYNAMODB_SECRET 你的 DynamoDb 密钥(密码)
  • DYNAMODB_ENDPOINT 默认为 https://:8000,这是你的 DynamoDb 安装地址
  • DYNAMODB_TABLE 定义所有模型的默认表(当在特定应用程序中使用单表设计时很有用)
  • DYNAMODB_CONSISTENT_READ 默认为 true,用于设置默认的一致性读取值(仍然可以被特定模型覆盖)
  • DYNAMODB_LOG_QUERIES 默认为 false,用于为所有 DynamoDb 查询添加日志(发送到 Dynamo 的 json 对象将被记录)

配置选项

可以使用 Laravel 的 publish 命令导出配置文件: php artisan vendor:publish,它看起来像这样

[
    'region' => env('DYNAMODB_REGION', 'localhost'),
    'version' => env('DYNAMODB_VERSION', 'latest'),
    'credentials' => [
        'key' => env('DYNAMODB_KEY', ''),
        'secret' => env('DYNAMODB_SECRET', ''),
    ],
    'endpoint' => env('DYNAMODB_ENDPOINT', 'https://:8000'),
    'defaults' => [
        'consistent_read' => env('DYNAMODB_CONSISTENT_READ', true),
        'table' => env('DYNAMODB_TABLE', 'default'),
        'partition_key' => 'pk',
        'sort_key' => 'sk',
        'global_secondary_indexes' => [
            'gsi1' => [
                'pk' => 'gsi1_pk',
                'sk' => 'gsi1_sk',
            ]
        ],
        'log_queries' => env('DYNAMODB_LOG_QUERIES', false),
    ],
]

除了基于环境的配置之外,你还可以指定分区键、排序键和全局辅助索引的默认值。这些默认值是基于 DynamoDb 的一般使用模式而设定的。

模型配置

创建模型很容易。至少,你需要定义表名、partitionKey 和 sortKey,以及一个“fillable”属性数组(注意,分区和排序键必须是可填充的!)

class Model extends \DynamoDb\Models\DynamoDbModel
{
    // optional, see environment variable for table name
    protected string $table = 'models';
    
    // optional, see environment variable for partition/sort keys
    protected string $partitionKey = 'pk';
    protected string $sortKey = 'sk';
    
    // required!  must include at least partition/sort keys
    public array $fillable = [
        'pk',
        'sk',
        // other attributes...
    ];
}

为了编写更详细的代码,您可以通过覆盖受保护的属性 fieldMappings 来创建从内部/数据库字段到友好属性的映射。映射在从数据库获取数据以及将数据保存到数据库时应用。其余时间,您始终使用映射的属性名称。

class Model extends \DynamoDb\Models\DynamoDbModel
{
    // optional
    protected array $fieldMappings = [
        'pk' => 'my_uuid',
        'sk' => 'created_date',
    ];
}

默认情况下,所有模型的分区键都是以模型类名后跟一个 # 然后是一个 UUID-7(时间顺序 UUID)来构建的。排序键默认为模型名称。例如

class Model extends \DynamoDb\Models\DynamoDbModel { }

$model = new Model();
// partition key = 'MODEL#{uuid-7}
// sort key = 'MODEL'

您也可以通过覆盖这些方法在创建新模型时更改这些默认值

class Model extends \DynamoDb\Models\DynamoDbModel
{
    public function defaultPartitionKey(): string
    {
        return 'MODEL#' . random_int(0,1000);    
    }
    
    public function defaultSortKey(): string
    {
        return 'CREATED_AT#' . now()->toISOString();
    }
    
    public function defaultValues(): array
    {
        return [
            'attribute' => 'default value',        
        ];
    }
}

相关模型

您可以通过一些额外的设置来配置具有相同分区键的相关模型

class ParentModel extends \DynamoDb\Models\DynamoDbModel
{
    public function childModels(): \DynamoDb\DynamoDb\Relation
    {
        return $this->addRelation(ChildModel::class);
    }
}

默认情况下,这使用父模型的分区键,并通过“begins_with”查询与子模型类名后跟一个哈希(#)进行匹配,例如 begins_with(sk, 'CHILDMODEL#')。匹配本身可以通过在子模型上覆盖 relationSearchParams 方法进行配置

class ChildModel extends \DynamoDb\Models\DynamoDbModel
{
    public static function relationSearchParams(): array
    {
        return [
            'sk',
            'begins_with',
            'CLASSNAME#'        
        ];
    }
}

您可以使用与关系方法同名的属性来作为集合访问相关模型

class Model extends \DynamoDb\Models\DynamoDbModel
{
    public function children(): \DynamoDb\DynamoDb\Relation
    {
        return $this->addRelation(Child::class);
    }
}

$model = Model::find(...);

$model->children->map(fn ($child) => $child->doSomething());

“内联”关系

除了在 DynamoDb 表的不同行中保存相关模型外,您还可以使用记录上的 JSON 属性作为关系。我们称这种关系为“内联关系”,并且它们可以像其他关系一样访问

class Model extends \DynamoDb\Models\DynamoDbModel
{
    public function inline(): InlineRelation
    {
        return $this->addInlineRelation(Inline::class, 'inline');
    }
}

$model = Model::find(...);

$model->inline->map(fn ($child) => $child->doSomething());

如果您想在表上使用辅助索引,只需在 $indexes 数组中定义分区和排序键名称。请确保将它们添加到 $fillable 数组中。

class Model extends \DynamoDb\Models\DynamoDbModel
{
    // optional, see config values
    protected array $indexes = [
        'gsi1' => [
            'pk' => 'gsi1_pk',
            'sk' => 'gsi1_sk',
        ],
    ];
    
    // required if you want to use a secondary index
    public array $fillable = [
        'gsi1_pk',
        'gsi1_sk',
        // other attributes...    
    ];
}

您也可以在 $fieldMappings 数组中定义从辅助索引到友好模型属性的映射。

class Model extends \JustRaviga\LaravelDynamodbExtreme\Models\DynamoDbModel
{
    // optional
    protected array $fieldMappings = [
        'gsi1_pk' => 'user_uuid',
        'gsi1_sk' => 'created_date',
    ];
}

最低要求

  1. PHP 8.2
  2. Laravel 10.0

愿望清单

我们可能想要包括但最终没有时间充分考虑的事情

将 DynamoDb 响应包装在缓存层中

可选地将从 DynamoDb 获取的模型存储在内存中,以便在请求期间重复使用相同的分区/排序键而无需查询 Dynamo。

属性转换

类似于 Laravel 模型的 $casts 数组,在从 DynamoDb 获取和保存时,应将这些数据类型转换为属性。

扩展 Collection 类以包含基于 DynamoDb 模型的过滤方法

Collection 类上的新方法,用于基于 DynamoDb 的 Last Evaluated Key 进行分页。

更好的文档 🙈

文档总是可以改进的。

并发请求

找出如何进行许多可以 await 的并发请求,并且只有当所有请求都返回其数据时才继续执行。

代码生成表

基本上是 Laravel 的 DynamoDb 表迁移系统

级联删除

在删除父模型后删除相关模型