/laravel-dynamodb

Eloquent syntax for DynamoDB

Primary LanguagePHPMIT LicenseMIT

laravel-dynamodb

Latest Stable Version Total Downloads Latest Unstable Version Build Status License

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.

Install

  • Composer install

    composer require baopham/dynamodb
  • Install service provider:

    // config/app.php
    
    'providers' => [
        ...
        BaoPham\DynamoDb\DynamoDbServiceProvider::class,
        ...
    ];
  • Put DynamoDb config in config/services.php:

    // config/services.php
    ...
    'dynamodb' => [
        'key' => env('DYNAMODB_KEY'),
        'secret' => env('DYNAMODB_SECRET'),
        'region' => env('DYNAMODB_REGION'),
        'local_endpoint' => env('DYNAMODB_LOCAL_ENDPOINT'), // see http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Tools.DynamoDBLocal.html
        'local' => env('DYNAMODB_LOCAL'), // true or false? should use dynamodb_local or not?
        'debug' => true, // if true, it will use Laravel Log. For advanced options, see http://docs.aws.amazon.com/aws-sdk-php/v3/guide/guide/configuration.html
    ],
    ...
    • If using from an assumed IAM role, you can also use the token parameter, your config in this case will look something like this:

      // config/services.php
      ...
      'dynamodb' => [
          ...
          'token' => env('AWS_SESSION_TOKEN'),
      ]
      ...

Usage

  • Extends your model with BaoPham\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 BaoPham\DynamoDb\ModelTrait, it will call a PutItem after the model is saved.

Supported features:

find() and delete()

$model->find(<id>);
$model->delete();

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

update()

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

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

chunk()

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

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

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

$model = new Model();
$model->where('id', 'foo')->removeAttribute('name', 'description');

// Or
Model::find('foo')->removeAttribute('name', 'description');

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

$this->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.

Composite Keys

To use composite keys with your model:

  • Set $compositeKey to 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(['id1' => 'value1', 'id2' => 'value2']);

Requirements

Laravel ^5.1

FAQ

Q: Cannot assign id property if its not in the fillable array
A: Try this?

Author and Contributors