laravie/eloquent-dynamodb
最新稳定版本:v1.3.1
Composer 安装命令:
composer require laravie/eloquent-dynamodb
包简介
Eloquent syntax for DynamoDB
README 文档
README
Supports all key types - primary hash key and composite keys.
For advanced users only. If you're not familiar with Laravel, Laravel Eloquent and DynamoDB, then I suggest that you get familiar with those first.
Installation
To install through composer, run the following command from terminal:
composer require "laravie/eloquent-dynamodb"
Laravel setup
Only if you disable package discovery, please add the following service provider to config/app.php under providers:
// config/app.php 'providers' => [ ... Laravie\DynamoDb\DynamoDbServiceProvider::class, ... ];
Next you may want to publish the configuration file using the following command:
php artisan vendor:publish --provider="Laravie\DynamoDb\DynamoDbServiceProvider"
You can then update DynamoDb config in config/dynamodb.php or configurate the value using
.env.
Lumen setup
You can either copy the configuration file manually or use laravelista/lumen-vendor-publish to install the vendor:publish command.
Next, load configuration file and enable Eloquent support in bootstrap/app.php:
$app = new Laravel\Lumen\Application( realpath(__DIR__.'/../') ); // Load dynamodb config file $app->configure('dynamodb'); // Enable Eloquent support $app->withEloquent();
You can then update DynamoDb config in config/dynamodb.php or configurate the value using
.env.
Usages
- Extends your model with
Laravie\DynamoDb\DynamoDbModel, then you can use Eloquent methods that are supported. The idea here is that you can switch back to Eloquent without changing your queries. - Or if you want to sync your DB table with a DynamoDb table, use trait
Laravie\DynamoDb\ModelTrait, it will call aPutItemafter the model is saved. - Alternatively, you can use the query builder facade to build more complex queries.
- AWS SDK v3 for PHP uses guzzlehttp promises to allow for asynchronous workflows. Using this package you can run eloquent queries like delete, update, save asynchronously on DynamoDb.
Supported features:
find() and delete()
$model->find($id, array $columns = []); $model->findMany($ids, array $columns = []); $model->delete(); $model->deleteAsync()->wait();
Conditions
// 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() and whereNotNull()
NULL and NOT_NULL only check for the attribute presence not its value being null
See: http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Condition.html
$model->whereNull('name'); $model->whereNotNull('name');
all() and 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();
Pagination
Unfortunately, offset of how many records to skip does not make sense for DynamoDb. Instead, provide the last result of the previous query as the starting point for the next query.
Examples:
For query such as:
$query = $model->where('count', 10)->limit(2); $items = $query->all(); $last = $items->last();
Take the last item of this query result as the next "offset":
$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()
Saving single model asynchronously and waiting on the promise for completion.
$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();
Saving multiple models asynchronously and waiting on all of them simultaneously.
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() and 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();
Query Scope
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 — Deleting Attributes From An Item
$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() Style
For debugging purposes, you can choose to convert to the actual DynamoDb query
$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;
where $raw is an instance of RawDynamoDbQuery
Decorate Query
Use decorate when you want to enhance the query. For example:
To set the order of the sort key:
$items = $model ->where('hash', 'hash-value') ->where('range', '>', 10) ->decorate(function (RawDynamoDbQuery $raw) { // desc order $raw->query['ScanIndexForward'] = false; }) ->get();
To force to use "Query" instead of "Scan" if the library fails to detect the correct operation:
$items = $model ->where('hash', 'hash-value') ->decorate(function (RawDynamoDbQuery $raw) { $raw->op = 'Query'; }) ->get();
Indexes
If your table has indexes, make sure to declare them in your model class like so
/** * 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' ], ];
Note that order of index matters when a key exists in multiple indexes.
For example, we have this
$model->where('user_id', 123)->where('count', '>', 10)->get();
with
protected $dynamoDbIndexKeys = [ 'count_index' => [ 'hash' => 'user_id', 'range' => 'count' ], 'user_index' => [ 'hash' => 'user_id', ], ];
will use count_index.
protected $dynamoDbIndexKeys = [ 'user_index' => [ 'hash' => 'user_id', ], 'count_index' => [ 'hash' => 'user_id', 'range' => 'count' ] ];
will use user_index.
Most of the time, you should not have to do anything but if you need to use a specific index, you can specify it like so
$model->where('user_id', 123)->where('count', '>', 10)->withIndex('count_index')->get();
Composite Keys
To use composite keys with your model:
- Set
$compositeKeyto an array of the attributes names comprising the key, e.g.
protected $primaryKey = 'customer_id'; protected $compositeKey = ['customer_id', 'agent_id'];
- To find a record with a composite key
$model->find(['customer_id' => 'value1', 'agent_id' => 'value2']);
Query Builder
Use DynamoDb facade to build raw queries
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');
The query builder methods are in the form of set<key_name>, where <key_name> is the key name of the query body to be sent.
For example, to build an UpdateTable query:
[
'AttributeDefinitions' => ...,
'GlobalSecondaryIndexUpdates' => ...,
'TableName' => ...
]
Do:
$query = DynamoDb::table('articles') ->setAttributeDefinitions(...) ->setGlobalSecondaryIndexUpdates(...);
And when ready:
$query->prepare()->updateTable();
FAQ
Q: Cannot assign id property if its not in the fillable array
A: Try this?
Q: How to create migration?
A: Please see this issue
Q: How to use with factory?
A: Please see this issue
Q: How do I use with Job? Getting a SerializesModels error
A: You can either write your own restoreModel or remove the SerializesModels trait from your Job.
Author and Contributors
laravie/eloquent-dynamodb 适用场景与选型建议
laravie/eloquent-dynamodb 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 8.24k 次下载、GitHub Stars 达 5, 最近一次更新时间为 2019 年 11 月 12 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「aws」 「dynamodb」 「laravel」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 laravie/eloquent-dynamodb 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 laravie/eloquent-dynamodb 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 laravie/eloquent-dynamodb 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
AWS SDK for PHP - Use Amazon Web Services in your PHP project
AWS package containing services for the DreamFactory Platform 2.0.
This package includes a script and fail2ban configuration that allows you to use fail2ban when utilizing AWS elastic load balancer (ELB) and an apache webserver.
Elastic Driver for Laravel Scout
simple api library.
DynamoDB driver for Laravel Auditing package
统计信息
- 总下载量: 8.24k
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 5
- 点击次数: 19
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2019-11-12