Skip to content

Commit bf51188

Browse files
gepAndrey Semikov
andauthored
Add usage documentation with examples (#4)
* Add Usage documentation with examples * Add query builder docs --------- Co-authored-by: Andrey Semikov <[email protected]>
1 parent ef979c9 commit bf51188

File tree

5 files changed

+586
-1
lines changed

5 files changed

+586
-1
lines changed

README.md

Lines changed: 334 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,340 @@ This is a library and an Object Document Mapper to use with AWS DynamoDB in a mo
66

77
## Usage
88

9-
To be added soon...
9+
### Configure the ODM
10+
11+
Set up native client:
12+
13+
```php
14+
$dynamoDbClient = new DynamoDbClient(array_merge(
15+
[
16+
'region' => 'eu-west-2',
17+
'version' => 'latest',
18+
]
19+
));
20+
```
21+
22+
Set up the main operations lib client:
23+
24+
```php
25+
$client = new DynamodbOperationsClient($dynamoDbClient);
26+
```
27+
28+
Set up the marshaller. Native AWS marshaller may be taken:
29+
```php
30+
$marshaler = new Marshaler();
31+
```
32+
Set up the Query builder:
33+
34+
```php
35+
$queryBuilder = new QueryBuilder($marshaler, new ExpressionFactory($marshaler));
36+
```
37+
38+
Set up annotation reader and annotation manager:
39+
40+
```php
41+
// annotation reader
42+
$annotationReader = new AnnotationReader();
43+
44+
// annotation manager
45+
$annotationManager = new AnnotationManager($annotationReader);
46+
```
47+
48+
The hydrators for the models:
49+
50+
```php
51+
$newModelHydrator = new Hydrator(NewModel::class, $annotationManager);
52+
$sortKeyModelHydrator = new Hydrator(SortKeyModel::class, $annotationManager);
53+
```
54+
Serializer for inserting records into DB:
55+
56+
```php
57+
// serializer for
58+
$serializer = new Serializer($annotationManager);
59+
```
60+
61+
The full example is in [here](examples/initialise_client.php).
62+
63+
### Model
64+
65+
#### Model field types
66+
67+
The lib operates with models. Each model may have various supported field types. Here is a list of types which correlate with PHP and Dynamodb types:
68+
69+
- `BooleanType`: boolean for DynamoDb and for php
70+
- `CollectionType`: list for DynamoDb, in php it's an array list of items of specific model
71+
- `DateType`: string for DynamoDb, DateTime for php
72+
- `EnumType`: string for DynamoDb, enum for php
73+
- `FloatType`: number for DynamoDb, float for php
74+
- `HashMapType`: map for DynamoDb, in php it's associative array of items of specific model
75+
- `IntegerType`: number for DynamoDb, int for php
76+
- `ModelType`: map for DynamoDb, instance of model for php
77+
- `Money`: map for DynamoDb, special MoneyObject for PHP. [Money value](https://martinfowler.com/eaaCatalog/money.html) as a concept
78+
- `NumberType`: number for DynamoDb. An abstract type, not a handy one. My be used occasionally
79+
- `ScalarCollectionType`: map for DynamoDb. in php it's associative array of any dynamodb compatible types excluding `CollectionType`, `HashMapType` or `ModelType`
80+
- `StringType`: string for DynamoDb and for php
81+
82+
Here is a model example:
83+
84+
```php
85+
class ExampleDemoModel extends Model
86+
{
87+
protected const TABLE_NAME = 'test-table';
88+
89+
// Primary means that this is a partition key for the DynamoDb table
90+
#[StringType, Primary] protected string $id;
91+
#[StringType] protected string $name;
92+
#[FloatType] protected float $price;
93+
#[Money] protected Money $priceNet;
94+
#[FloatType] protected float $percent;
95+
#[IntegerType] protected int $itemsAmount;
96+
#[DateType] protected DateTime $createdAt;
97+
#[BooleanType] protected bool $isDeleted;
98+
#[BooleanType] protected bool $isPhoneNumber;
99+
#[ModelType([ModelType::MODEL_CLASS_NAME => RelatedModel::class])]
100+
protected RelatedModel $buyer;
101+
#[CollectionType([CollectionType::MODEL_CLASS_NAME => RelatedModel::class])]
102+
protected array $buyers;
103+
#[ModelType([Asset::MODEL_CLASS_NAME => Asset::class])]
104+
protected Asset $asset;
105+
#[HashMapType([HashMapType::MODEL_CLASS_NAME => RelatedModel::class])]
106+
protected array $buyersMap;
107+
108+
// getter and setters should be here
109+
}
110+
```
111+
Model full example is here: [model.php](examples/model.php)
112+
113+
#### Enumerations example
114+
115+
Enumerations are also supported. Here is an example of the model with enumeration fields:
116+
117+
```php
118+
class ModelWithEnumeration extends Model
119+
{
120+
#[Primary, StringType]
121+
protected string $id;
122+
123+
#[EnumType]
124+
protected OrderStatus $orderStatus;
125+
126+
// union types
127+
#[EnumType]
128+
protected OrderStatus|ApplicationStatus $unionStatus;
129+
130+
// union types with null
131+
#[EnumType]
132+
protected OrderStatus|ApplicationStatus|null $unionNullableStatus;
133+
134+
// isStrict means the value will be null in case wrong value comes from the DB
135+
#[EnumType(isStrict: false)]
136+
protected ?OrderStatus $orderStatusAdditional = null;
137+
138+
#[EnumType]
139+
protected CustomerType $customerType;
140+
}
141+
```
142+
143+
#### Fields encryption
144+
145+
Certain types custom encryption is supported. In case there are some fields which needs to be encrypted.
146+
147+
First of all we need to create a custom encryptor:
148+
149+
```php
150+
MyEncryptor implements EncryptorInterface {
151+
protected const ENCRYPTION_KEY = 'def000008053addc0f94b14c0e480a10631a0a970b3565e5a7a2aeaeeb51a39e2d139a8977bc02be0195f0036a29aefff9df6d2ddb81432d14b4dce82b83b3a95c6d0205';
152+
153+
public function decrypt(string|array $encryptedData, array $options = []): string|array
154+
{
155+
// any decryption way may be implemented
156+
if (is_array($encryptedData)) {
157+
// ...specific property decryption operations...
158+
return $encryptedData;
159+
}
160+
161+
return Crypto::decrypt(
162+
$encryptedData,
163+
Key::loadFromAsciiSafeString(static::ENCRYPTION_KEY)
164+
);
165+
}
166+
}
167+
```
168+
169+
Then the decryptor should be passed into the hydrator:
170+
171+
```php
172+
$newModelHydrator = new Hydrator(
173+
EncryptionDemoModel::class,
174+
$annotationManager,
175+
new MyEncryptor(),
176+
);
177+
```
178+
And the model may be the following:
179+
180+
```php
181+
class EncryptionDemoModel extends Model
182+
{
183+
#[Key\Primary, Types\StringType]
184+
protected string $id;
185+
186+
// ability to encrypt a specific property in a scalar associative array
187+
#[Types\ScalarCollectionType, Encrypted(["encryptedProperty" => "secretProperty"])]
188+
protected array $encryptedArray;
189+
190+
#[Types\StringType, Encrypted]
191+
protected string $encryptedName;
192+
}
193+
```
194+
195+
### Set up the repository for your model
196+
197+
The best way to operate with records is to create a repository. There is a built-in already:
198+
199+
```php
200+
$newModelDynamoDbRepository = new DynamoDBRepository(
201+
NewModel::class,
202+
$client,
203+
$queryBuilder,
204+
$newModelHydrator,
205+
$annotationManager,
206+
$marshaler,
207+
$serializer
208+
);
209+
```
210+
There are built-in operation in the default repository.
211+
212+
#### Get model by partition Id
213+
214+
##### Just by partition key
215+
```php
216+
$foundModel = $newModelDynamoDbRepository->get($id);
217+
```
218+
219+
##### By partition key and sort key
220+
```php
221+
$foundModel = $newModelDynamoDbRepository->get($id, $sortKey);
222+
```
223+
224+
##### Non-consistent read
225+
```php
226+
$foundModel = $newModelDynamoDbRepository->get($id, $sortKey, false);
227+
```
228+
229+
##### Get one item
230+
```php
231+
$foundModel = $newModelDynamoDbRepository->getOneById($id, $sortKey, false);
232+
```
233+
234+
#### Insert model
235+
```php
236+
$newModelDynamoDbRepository->save($model);
237+
```
238+
239+
#### Delete item
240+
```php
241+
$newModelDynamoDbRepository->delete($model);
242+
```
243+
244+
### Document repository
245+
246+
Sometimes we need to fetch not the whole model, but just a part of it. For this purpose there is such called `DocumentRepository`. The part of the document may be technically fetched using native DynamoDb projection expressions.
247+
248+
Setting `DocumentRepository` up:
249+
250+
```php
251+
$documentRepository = new DocumentRepository(
252+
NewModelNested::class,
253+
$client,
254+
$this->queryBuilder,
255+
$this->newModelHydrator,
256+
$annotationManager,
257+
$marshaler,
258+
$serializer
259+
);
260+
```
261+
262+
#### Get a model by projection expression
263+
264+
```php
265+
266+
$projectionExpression = "property.subPropertyModel";
267+
268+
$model = $documentRepository->getDocument()
269+
->setConsistentRead(true)
270+
->withAttrPath($projectionExpression)
271+
->withPrKey($keyValue)
272+
->execute()
273+
;
274+
```
275+
276+
#### Get a specific scalar property by projection expression
277+
278+
```php
279+
$projectionExpression = "property.subPropertyModel.name";
280+
281+
$name = $this->documentRepository->getDocumentProperty()
282+
->setConsistentRead(true)
283+
->withAttrPath($projection)
284+
->withPrKey($keyValue)
285+
->execute()
286+
;
287+
```
288+
289+
#### Get/create/update/delete operations
290+
291+
Document repository supports specific property get/create/update/delete operations:
292+
293+
- `createDocument()`
294+
- `updateDocument()`
295+
- `removeDocument()`
296+
- `getDocumentCollection()`
297+
- `updateDocumentCollection()`
298+
- `createDocumentCollection()`
299+
300+
### Query builder
301+
302+
Another powerful feature is query builders. This adds flexibility to fetch items by specific criteria which is supported by DynamoDB.
303+
304+
This is a way to work with the Dynamodb using raw queries and results
305+
306+
#### Get query builder
307+
308+
Fetch items:
309+
310+
```php
311+
$getItemQuery = $queryBuilder
312+
->getItem(self::DB_TABLE)
313+
->itemKey([$itemKey => $keyValue])
314+
->getQuery();
315+
316+
$item = $this->dynamoDbClient
317+
->getItem($getItemQuery)->get('Item');
318+
```
319+
#### Update query builder
320+
321+
Ability to update specific attributes.
322+
323+
```php
324+
$attributesForUpdate = [
325+
"numberProp" => 2,
326+
"stringProp" => "updated string value",
327+
"hashMapProp.map-id-1.type" => "updated map-type-1",
328+
"hashMapProp.map-id-1.mapProp" => "updated mapProp",
329+
"listProp" => [
330+
"updated listProp 1",
331+
"updated listProp 2"
332+
]
333+
];
334+
335+
$getItemQuery = $queryBuilder
336+
->updateItem(self::DB_TABLE)
337+
->itemKey([$itemKey => $keyValue])
338+
->attributes($attributesForUpdate)
339+
->getQuery();
340+
341+
$dynamoDbClient->updateItem($getItemQuery);
342+
```
10343

11344
## Local dev environment installation
12345

examples/initialise_client.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Autoprotect\DynamodbODM\Annotation\AnnotationManager;
6+
use Autoprotect\DynamodbODM\Client\DynamodbOperationsClient;
7+
use Autoprotect\DynamodbODM\Hydrator\Hydrator;
8+
use Autoprotect\DynamodbODM\Model\Serializer\Serializer;
9+
use Autoprotect\DynamodbODM\Query\Factory\ExpressionFactory;
10+
use Autoprotect\DynamodbODM\Query\QueryBuilder;
11+
use Aws\DynamoDb\DynamoDbClient;
12+
use Aws\DynamoDb\Marshaler;
13+
use Doctrine\Common\Annotations\AnnotationReader;
14+
15+
// Init native AWS dynamo Db client
16+
$dynamoDbClient = new DynamoDbClient(array_merge(
17+
[
18+
'region' => 'eu-west-2',
19+
'version' => 'latest',
20+
]
21+
));
22+
23+
// init lib operations client
24+
$client = new DynamodbOperationsClient($dynamoDbClient);
25+
26+
// native marshaller
27+
$marshaler = new Marshaler();
28+
29+
// query builder
30+
$queryBuilder = new QueryBuilder($marshaler, new ExpressionFactory($marshaler));
31+
32+
// annotation reader
33+
$annotationReader = new AnnotationReader();
34+
35+
// annotation manager
36+
$annotationManager = new AnnotationManager($annotationReader);
37+
38+
// various hydrators
39+
$newModelHydrator = new Hydrator(NewModel::class, $annotationManager);
40+
$sortKeyModelHydrator = new Hydrator(SortKeyModel::class, $annotationManager);
41+
42+
// serializer for
43+
$serializer = new Serializer($annotationManager);

0 commit comments

Comments
 (0)