This document provides a comprehensive guide on how to utilize the Terseq library to build and execute queries on AWS DynamoDB using the AWS SDK for PHP.
AWS SDK for PHP is a powerful tool for working with AWS services, but it can be challenging to use due to its complexity. It requires a lot of boilerplate code to build and execute queries. Terseq simplifies this process by providing a fluent interface to build queries for DynamoDB operations. It also supports single-table design, which is a recommended practice for DynamoDB.
To install the Terseq package, run the following command in your project directory using Composer:
composer require aiotu/terseq
$client = new \Aws\DynamoDb\DynamoDbClient([
'region' => 'us-west-2',
'version' => 'latest',
]);
$manager = new \Terseq\DatabaseManager($client, new Marshaler());
$manager->getItem()
->table(['Books', 'BookId'])
->pk('super-cool-id')
->dispatch();
$manager->putItem()
->table(['Books', 'BookId'])
->item([
'BookId' => 'super-cool-id',
'Title' => 'Super Cool Book',
'Author' => 'Super Cool Author',
])
->dispatch();
$manager->updateItem()
->table(['Books', 'BookId'])
->pk('super-cool-id')
->set('Title', 'Super Cool Book Updated')
->set('Author', 'Super Cool Author Updated')
->dispatch();
$manager->deleteItem()
->table(['Books', 'BookId'])
->pk('super-cool-id')
->dispatch();
$result = $manager->query()
->table(['Books', 'BookId'])
->pk('super-cool-id')
->consistentRead()
->disaptch();
use Terseq\Builders\Operations\TransactGetItems\Operations\Get;
$manager->transactGetItems()
->get(
[
static fn (Get $get) => $get->pk('super-cool-id1'),
static fn (Get $get) => $get->pk('super-cool-id2'),
],
table: ['Books', 'BookId'],
)
->dispatch();
use Terseq\Builders\Operations\TransactWriteItems\Operations\Delete;
use Terseq\Builders\Operations\TransactWriteItems\Operations\Put;
use Terseq\Builders\Operations\TransactWriteItems\Operations\Update;
$manager->transactWriteItems()
->put(
[
fn (Put $put) => $put->item([
'BookId' => 'super-book1',
'Author' => 'Unknown',
]),
fn (Put $put) => $put->item([
'BookId' => 'super-book-2',
'Author' => 'Incognito',
]),
],
table: ['Books', 'BookId'],
)
->update(
fn (Update $update) => $update
->pk('super-book-3')
->set('Author', 'Incognito'),
table: ['Books', 'BookId'],
)
->delete(
fn (Delete $delete) => $delete->pk('super-book-4'),
table: ['Books', 'BookId'],
)
->dispatch();
$manager->batchGetItem()
->get(
[
'BookId' => 'super-book-1',
'Author' => 'Unknown',
],
table: ['Books', 'BookId'],
)
->get(
[
'BookId' => 'super-book-2',
'Author' => 'Incognito',
],
table: ['Books', 'BookId'],
)
->dispatch();
$manager->batchWriteItem()
->put(
[
'BookId' => 'super-book-1',
'Author' => 'Unknown',
],
table: ['Books', 'BookId'],
)
->put(
[
'BookId' => 'super-book-2',
'Author' => 'Incognito',
],
table: ['Books', 'BookId'],
)
->delete(
[
'BookId' => 'super-book-3',
],
table: ['Books', 'BookId'],
)
->dispatch();
use Terseq\Builders\Table;
class Books extends Table
{
public function getTableName(): string
{
return 'Books';
}
public function getKeys(): Keys
{
return new Keys(partitionKey: 'BookId', sortKey: null);
}
}
Example with secondary indexes
use Terseq\Builders\Table;
class BooksTable extends Table
{
/**
* Table name
*/
public function getTableName(): string
{
return 'Books';
}
/**
* Partition key and sort key (optional)
*/
public function getKeys(): Keys
{
return new Keys(partitionKey: 'BookId', sortKey: 'ReleaseDate');
}
/**
* Secondary index map (optional) also known as GSI and LSI
*/
public function getSecondaryIndexMap(): ?array
{
return [
'AuthorIndex' => new Keys(partitionKey: 'AuthorId', sortKey: 'AuthorBornYear'),
'GenreIndex' => new Keys(partitionKey: 'GenreId', sortKey: 'GenreName'),
'LsiExample' => new Keys(partitionKey: 'BookId', sortKey: 'AuthorBornYear'),
];
}
}
table(table: ['TableName', 'PartitionKey', 'SortKey']);
OR
table(table: ['TableName', 'PartitionKey']); // Sort key by default is null
OR
table(table: ['TableName']); // throws exception, because PartitionKey is required
OR
table(table: [
'table' => 'TableName',
'pk' => 'PartitionKey',
'sk' => 'SortKey',
]);
Library supports single-table design.
$manager = new \Terseq\DatabaseManager(
client: $client,
marshaler: new Marshaler(),
singleTable: new class extends \Terseq\Builders\Table {
public function getTableName(): string
{
return 'Books';
}
public function getKeys(): Keys
{
return new Keys(partitionKey: 'BookId');
}
},
);
That's all! Now you can build queries without passing table name and keys.
// Query
$manager->getItem()->pk('super-cool-id')->disaptch();
$manager->batchWriteItem()
->put(
[
'BookId' => 'super-book-1',
'Author' => 'Unknown',
],
)
->put(
[
'BookId' => 'super-book-2',
'Author' => 'Incognito',
],
)
->delete(
[
'BookId' => 'super-book-3',
],
)
->dispatch();
$client->updateItem(
[
'TableName' => 'Books',
'UpdateExpression' => 'SET #Title = :title_0, #Author = :author_0',
'ExpressionAttributeNames' =>
[
'#Title' => 'Title',
'#Author' => 'Author',
],
'ExpressionAttributeValues' =>
[
':title_0' =>
[
'S' => 'Super Cool Book Updated',
],
':author_0' =>
[
'S' => 'Super Cool Author Updated',
],
],
'Key' =>
[
'BookId' =>
[
'S' => 'super-cool-id',
],
],
],
);
for the same operation
$manager->updateItem()
->table(['Books', 'BookId'])
->pk('super-cool-id')
->set('Title', 'Super Cool Book Updated')
->set('Author', 'Super Cool Author Updated')
->dispatch();