laravie/eloquent-dynamodb

此包已被废弃,不再维护。作者建议使用 baopham/laravel-dynamodb 包。

DynamoDB 的 Eloquent 语法

v1.3.1 2020-03-03 02:21 UTC

This package is auto-updated.

Last update: 2022-04-11 09:38:52 UTC


README

Latest Stable Version Total Downloads Latest Unstable Version tests License

支持所有关键类型 - 主哈希键和组合键。

仅适用于高级用户。如果您不熟悉 Laravel、Laravel Eloquent 和 DynamoDB,则建议您首先熟悉这些。

安装

要通过 composer 安装,请在终端中运行以下命令

composer require "laravie/eloquent-dynamodb"

Laravel 设置

只有当您禁用包发现时,请将以下服务提供程序添加到 config/app.php 下的 providers

    // config/app.php

    'providers' => [
        ...
        Laravie\DynamoDb\DynamoDbServiceProvider::class,
        ...
    ];

接下来,您可能想使用以下命令发布配置文件

php artisan vendor:publish --provider="Laravie\DynamoDb\DynamoDbServiceProvider"

然后,您可以在 config/dynamodb.php 中更新 DynamoDb 配置,或使用 .env 配置值。

Lumen 设置

您可以选择手动复制配置文件,或使用 laravelista/lumen-vendor-publish 安装 vendor:publish 命令。

接下来,加载配置文件并在 bootstrap/app.php 中启用 Eloquent 支持

$app = new Laravel\Lumen\Application(
    realpath(__DIR__.'/../')
);

// Load dynamodb config file
$app->configure('dynamodb');
   
// Enable Eloquent support
$app->withEloquent();

然后,您可以在 config/dynamodb.php 中更新 DynamoDb 配置,或使用 .env 配置值。

用法

  • 扩展模型以使用 Laravie\DynamoDb\DynamoDbModel,然后您可以使用受支持的 Eloquent 方法。这里的想法是您可以切换回 Eloquent 而不更改您的查询。
  • 或者,如果您想将数据库表与 DynamoDb 表同步,请使用 trait Laravie\DynamoDb\ModelTrait,它将在模型保存后调用 PutItem
  • 或者,您可以使用 查询构建器 门面来构建更复杂的查询。
  • AWS SDK v3 for PHP 使用 guzzlehttp promises 允许异步工作流程。使用此包,您可以在 DynamoDb 上异步执行类似 删除更新保存 的 Eloquent 查询。

支持的功能

find() 和 delete()

$model->find($id, array $columns = []);
$model->findMany($ids, array $columns = []);
$model->delete();
$model->deleteAsync()->wait();

条件

// Using getIterator()
// If 'key' is the primary key or a global/local index and it is a supported Query condition,
// will use 'Query', otherwise 'Scan'.
$model->where('key', 'key value')->get();

$model->where(['key' => 'key value']);

// Chainable for 'AND'.
$model->where('foo', 'bar')
    ->where('foo2', '!=' 'bar2')
    ->get();
    
// Chainable for 'OR'.
$model->where('foo', 'bar')
    ->orWhere('foo2', '!=' 'bar2')
    ->get();
 
// Other types of conditions
$model->where('count', '>', 0)->get();
$model->where('count', '>=', 0)->get();
$model->where('count', '<', 0)->get();
$model->where('count', '<=', 0)->get();
$model->whereIn('count', [0, 100])->get();
$model->whereNotIn('count', [0, 100])->get();
$model->where('count', 'between', [0, 100])->get();
$model->where('description', 'begins_with', 'foo')->get();
$model->where('description', 'contains', 'foo')->get();
$model->where('description', 'not_contains', 'foo')->get();

// Nested conditions
$model->where('name', 'foo')
    ->where(function ($query) {
        $query->where('count', 10)->orWhere('count', 20);
    })
    ->get();

// Nested attributes
$model->where('nestedMap.foo', 'bar')->where('list[0]', 'baz')->get();
whereNull() 和 whereNotNull()

NULL 和 NOT_NULL 只检查属性是否存在,而不是其值是否为 NULL
请参阅: http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Condition.html

$model->whereNull('name');
$model->whereNotNull('name');

all() 和 first()

// Using scan operator, not too reliable since DynamoDb will only give 1MB total of data.
$model->all();

// Basically a scan but with limit of 1 item.
$model->first();

分页

遗憾的是,对于 DynamoDb 来说,跳过多少条记录的偏移量没有意义。相反,提供前一个查询的最后结果作为下一个查询的起点。

示例

对于查询

$query = $model->where('count', 10)->limit(2);
$items = $query->all();
$last = $items->last();

将此查询结果的最后一项作为下一个 "偏移量"。

$nextPage = $query->after($last)->limit(2)->all();
// or
$nextPage = $query->afterKey($items->lastKey())->limit(2)->all();
// or (for query without index condition only)
$nextPage = $query->afterKey($last->getKeys())->limit(2)->all();

update()

// update
$model->update($attributes);

updateAsync()

// update asynchronously and wait on the promise for completion.
$model->updateAsync($attributes)->wait();

save()

$model = new Model();
// Define fillable attributes in your Model class.
$model->fillableAttr1 = 'foo';
$model->fillableAttr2 = 'foo';
// DynamoDb doesn't support incremented Id, so you need to use UUID for the primary key.
$model->id = 'de305d54-75b4-431b-adb2-eb6b9e546014';
$model->save();

saveAsync()

异步保存单个模型并等待完成。

$model = new Model();
// Define fillable attributes in your Model class.
$model->fillableAttr1 = 'foo';
$model->fillableAttr2 = 'bar';
// DynamoDb doesn't support incremented Id, so you need to use UUID for the primary key.
$model->id = 'de305d54-75b4-431b-adb2-eb6b9e546014';
$model->saveAsync()->wait();

异步保存多个模型并等待所有模型同时完成。

for($i = 0; $i < 10; $i++){
    $model = new Model();
    // Define fillable attributes in your Model class.
    $model->fillableAttr1 = 'foo';
    $model->fillableAttr2 = 'bar';
    // DynamoDb doesn't support incremented Id, so you need to use UUID for the primary key.
    $model->id = uniqid();
    // Returns a promise which you can wait on later.
    $promises[] = $model->saveAsync();
}

\GuzzleHttp\Promise\all($promises)->wait();

delete()

$model->delete();

deleteAsync()

$model->deleteAsync()->wait();

chunk()

$model->chunk(10, function ($records) {
    foreach ($records as $record) {

    }
});

limit() 和 take()

// Use this with caution unless your limit is small.
// DynamoDB has a limit of 1MB so if your limit is very big, the results will not be expected.
$model->where('name', 'foo')->take(3)->get();

firstOrFail()

$model->where('name', 'foo')->firstOrFail();
// for composite key
$model->where('id', 'foo')->where('id2', 'bar')->firstOrFail();

findOrFail()

$model->findOrFail('foo');
// for composite key
$model->findOrFail(['id' => 'foo', 'id2' => 'bar']);

refresh()

$model = Model::first();
$model->refresh();

查询范围

class Foo extends DynamoDbModel
{
    protected static function boot()
    {
        parent::boot();

        static::addGlobalScope('count', function (DynamoDbQueryBuilder $builder) {
            $builder->where('count', '>', 6);
        });
    }

    public function scopeCountUnderFour($builder)
    {
        return $builder->where('count', '<', 4);
    }

    public function scopeCountUnder($builder, $count)
    {
        return $builder->where('count', '<', $count);
    }
}

$foo = new Foo();
// Global scope will be applied
$foo->all();
// Local scope
$foo->withoutGlobalScopes()->countUnderFour()->get();
// Dynamic local scope
$foo->withoutGlobalScopes()->countUnder(6)->get();

REMOVE — 从项中删除属性

请参阅: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.REMOVE

$model = new Model();
$model->where('id', 'foo')->removeAttribute('name', 'description', 'nested.foo', 'nestedArray[0]');

// Or
Model::find('foo')->removeAttribute('name', 'description', 'nested.foo', 'nestedArray[0]');

toSql() 风格

出于调试目的,您可以选择将其转换为实际的 DynamoDb 查询

$raw = $model->where('count', '>', 10)->toDynamoDbQuery();
// $op is either "Scan" or "Query"
$op = $raw->op;
// The query body being sent to AWS
$query = $raw->query;

其中 $rawRawDynamoDbQuery 的实例

装饰查询

当您想增强查询时使用 decorate。例如

设置排序键的顺序

$items = $model
    ->where('hash', 'hash-value')
    ->where('range', '>', 10)
    ->decorate(function (RawDynamoDbQuery $raw) {
        // desc order
        $raw->query['ScanIndexForward'] = false;
    })
    ->get();

如果库未能检测到正确的操作,则强制使用 "Query" 而不是 "Scan"

$items = $model
    ->where('hash', 'hash-value')
    ->decorate(function (RawDynamoDbQuery $raw) {
        $raw->op = 'Query';
    })
    ->get();

索引

如果您的表有索引,请确保在您的模型类中声明它们,如下所示

/**
 * Indexes.
 * [
 *     '<simple_index_name>' => [
 *          'hash' => '<index_key>'
 *     ],
 *     '<composite_index_name>' => [
 *          'hash' => '<index_hash_key>',
 *          'range' => '<index_range_key>'
 *     ],
 * ]
 *
 * @var array
 */
protected $dynamoDbIndexKeys = [
    'count_index' => [
        'hash' => 'count'
    ],
];

请注意,当键存在于多个索引中时,索引的顺序很重要。
例如,我们有这个

$model->where('user_id', 123)->where('count', '>', 10)->get();

protected $dynamoDbIndexKeys = [
    'count_index' => [
        'hash' => 'user_id',
        'range' => 'count'
    ],
    'user_index' => [
        'hash' => 'user_id',
    ],
];

将使用 count_index

protected $dynamoDbIndexKeys = [
    'user_index' => [
        'hash' => 'user_id',
    ],
    'count_index' => [
        'hash' => 'user_id',
        'range' => 'count'
    ]
];

将使用 user_index

大多数情况下,您不需要做任何事情,但如果您需要使用特定的索引,您可以像这样指定它

$model->where('user_id', 123)->where('count', '>', 10)->withIndex('count_index')->get();

复合键

要使用模型中的复合键

  • $compositeKey 设置为包含键属性名的数组,例如
protected $primaryKey = 'customer_id';
protected $compositeKey = ['customer_id', 'agent_id'];
  • 要找到具有复合键的记录
$model->find(['customer_id' => 'value1', 'agent_id' => 'value2']);

查询构建器

使用 DynamoDb 门面构建原始查询

use Laravie\DynamoDb\Facades\DynamoDb;

DynamoDb::table('articles')
    // call set<key_name> to build the query body to be sent to AWS
    ->setFilterExpression('#name = :name')
    ->setExpressionAttributeNames(['#name' => 'author_name'])
    ->setExpressionAttributeValues([':name' => DynamoDb::marshalValue('Bao')])
    ->prepare()
    // the query body will be sent upon calling this.
    ->scan(); // supports any DynamoDbClient methods (e.g. batchWriteItem, batchGetItem, etc.)
  
DynamoDb::table('articles')
    ->setIndex('author_name')
    ->setKeyConditionExpression('#name = :name')
    ->setProjectionExpression('id, author_name')
    // Can set the attribute mapping one by one instead
    ->setExpressionAttributeName('#name', 'author_name')
    ->setExpressionAttributeValue(':name', DynamoDb::marshalValue('Bao'))
    ->prepare()
    ->query();

DynamoDb::table('articles')
    ->setKey(DynamoDb::marshalItem(['id' => 'ae025ed8']))
    ->setUpdateExpression('REMOVE #c, #t')
    ->setExpressionAttributeName('#c', 'comments')
    ->setExpressionAttributeName('#t', 'tags')
    ->prepare()
    ->updateItem();

DynamoDb::table('articles')
    ->setKey(DynamoDb::marshalItem(['id' => 'ae025ed8']))
    ->prepare()
    ->deleteItem();

DynamoDb::table('articles')
    ->setItem(DynamoDb::marshalItem(['id' => 'ae025ed8', 'author_name' => 'New Name']))
    ->prepare()
    ->putItem();

// Or, instead of ::table()
DynamoDb::newQuery()
    ->setTableName('articles')

// Or access the DynamoDbClient instance directly
DynamoDb::client();
// pass in the connection name to get a different client instance other than the default.
DynamoDb::client('test');

查询构建器方法的形式为 set<key_name>,其中 <key_name> 是要发送的查询体中的键名称。

例如,构建一个 UpdateTable 查询

[
    'AttributeDefinitions' => ...,
    'GlobalSecondaryIndexUpdates' => ...,
    'TableName' => ...
]

$query = DynamoDb::table('articles')
    ->setAttributeDefinitions(...)
    ->setGlobalSecondaryIndexUpdates(...);

准备好后

$query->prepare()->updateTable();

常见问题解答 (FAQ)

问题:如果不在可填充数组中,无法分配 id 属性
答案:尝试 这个

问题:如何创建迁移?
答案:请参阅 这个问题

问题:如何与工厂一起使用?
答案:请参阅 这个问题

问题:如何与作业一起使用?得到 SerializesModels 错误
答案:您可以 自己编写 restoreModel 或者从您的作业中移除 SerializesModels 特性。

作者和贡献者