rennokki/dynamodb

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

Laravel 7+ 的 AWS DynamoDB Eloquent ORM

1.6.1 2021-01-20 08:20 UTC

This package is auto-updated.

Last update: 2021-03-01 11:46:54 UTC


README

CI Latest Stable Version Total Downloads Monthly Downloads codecov StyleCI

Laravel DynamoDB

此包是从 Bao Pham 的原始包 分支出来的。

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

安装

使用 Composer 安装包

$ composer require rennokki/dynamodb

如果您的 Laravel 包不支持自动发现,请将以下内容添加到您的 config/app.php 文件中

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

发布配置文件。

php artisan vendor:publish

安装(适用于 Lumen)

对于 Lumen,请尝试 此方法 来安装 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();

用法

扩展模型

通过扩展 Rennokki\DynamoDb\DynamoDbModel 来扩展您的模型,然后您可以使用支持 Eloquent 的方法。这里的想法是您可以在不更改查询的情况下切换回 Eloquent。

use Rennokki\DynamoDb\DynamoDbModel;

class MyModel extends DynamoDbModel
{
    //
}

向模型添加特性(用于同步)

要同步您的数据库表与 DynamoDb 表,请使用特性 Rennokki\DynamoDb\ModelTrait。此特性将在模型保存、更新或删除后调用 PutItem

use Rennokki\DynamoDb\ModelTrait as DynamoDbable;

class MyModel extends Model
{
    use DynamoDbable;
}

查询构建器

您可以使用 查询构建器 外观构建更复杂的查询。

AWS SDK

AWS SDK v3 for PHP 使用 guzzlehttp promises 以允许异步工作流程。使用此包,您可以在 DynamoDb 上异步执行类似 deleteupdatesave 的 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) {
        return $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()

$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();

getItemsCount()

// returns the approximate total count of the table items
$total = Model::getItemsCount(); // ex: 5

查询范围

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]');

// Equivalent of:
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;

$raw 变量是 RawDynamoDbQuery 的一个实例

装饰查询

当您想增强查询时,请使用 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();

索引

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

/**
 * The DynamoDb 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'
    ],
];

请注意,当键存在于多个索引中时,索引的顺序很重要。

例如,对于以下查询,将使用 count_index

$model->where('user_id', 123)->where('count', '>', 10)->get();
protected $dynamoDbIndexKeys = [
    'count_index' => [
        'hash' => 'user_id',
        'range' => 'count'
    ],
    'user_index' => [
        'hash' => 'user_id',
    ],
];

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

$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 Rennokki\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();

常见问题解答

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

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

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

问题:如何使用 Job?收到 SerializesModels 错误。答案:您可以编写自己的 restoreModel 或从您的 Job 中移除 SerializesModels 特性。

安全

如果您发现任何与安全相关的问题,请通过电子邮件 alex@renoki.org 而不是使用问题跟踪器来报告。

致谢

许可

MIT 许可证(MIT)。有关更多信息,请参阅 许可文件