PHP Slim 環境構築(7) DynamoDB

# PHP Slim 環境構築(7) DynamoDB

## Introduction

[前回](https://qiita.com/dynamitecoolguy/items/2a7df34c641327166f15)は、Redisにアクセスしてみました。
今回は、ローカル用のDynamoDBを作成して、DynamoDBへのアクセスを行います。

## 変更点

### ソースツリー

前回からの変更・追加ソースは以下の通りです。

```
$(PROJECTROOT)
  /compose
    docker-compose.yml
  /src
    /hoge
      /lib
        /Controller
          DynamodbController.php            (NEW!)
        /Model
      /public
        index.php
    composer.json
    composer.lock
```

### docker-compose.yml

新たにdynamodb用コンテナを追加します。
また、web_hogeコンテナから、dynamodbコンテナにアクセスするために、linksを設定します。
これで、ホスト名"dynamodb"でdynamodbを識別することができます。

なお、dynamodbでlocalhost:18000でアクセスできるようにしているのは、ローカルホストのコマンドラインからawsコマンドで動作確認するためなので、実際の処理には必要ありません。

```yml
  web_hoge:
    build:
      context: ./web_hoge
      args:
        - environment=local
    links:
      - mysql
      - postgresql
      - redis
      - dynamodb                        (追加)

  ..(略)..  

  dynamodb:
    image: amazon/dynamodb-local
    container_name: dynamodb
    ports:
      - "18000:8000"
    networks:
      - localnet 
```

### composer.json/composer.lock

aws-sdkをcomposerで追加しておきます。

```sh
$ composer require aws/aws-sdk-php
```

### index.php



```php:/src/hoge/public/index.php
use Hoge\Controller\RedisController;
use Hoge\Controller\DynamodbController;         (追加)

..(略)..  

$containerBuilder->addDefinitions([
    'settings' => [

        ..(略)..  

        'dynamodb' => [
            'endpoint' => 'http://dynamodb:8000',
            'region' => 'ap-northeast-1'
        ]
    ],

    ..(略)..  

    'dynamodb' => function (ContainerInterface $container) {
        $settings = $container->get('settings')['dynamodb'];

        $sdk = new Aws\Sdk([
            'endpoint' => $settings['endpoint'],
            'region' => $settings['region'],
            'version' => '2012-08-10',
            'credentials' => [
                'key' => 'dummy-key',
                'secret' => 'dummy-secret'
            ]
        ]);
        
        $dynamodb = $sdk->createDynamoDb();
        return $dynamodb;
    }

..(略)..  

$app->group('/dynamodb', function (RouteCollectorProxy $group) {
    $group->get('/{key}', DynamodbController::class . ':get');
    $group->post('', DynamodbController::class . ':post');
});
```

### DynamodbController

getとpostに対応したメソッドを定義しています。
また、コンストラクタでダミー用のテーブルを作成しています。

```php:/src/hoge/lib/Controller/DynamodbController.php
<?php

namespace Hoge\Controller;

use Aws\DynamoDb\DynamoDbClient;
use Aws\DynamoDb\Exception\DynamoDbException;
use Aws\DynamoDb\Marshaler;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Container\ContainerInterface;

class DynamodbController
{
    /**
     * @var ContainerInterface
     */
    protected $container;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
        $dynamodb = $this->container->get('dynamodb'); /** @var DynamoDbClient $dynamodb */

        // dummy table
        try {
            $result = $dynamodb->describeTable([
                'TableName' => 'hogehoge'
            ]);
        } catch (DynamoDbException $ex) {
            $result = $dynamodb->createTable([
                'TableName' => 'hogehoge',
                'AttributeDefinitions' => [
                    [
                        'AttributeName' => 'key',
                        'AttributeType' => 'S'
                    ]
                ],
                'KeySchema' => [
                    [
                        'AttributeName' => 'key',
                        'KeyType' => 'HASH'
                    ]
                ],
                'ProvisionedThroughput' => [
                    'ReadCapacityUnits' => 1,
                    'WriteCapacityUnits' => 1
                ]
            ]);
        }
    }

    public function get(Request $request, Response $response, array $args) : Response
    {
        // parameters (id)
        $key = $args['key'];

        $dynamodb = $this->container->get('dynamodb'); /** @var DynamoDbClient $dynamodb */
        $marshaller = new Marshaler();
        $record = $marshaller->marshalItem(["key"=> $key]);

        try {
            $result = $dynamodb->getItem([
                'TableName' => 'hogehoge',
                'Key' => $record
            ]);
        } catch (DynamoDbException $ex) {
            return $response->withStatus(404);
        }
        if (!$result->hasKey('Item')) {
            return $response->withStatus(404);
        }
        $item = $result->get('Item');
        $found = $marshaller->unmarshalItem($item);
        $response->getBody()->write(json_encode($found));
        return $response;
    }

    public function post(Request $request, Response $response, array $args) : Response
    {
        $body = $request->getBody()->getContents();
        $input = json_decode($body);
        $key = $input->key;
        $value = $input->value;

        $dynamodb = $this->container->get('dynamodb'); /** @var DynamoDbClient $dynamodb */

        $marshaller = new Marshaler();
        $record = $marshaller->marshalItem(['key'=>$key, 'value'=>$value]);

        $dynamodb->putItem(['TableName'=>'hogehoge', 'Item'=>$record]);
        $response->getBody()->write('OK');
        return $response;
    }
}
```

## ここまでのソース

[こちら](https://github.com/dynamitecoolguy/slimtemplate/tree/chapter7)でどうぞ。


## 変更履歴

### 2019/10/09

* DynamodbControllerでDynamoDBのcreateTableとattributeDefinitionsとKeySchemaの数が異なっていたので修正
* リポジトリにaws-sdk-php関連のソースが抜けていたので追加。