PHPackages                             kitar/laravel-dynamodb - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. kitar/laravel-dynamodb

ActiveLibrary[Database &amp; ORM](/categories/database)

kitar/laravel-dynamodb
======================

A DynamoDB based Eloquent model and Query builder for Laravel.

v1.4.0(11mo ago)193675.3k—9.9%31[3 issues](https://github.com/kitar/laravel-dynamodb/issues)[1 PRs](https://github.com/kitar/laravel-dynamodb/pulls)1MITPHPPHP ^7.3|^7.4|^8.0|^8.1|^8.2|^8.3|^8.4CI passing

Since Mar 23Pushed 2mo ago6 watchersCompare

[ Source](https://github.com/kitar/laravel-dynamodb)[ Packagist](https://packagist.org/packages/kitar/laravel-dynamodb)[ Docs](https://github.com/kitar/laravel-dynamodb)[ RSS](/packages/kitar-laravel-dynamodb/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (10)Versions (19)Used By (1)

Laravel DynamoDB
================

[](#laravel-dynamodb)

[![test](https://github.com/kitar/laravel-dynamodb/workflows/test/badge.svg)](https://github.com/kitar/laravel-dynamodb/actions)[![codecov](https://camo.githubusercontent.com/a101c5061f7154b3a8590312330e3ee804fa46c85929e3e680fe670b71fd270b/68747470733a2f2f636f6465636f762e696f2f67682f6b697461722f6c61726176656c2d64796e616d6f64622f6272616e63682f6d61737465722f67726170682f62616467652e737667)](https://codecov.io/gh/kitar/laravel-dynamodb/branch/master)

A DynamoDB based Eloquent model and Query builder for Laravel.

You can find an example implementation in [kitar/simplechat](https://github.com/kitar/simplechat).

- [Motivation](#motivation)
- [Installation](#installation)
    - [Laravel](#laravel)
    - [Non-Laravel projects](#non-laravel-projects)
- [Sample data](#sample-data)
- [Model](#model)
    - [Extending the base model](#extending-the-base-model)
    - [Basic Usage](#basic-usage)
        - [Retrieving all models](#retrieving-all-models)
        - [Retrieving a model](#retrieving-a-model)
        - [create()](#create)
        - [save()](#save)
        - [update()](#update)
        - [delete()](#delete)
        - [increment() / decrement()](#increment--decrement)
    - [Advanced Queries](#advanced-queries)
- [Authentication with model](#authentication-with-model)
    - [Register custom user provider](#register-custom-user-provider)
    - [Change auth config](#change-auth-config)
- [Query Builder](#query-builder)
    - [Basic Usage](#basic-usage-1)
        - [getItem()](#getitem)
        - [putItem()](#putitem)
        - [updateItem()](#updateitem)
        - [deleteItem()](#deleteitem)
    - [Projection Expressions](#projection-expressions)
        - [select()](#select)
    - [Condition Expressions](#condition-expressions)
        - [condition()](#condition)
        - [conditionIn()](#conditionin)
        - [conditionBetween()](#conditionbetween)
    - [Working with Queries](#working-with-queries)
        - [query() and keyCondition()](#query-and-keycondition)
        - [keyConditionBetween()](#keyconditionbetween)
        - [Sort order](#sort-order)
    - [Working with Scans](#working-with-scans)
        - [scan()](#scan)
    - [Filtering the Results](#filtering-the-results)
        - [filter()](#filter)
        - [filterIn()](#filterin)
        - [filterBetween()](#filterbetween)
    - [Paginating the Results](#paginating-the-results)
        - [exclusiveStartKey()](#exclusivestartkey)
    - [Using Global Secondary Indexes](#using-global-secondary-indexes)
        - [index()](#index)
    - [Atomic Counter](#atomic-counter)
    - [Batch Operations](#batch-operations)
        - [batchGetItem()](#batchgetitem)
        - [batchPutItem()](#batchputitem)
        - [batchDeleteItem()](#batchdeleteitem)
        - [batchWriteItem()](#batchwriteitem)
    - [DynamoDB-specific operators for condition() and filter()](#dynamodb-specific-operators-for-condition-and-filter)
        - [Comparators](#comparators)
        - [functions](#functions)
- [Debugging](#debugging)
- [Testing](#testing)

Motivation
----------

[](#motivation)

- I want to use DynamoDB with Laravel. (e.g., authenticate with custom user provider)
- I want to use a simple API which doesn't need to worry about cumbersome things like manually handling Expression Attributes.
- I want to extend Laravel's code as much as I can to:
    - Rely on Laravel's robust codes.
    - keep the additional implementation simple and maintainable.
- I don't want to make it fully compatible with Eloquent because DynamoDB is different from relational databases.
- I'm longing for [jessengers/laravel-mongodb](https://github.com/jenssegers/laravel-mongodb). What if we have that for DynamoDB?

Installation
------------

[](#installation)

Install the package via Composer:

```
$ composer require kitar/laravel-dynamodb

```

### Laravel (6.x, 7.x, 8.x, 9.x, 10.x, 11.x, 12.x)

[](#laravel-6x-7x-8x-9x-10x-11x-12x)

Add dynamodb configs to `config/database.php`:

```
'connections' => [

    'dynamodb' => [
        'driver' => 'dynamodb',
        'key' => env('AWS_ACCESS_KEY_ID'),
        'secret' => env('AWS_SECRET_ACCESS_KEY'),
        'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
        'token' => env('AWS_SESSION_TOKEN', null),
        'endpoint' => env('DYNAMODB_ENDPOINT', null),
        'prefix' => '', // table prefix
    ],

    ...

],
```

Update the `DB_CONNECTION` variable in your `.env` file:

```
DB_CONNECTION=dynamodb

```

> **Note for Laravel 11+**: Laravel 11 and later versions default to `database` driver for session, cache, and queue, which are not compatible with this DynamoDB package. You'll need to configure these services to use alternative drivers. For instance:
>
> ```
> SESSION_DRIVER=file
> CACHE_STORE=file
> QUEUE_CONNECTION=sync
>
> ```

### Non-Laravel projects

[](#non-laravel-projects)

For usage outside Laravel, you can create the connection manually and start querying with [Query Builder](#query-builder).

```
$connection = new Kitar\Dynamodb\Connection([
    'key' => env('AWS_ACCESS_KEY_ID'),
    'secret' => env('AWS_SECRET_ACCESS_KEY'),
    'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
    'token' => env('AWS_SESSION_TOKEN', null),
    'endpoint' => env('DYNAMODB_ENDPOINT', null),
    'prefix' => '', // table prefix
]);

$connection->table('your-table')->...
```

Sample data
-----------

[](#sample-data)

Many of the example codes in this document are querying to [DynamoDB's official sample data](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/AppendixSampleTables.html). If you want to try these codes with actual DynamoDB tables, it's handy to load them to your tables before.

Model
-----

[](#model)

DynamoDB model extends Eloquent model so that we can use familiar features such as mutators, serialization, etc.

The main difference between Eloquent model and DynamoDB model is:

- Eloquent model
    - Can handle relations.
    - Forward calls to model (Eloquent) query builder. (e.g., `create`, `createOrFirst` `where` `with`)
- DynamoDB model
    - Cannot handle relations.
    - Forward calls to database (DynamoDB) query builder. (e.g., `getItem`, `putItem`, `scan`, `filter`)

### Extending the base model

[](#extending-the-base-model)

Most of the attributes are the same as the original Eloquent model, but there are few DynamoDB-specific attributes.

NameRequiredDescriptiontableyesName of the Table.primaryKeyyesName of the Partition Key.sortKeyName of the Sort Key.sortKeyDefaultDefault value for the Sort Key.For example, if our table has only partition key, the model will look like this:

```
use Kitar\Dynamodb\Model\Model;

class ProductCatalog extends Model
{
    protected $table = 'ProductCatalog';
    protected $primaryKey = 'Id';
    protected $fillable = ['Id', 'Price', 'Title'];
}
```

If our table also has sort key:

```
use Kitar\Dynamodb\Model\Model;

class Thread extends Model
{
    protected $table = 'Thread';
    protected $primaryKey = 'ForumName';
    protected $sortKey = 'Subject';
    protected $fillable = ['ForumName', 'Subject'];
}
```

If we set `sortKeyDefault`, it will be used when we instantiate or call `find` without sort key.

```
use Kitar\Dynamodb\Model\Model;
use Illuminate\Auth\Authenticatable;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;

class User extends Model implements AuthenticatableContract
{
    use Authenticatable;

    protected $table = 'User';
    protected $primaryKey = 'email';
    protected $sortKey = 'type';
    protected $sortKeyDefault = 'profile';
    protected $fillable = [
        'name', 'email', 'password', 'type',
    ];
}
```

> Note that this model is implementing `Illuminate\Contracts\Auth\Authenticatable` and using `Illuminate\Auth\Authenticatable`. This is **optional**, but if we use them, we can use this model with authentication as well. For authentication, please refer to [Authentication section](#authentication-with-model)) for more details.

### Basic Usage

[](#basic-usage)

#### Retrieving all models

[](#retrieving-all-models)

```
$products = ProductCatalog::scan();
```

or alternatively,

```
$products = ProductCatalog::all();
```

You also can override the `scan()` method to fit your needs, such as filtering models for single table design. For example:

```
public static function scan($exclusiveStartKey = null, $sort = 'asc', $limit = 50)
{
    return static::index('GSI1')
        ->keyCondition('GSI1PK', '=', 'PRODUCT#')
        ->keyCondition('GSI1SK', 'begins_with', 'PRODUCT#')
        ->exclusiveStartKey($exclusiveStartKey)
        ->scanIndexForward($sort == 'desc' ? false : true)
        ->limit($limit)
        ->query();
}
```

> DynamoDB can only handle result set up to 1MB per call, so we have to paginate if there are more results. see [Paginating the Results](#paginating-the-results) for more details.

#### Retrieving a model

[](#retrieving-a-model)

If the model has only partition key:

```
ProductCatalog::find(101);
```

If the model also has sort key:

```
Thread::find([
    'ForumName' => 'Amazon DynamoDB', // Partition key
    'Subject' => 'DynamoDB Thread 1' // Sort key
]);
```

If the model has sort key and `sortKeyDefault` is defined:

```
User::find('foo@bar.com'); // Partition key. sortKeyDefault will be used for Sort key.
```

You also can modify the behavior of the `find()` method to fit your needs. For example:

```
public static function find($userId)
{
    return parent::find([
        'PK' => str_starts_with($userId, 'USER#') ? $userId : 'USER#'.$userId,
        'SK' => 'USER#',
    ]);
}
```

#### create()

[](#create)

```
$user = User::create([
    'email' => 'foo@bar.com',
    'type' => 'profile' // Sort key. If we don't specify this, sortKeyDefault will be used.
]);
```

#### save()

[](#save)

```
$user = new User([
    'email' => 'foo@bar.com',
    'type' => 'profile'
]);

$user->save();
```

```
$user->name = 'foo';
$user->save();
```

#### update()

[](#update)

```
$user->update([
    'name' => 'foobar'
]);
```

#### delete()

[](#delete)

```
$user->delete();
```

#### increment() / decrement()

[](#increment--decrement)

When we call `increment()` and `decrement()`, the [Atomic Counter](#atomic-counter) will be used under the hood.

```
$user->increment('views', 1);
$user->decrement('views', 1);
```

We can also pass additional attributes to update.

```
$user->increment('views', 1, [
    'last_viewed_at' => '...',
]);
```

### Advanced Queries

[](#advanced-queries)

We can use Query Builder functions through model such as `query` `scan` `filter` `condition` `keyCondition` etc.

For example:

```
Thread::keyCondition('ForumName', '=', 'Amazon DynamoDB')
    ->keyCondition('Subject', 'begins_with', 'DynamoDB')
    ->filter('Views', '=', 0)
    ->query();
```

Please refer to [Query Builder](#query-builder) for the details.

Authentication with model
-------------------------

[](#authentication-with-model)

We can create a Custom User Provider to authenticate with DynamoDB. For the detail, please refer to [Laravel's official document](https://laravel.com/docs/authentication#adding-custom-user-providers).

To use authentication with the model, the model should implement `Illuminate\Contracts\Auth\Authenticatable` contract. In this section, we'll use the example `User` model above.

### Register custom user provider

[](#register-custom-user-provider)

After we prepare authenticatable model, we need to make the custom user provider. We can make it own (it's simple), but we'll use `Kitar\Dynamodb\Model\AuthUserProvider` in this section.

To register custom user provider, add codes below in `App/Providers/AuthServiceProvider.php`.

```
use Kitar\Dynamodb\Model\AuthUserProvider;
...
public function boot()
{
    $this->registerPolicies();

    Auth::provider('dynamodb', function ($app, array $config) {
        return new AuthUserProvider(
            $app['hash'],
            $config['model'],
            $config['api_token_name'] ?? null,
            $config['api_token_index'] ?? null
        );
    });
}
```

### Change auth config

[](#change-auth-config)

Then specify driver and model name for authentication in `config/auth.php`.

```
'providers' => [
    // Eloquent
    // 'users' => [
    //     'driver' => 'eloquent',
    //     'model' => App\User::class,
    // ],

    // DynamoDB
    'users' => [
        'driver' => 'dynamodb',
        'model' => App\User::class,
        'api_token_name' => 'api_token',
        'api_token_index' => 'api_token-index'
    ],
],
```

`api_token_name` and `api_token_index` are optional, but we need them if we use api token authentication.

### Registration Controller

[](#registration-controller)

You might need to modify the registration controller. For example, if we use Laravel Starter Kits, the modification looks like below.

```
class RegisteredUserController extends Controller
{
    ...

    public function store(Request $request)
    {
        $request->validate([
            'name' => 'required|string|max:255',
            'email' => ['required', 'string', 'lowercase', 'email', 'max:255', function ($attribute, $value, $fail) {
                if (User::find($value)) {
                    $fail('The '.$attribute.' has already been taken.');
                }
            }],
            'password' => ['required', 'confirmed', Rules\Password::defaults()],
        ]);

        $user = User::create([
            'name' => $request->name,
            'email' => $request->email,
            'password' => Hash::make($request->password),
        ]);

        event(new Registered($user));

        Auth::login($user);

        return to_route('dashboard');
    }
}
```

The change is in the email validation rules. Instead of using the `unique` rule, we pass a closure to perform the duplicate check directly.

Query Builder
-------------

[](#query-builder)

We can use Query Builder without model.

```
$result = DB::table('Thread')->scan();
```

Or even outside Laravel.

```
$connection = new Kitar\Dynamodb\Connection([
    'key' => env('AWS_ACCESS_KEY_ID'),
    'secret' => env('AWS_SECRET_ACCESS_KEY'),
    'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
    'token' => env('AWS_SESSION_TOKEN', null),
    'endpoint' => env('DYNAMODB_ENDPOINT', null),
    'prefix' => '', // table prefix
]);

$result = $connection->table('Thread')->scan();
```

If we query through the model, we don't need to specify the table name, and the response will be the model instance(s).

```
$threads = Thread::scan();
```

### Basic Usage

[](#basic-usage-1)

#### getItem()

[](#getitem)

```
$response = DB::table('ProductCatalog')
    ->getItem(['Id' => 101]);
```

> Instead of marshaling manually, pass a plain array. `Kitar\Dynamodb\Query\Grammar` will automatically marshal them before querying.

#### putItem()

[](#putitem)

```
DB::table('Thread')
    ->putItem([
        'ForumName' => 'Amazon DynamoDB',
        'Subject' => 'New discussion thread',
        'Message' => 'First post in this thread',
        'LastPostedBy' => 'fred@example.com',
        'LastPostedDateTime' => '201603190422'
    ]);
```

#### updateItem()

[](#updateitem)

```
DB::table('Thread')
    ->key([
        'ForumName' => 'Laravel',
        'Subject' => 'Laravel Thread 1'
    ])->updateItem([
        'LastPostedBy' => null, // REMOVE
        'Replies' => null, // REMOVE
        'Message' => 'Updated' // SET
    ]);
```

Currently, we only support simple `SET` and `REMOVE` actions. If the attribute has value, it will be passed to `SET` action. If the value is null, it will be passed to `REMOVE` action.

#### deleteItem()

[](#deleteitem)

```
DB::table('Thread')
    ->deleteItem([
        'ForumName' => 'Amazon DynamoDB',
        'Subject' => 'New discussion thread'
    ]);
```

### Projection Expressions

[](#projection-expressions)

A Projection Expression is a string that identifies the attributes that web want. (It's like `select` statement for SQL)

#### select()

[](#select)

We can specify Projection Expressions in the same manner as the original `select` clause.

```
$response = DB::table('ProductCatalog')
    ->select('Price', 'Title')
    ->getItem(['Id' => 101]);
```

### Condition Expressions

[](#condition-expressions)

When we manipulate data in Amazon DynamoDB table, we use `putItem`, `updateItem` and `DeleteItem`. We can use Condition Expressions to determine which items should be modified.

#### condition()

[](#condition)

To specify Condition Expression, we use `condition` clause. This works basically same as the original `where` clause, but it's for Condition Expressions.

```
DB::table('ProductCatalog')
    ->condition('Id', 'attribute_not_exists')
    ->putItem([
        'Id' => 101,
        'ProductCategory' => 'Can I overwrite?'
    ]);
```

> Note that we specify `attribute_not_exists` for the operator of condition. This is DynamoDB-specific operator which called `function`. See [DynamoDB-specific operators for condition() and filter()](#dynamodb-specific-operators-for-condition-and-filter) for more details.

OR statements

```
DB::table('ProductCatalog')
    ->condition('Id', 'attribute_not_exists')
    ->orCondition('Price', 'attribute_not_exists)
    ->putItem([...]);
```

AND statements

```
DB::table('ProductCatalog')
    ->condition('Id', 'attribute_not_exists')
    ->condition('Price', 'attribute_not_exists)
    ->putItem([...]);
```

#### conditionIn()

[](#conditionin)

```
ProductCatalog::key(['Id' => 101])
    ->conditionIn('ProductCategory', ['Book', 'Bicycle'])
    ->updateItem([
        'Description' => 'updated!'
    ]);
```

#### conditionBetween()

[](#conditionbetween)

```
ProductCatalog::key(['Id' => 101])
    ->conditionBetween('Price', [0, 10])
    ->updateItem([
        'Description' => 'updated!'
    ]);
```

### Working with Queries

[](#working-with-queries)

The Query operation in Amazon DynamoDB finds items based on primary key values.

#### query() and keyCondition()

[](#query-and-keycondition)

When we `query`, we must specify `keyCondition` as well.

We can use some comparison operators for sort key, but we must use the equality condition for the partition key.

```
$response = DB::table('Thread')
    ->keyCondition('ForumName', '=', 'Amazon DynamoDB')
    ->keyCondition('Subject', 'begins_with', 'DynamoDB')
    ->query();
```

#### keyConditionBetween()

[](#keyconditionbetween)

```
$response = DB::table('Thread')
    ->keyCondition('ForumName', '=', 'Amazon DynamoDB')
    ->keyConditionBetween('Subject', ['DynamoDB Thread 1', 'DynamoDB Thread 2'])
    ->query();
```

#### Sort order

[](#sort-order)

`query` results are always sorted by the sort key value. To reverse the order, set the `ScanIndexForward` parameter to `false`.

```
$response = DB::table('Thread')
    ->keyCondition('ForumName', '=', 'Amazon DynamoDB')
    ->scanIndexForward(false)
    ->query();
```

> Note that DynamoDB's `ScanIndexForward` is a feature for `query`. It will not work with `scan`.

### Working with Scans

[](#working-with-scans)

#### scan()

[](#scan)

```
$response = DB::table('Thread')->scan();
```

### Filtering the Results

[](#filtering-the-results)

When we `query` or `scan`, we can filter results with Filter Expressions before it returned.

It can't reduce the amount of read capacity, but it can reduce the size of traffic data.

#### filter()

[](#filter)

```
$response = DB::table('Thread')
    ->filter('LastPostedBy', '=', 'User A')
    ->scan();
```

OR statement

```
$response = DB::table('Thread')
    ->filter('LastPostedBy', '=', 'User A')
    ->orFilter('LastPostedBy', '=', 'User B')
    ->scan();
```

AND statement

```
$response = DB::table('Thread')
    ->filter('LastPostedBy', '=', 'User A')
    ->filter('Subject', 'begins_with', 'DynamoDB')
    ->scan();
```

#### filterIn()

[](#filterin)

```
$response = DB::table('Thread')
    ->filterIn('LastPostedBy', ['User A', 'User B'])
    ->scan();
```

#### filterBetween()

[](#filterbetween)

```
$response = DB::table('ProductCatalog')
    ->filterBetween('Price', [0, 100])
    ->scan();
```

### Paginating the Results

[](#paginating-the-results)

A single `query` or `scan` only returns a result set that fits within the 1 MB size limit. If there are more results, we need to paginate.

#### exclusiveStartKey()

[](#exclusivestartkey)

If there are more results, the response contains `LastEvaluatedKey`.

```
$response = DB::table('ProductCatalog')
    ->limit(5)
    ->scan();

$response['LastEvaluatedKey']; // array
```

We can pass this key to `exclusiveStartKey` to get next results.

```
$response = DB::table('ProductCatalog')
    ->exclusiveStartKey($response['LastEvaluatedKey'])
    ->limit(5)
    ->scan();
```

If you are using Query Builder through model, you can access to `exclusiveStartKey` by:

```
$products = ProductCatalog::limit(5)->scan();

$products->getLastEvaluatedKey(); // array
```

Alternatively, you can achieve the same result using individual models; however, please be aware that this approach is planned to be deprecated in versions subsequent to v2.x.

```
$products->first()->meta()['LastEvaluatedKey']; // array
```

### Using Global Secondary Indexes

[](#using-global-secondary-indexes)

Some applications might need to perform many kinds of queries, using a variety of different attributes as query criteria. To support these requirements, you can create one or more global secondary indexes and issue `query` requests against these indexes in Amazon DynamoDB.

#### index()

[](#index)

Use `index` clause to specify Global Secondary Index name.

```
$response = DB::table('Reply')
    ->index('PostedBy-Message-index')
    ->keyCondition('PostedBy', '=', 'User A')
    ->keyCondition('Message', '=', 'DynamoDB Thread 2 Reply 1 text')
    ->query();
```

### Atomic Counter

[](#atomic-counter)

DynamoDB [supports Atomic Counter](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/WorkingWithItems.html#WorkingWithItems.AtomicCounters). When we call `increment()` and `decrement()` [through Model](#increment--decrement) or Query Builder, Atomic Counter will be used under the hood.

```
DB::('Thread')->key([
    'ForumName' => 'Laravel',
    'Subject' => 'Laravel Thread 1'
])->increment('Replies', 2);
```

We can also pass additional attributes to update.

```
DB::('Thread')->key([
    'ForumName' => 'Laravel',
    'Subject' => 'Laravel Thread 1'
])->increment('Replies', 2, [
    'LastPostedBy' => 'User A',
]);
```

### Batch Operations

[](#batch-operations)

Batch operations can get, put or delete multiple items with a single call. There are some DynamoDB limitations (such as items count, payload size, etc), so please check the documentation in advance. ([BatchGetItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchGetItem.html), [BatchWriteItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchWriteItem.html))

#### batchGetItem()

[](#batchgetitem)

```
DB::table('Thread')
    ->batchGetItem([
        [
            'ForumName' => 'Amazon DynamoDB',
            'Subject' => 'DynamoDB Thread 1'
        ],
        [
            'ForumName' => 'Amazon DynamoDB',
            'Subject' => 'DynamoDB Thread 2'
        ]
    ]);
```

#### batchPutItem()

[](#batchputitem)

```
DB::table('Thread')
    ->batchPutItem([
        [
            'ForumName' => 'Amazon DynamoDB',
            'Subject' => 'DynamoDB Thread 3'
        ],
        [
            'ForumName' => 'Amazon DynamoDB',
            'Subject' => 'DynamoDB Thread 4'
        ]
    ]);
```

> This is a handy method to batch-put items using `batchWriteItem`

#### batchDeleteItem()

[](#batchdeleteitem)

```
DB::table('Thread')
    ->batchDeleteItem([
        [
            'ForumName' => 'Amazon DynamoDB',
            'Subject' => 'DynamoDB Thread 1'
        ],
        [
            'ForumName' => 'Amazon DynamoDB',
            'Subject' => 'DynamoDB Thread 2'
        ]
    ]);
```

> This is a handy method to batch-delete items using `batchWriteItem`

#### batchWriteItem()

[](#batchwriteitem)

```
DB::table('Thread')
    ->batchWriteItem([
        [
            'PutRequest' => [
                'Item' => [
                    'ForumName' => 'Amazon DynamoDB',
                    'Subject' => 'DynamoDB Thread 3'
                ]
            ]
        ],
        [
            'DeleteRequest' => [
                'Key' => [
                    'ForumName' => 'Amazon DynamoDB',
                    'Subject' => 'DynamoDB Thread 1'
                ]
            ]
        ]
    ]);
```

### DynamoDB-specific operators for condition() and filter()

[](#dynamodb-specific-operators-for-condition-and-filter)

For `condition` and `filter` clauses, we can use DynamoDB's comparators and functions.

#### Comparators

[](#comparators)

`=` `` `=` can be used in the form of:

```
filter($key, $comparator, $value);
```

#### functions

[](#functions)

Available functions are:

```
filter($key, 'attribute_exists');
filter($key, 'attribute_not_exists');
filter($key, 'attribute_type', $type);
filter($key, 'begins_with', $value);
filter($key, 'contains', $value);
```

> `size` function is not supported at this time.

Debugging
---------

[](#debugging)

#### dryRun()

[](#dryrun)

We can inspect what parameters (and which method) will actually send to DynamoDB by adding `dryRun()` to our query. For example:

```
// via Model
$request = ProductCatalog::dryRun()->getItem(['Id' => 101]);

// via Query Builder
$request = DB::table('ProductCatalog')->dryRun()->getItem(['Id' => 101]);

dump($request);
```

> Our PHPUnit tests also use this feature, without actually calling DynamoDB

Testing
-------

[](#testing)

```
$ ./vendor/bin/phpunit

```

###  Health Score

63

—

FairBetter than 99% of packages

Maintenance69

Regular maintenance activity

Popularity56

Moderate usage in the ecosystem

Community26

Small or concentrated contributor base

Maturity82

Battle-tested with a long release history

 Bus Factor1

Top contributor holds 82.7% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~118 days

Recently: every ~158 days

Total

17

Last Release

344d ago

Major Versions

v0.6.0 → v1.0.02022-03-24

PHP version history (6 changes)v0.1.0PHP ^7.2

v0.3.0PHP ^7.3|^8.0

v1.0.0PHP ^7.3|^8.0|^8.1

v1.0.2PHP ^7.3|^7.4|^8.0|^8.1|^8.2

v1.3.0PHP ^7.3|^7.4|^8.0|^8.1|^8.2|^8.3

v1.4.0PHP ^7.3|^7.4|^8.0|^8.1|^8.2|^8.3|^8.4

### Community

Maintainers

![](https://www.gravatar.com/avatar/edd7f4e5d97d0b1a4a1eb465feac01544e3a500c6b1c61e28bdfe3faa0613674?d=identicon)[kitar](/maintainers/kitar)

---

Top Contributors

[![kitar](https://avatars.githubusercontent.com/u/157844?v=4)](https://github.com/kitar "kitar (86 commits)")[![mehedimi](https://avatars.githubusercontent.com/u/10108409?v=4)](https://github.com/mehedimi "mehedimi (4 commits)")[![paulhenri-l](https://avatars.githubusercontent.com/u/25308170?v=4)](https://github.com/paulhenri-l "paulhenri-l (4 commits)")[![madrussa](https://avatars.githubusercontent.com/u/1470257?v=4)](https://github.com/madrussa "madrussa (3 commits)")[![hschimpf](https://avatars.githubusercontent.com/u/493357?v=4)](https://github.com/hschimpf "hschimpf (2 commits)")[![lucasgiovanny](https://avatars.githubusercontent.com/u/4853801?v=4)](https://github.com/lucasgiovanny "lucasgiovanny (1 commits)")[![andreagroferreira](https://avatars.githubusercontent.com/u/35269365?v=4)](https://github.com/andreagroferreira "andreagroferreira (1 commits)")[![marvinosswald](https://avatars.githubusercontent.com/u/1621844?v=4)](https://github.com/marvinosswald "marvinosswald (1 commits)")[![localpath](https://avatars.githubusercontent.com/u/44937070?v=4)](https://github.com/localpath "localpath (1 commits)")[![negoziator](https://avatars.githubusercontent.com/u/2228675?v=4)](https://github.com/negoziator "negoziator (1 commits)")

---

Tags

awsdynamodbeloquentlaravelphplaraveldynamodbdatabaseauthmodel

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/kitar-laravel-dynamodb/health.svg)

```
[![Health](https://phpackages.com/badges/kitar-laravel-dynamodb/health.svg)](https://phpackages.com/packages/kitar-laravel-dynamodb)
```

###  Alternatives

[mongodb/laravel-mongodb

A MongoDB based Eloquent model and Query builder for Laravel

7.1k7.2M71](/packages/mongodb-laravel-mongodb)[dyrynda/laravel-model-uuid

This package allows you to easily work with UUIDs in your Laravel models.

4802.8M8](/packages/dyrynda-laravel-model-uuid)[pdphilip/elasticsearch

An Elasticsearch implementation of Laravel's Eloquent ORM

145360.2k4](/packages/pdphilip-elasticsearch)[spiritix/lada-cache

A Redis based, automated and scalable database caching layer for Laravel

591444.8k2](/packages/spiritix-lada-cache)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
