2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

aws-sdk-php + PartiQL

Last updated at Posted at 2021-02-12

What's?

DynamoDB-local + PartiQLが可能になったので試しているところ。
コードはSlim4-SkeltonをDynamoDB用に書き換えたもの。

↓リポジトリはこちら
https://github.com/watarukura/php_vscode_project_template

テーブル定義

usersテーブル

$ aws --endpoint http://localhost:8000 dynamodb delete-table --table-name users
TableDescription:
  AttributeDefinitions:
  - AttributeName: id
    AttributeType: N
  BillingModeSummary:
    BillingMode: PAY_PER_REQUEST
    LastUpdateToPayPerRequestDateTime: '2021-02-17T13:02:33.967000+09:00'
  CreationDateTime: '2021-02-17T13:02:33.967000+09:00'
  ItemCount: 1
  KeySchema:
  - AttributeName: id
    KeyType: HASH
  ProvisionedThroughput:
    LastDecreaseDateTime: '1970-01-01T09:00:00+09:00'
    LastIncreaseDateTime: '1970-01-01T09:00:00+09:00'
    NumberOfDecreasesToday: 0
    ReadCapacityUnits: 0
    WriteCapacityUnits: 0
  TableArn: arn:aws:dynamodb:ddblocal:000000000000:table/users
  TableName: users
  TableSizeBytes: 66
  TableStatus: ACTIVE

counterテーブル

$ aws --endpoint http://localhost:8000 dynamodb delete-table --table-name counter
TableDescription:
  AttributeDefinitions:
  - AttributeName: id
    AttributeType: S
  BillingModeSummary:
    BillingMode: PAY_PER_REQUEST
    LastUpdateToPayPerRequestDateTime: '2021-02-17T13:02:33.947000+09:00'
  CreationDateTime: '2021-02-17T13:02:33.947000+09:00'
  ItemCount: 1
  KeySchema:
  - AttributeName: id
    KeyType: HASH
  ProvisionedThroughput:
    LastDecreaseDateTime: '1970-01-01T09:00:00+09:00'
    LastIncreaseDateTime: '1970-01-01T09:00:00+09:00'
    NumberOfDecreasesToday: 0
    ReadCapacityUnits: 0
    WriteCapacityUnits: 0
  TableArn: arn:aws:dynamodb:ddblocal:000000000000:table/counter
  TableName: counter
  TableSizeBytes: 16
  TableStatus: ACTIVE

get-item

before

    /**
     * Get user by the given user id.
     *
     * @param int $userId The user id
     *
     * @return UserReaderData The user data
     *
     * @throws DomainException
     * @throws Exception
     */
    public function getUserById(int $userId): UserReaderData
    {
        try {
            $marshaler = new Marshaler();
            $key = $marshaler->marshalJson((string)json_encode([
                'id' => $userId
            ]));
            $row = $this->client->getItem([
                'TableName'      => 'users',
                'Key'            => $key,
            ]);
            if (!$row->get('Item')) {
                throw new DomainException(sprintf('User not found: %s', $userId));
            }
        } catch (Exception $exception) {
            throw $exception;
        }
        // Map array to data object
        $user = new UserReaderData();
        $user->id = (int)$row['Item']['id']['N'];
        $user->username = (string)$row['Item']['username']['S'];
        $user->first_name = (string)$row['Item']['first_name']['S'];
        $user->last_name = (string)$row['Item']['last_name']['S'];
        $user->email = (string)$row['Item']['email']['S'];

        return $user;

after

    /**
     * Get user by the given user id.
     *
     * @param int $userId The user id
     *
     * @return UserReaderData The user data
     *
     * @throws DomainException
     * @throws Exception
     */
    public function getUserById(int $userId): UserReaderData
    {
        try {
            $result = $this->client->executeStatement([
                'Parameters' => [
                    [
                        'N' => $userId
                    ]
                ],
                'Statement'  => 'SELECT * FROM users WHERE id = ?'
            ]);
            $items = $result->get('Items') ?? [];
            if (!$items) {
                throw new DomainException(sprintf('User not found: %s', $userId));
            } else {
                $marshaler = new Marshaler();
                $result_item = json_decode(
                    $marshaler->unmarshalJson($items[0]),
                    true
                );
            }
        } catch (Exception $exception) {
            throw $exception;
        }

        // Map array to data object
        $user = new UserReaderData();
        $user->id = $result_item['id'];
        $user->username = $result_item['user_name'];
        $user->first_name = $result_item['first_name'];
        $user->last_name = $result_item['last_name'];
        $user->email = $result_item['email']

        return $user;
    }

put-item

before

    /**
     * Insert user row.
     *
     * @param UserCreatorData $user The user
     *
     * @return int The new ID
     * @throws Exception
     */
    public function insertUser(UserCreatorData $user): int
    {
        $id = $this->incrementUserId();

        try {

            $marshaler = new Marshaler();
            $item = $marshaler->marshalJson((string)json_encode([
                'id'         => $id,
                'username'   => $user->username,
                'first_name' => $user->first_name,
                'last_name'  => $user->last_name,
                'email'      => $user->email,
            ]));

            $this->client->putItem([
                'TableName' => 'users',
                'Item'      => $item,
            ]);
        } catch (Exception $exception) {
            throw $exception;
        }

        return $id;
    }

after

    /**
     * Insert user row.
     *
     * @param UserCreatorData $user The user
     *
     * @return int The new ID
     * @throws Exception
     */
    public function insertUser(UserCreatorData $user): int
    {
        $id = $this->incrementUserId();

        try {
            $this->client->executeStatement([
                'Parameters' => [
                    [
                        'N' => $id,
                    ],
                    [
                        'S' => $user->username,
                    ],
                    [
                        'S' => $user->first_name,
                    ],
                    [
                        'S' => $user->last_name,
                    ],
                    [
                        'S' => $user->email
                    ]
                ],
                'Statement'  => "INSERT INTO users
                                 VALUE {
                                    'id': ?,
                                    'user_name': ?,
                                    'first_name': ?,
                                    'last_name': ?,
                                    'email': ?
                                 }"
            ]);
        } catch (Exception $exception) {
            throw $exception;
        }

        return $id;
    }

update-item

updateは元ネタがないのでスクラッチ。

    /**
     * Update user row.
     *
     * @param UserUpdaterData $userUpdaterData
     *
     * @return UserUpdaterData The user data
     *
     * @throws Exception
     */
    public function updateUser(UserUpdaterData $userUpdaterData): UserUpdaterData
    {
        $statement = 'UPDATE users ';
        $parameters = [];
        foreach ($userUpdaterData->jsonSerialize() as $k => $v) {
            if ($k === 'id') {
                // TODO: Omit Partition Key
                continue;
            }
            if (is_null($v)) {
                continue;
            }
            $statement .= sprintf('SET %s=? ', $k);
            $parameters[] = $marshaler->marshalValue($v);
        }
        $statement .= ' WHERE id = ? RETURNING ALL NEW *';
        $parameters[] = [
            'N' => $userUpdaterData->id
        ];

        try {
            $result = $this->client->executeStatement([
                'Parameters' => $parameters,
                'Statement'  => $statement
            ]);
            $items = $result->get('Items') ?? [];
            if (!$items) {
                throw new DomainException(sprintf('User not found: %s', $userUpdaterData->id));
            } else {
                $marshaler = new Marshaler();
                $result_item = json_decode(
                    $marshaler->unmarshalJson($items[0]),
                    true
                );
            }
        } catch (Exception $exception) {
            throw $exception;
        }

        return new UserUpdaterData($userUpdaterData->id, $result_item);
    }

atomic counter

こちらはbeforeも含めてスクラッチ。
counterテーブルへの初期登録が必要です。

aws --endpoint http://localhost:8000 ddb put counter '{id: "users", counter: 0}'

before

    /**
     * @return int
     * @throws Exception
     */
    private function incrementUserId(): int
    {
        $marshaler = new Marshaler();
        $key = $marshaler->marshalItem([
            'id' => 'users'
        ]);
        $eav = $marshaler->marshalItem([
            ':increment' => 1
        ]);
        $ean = [
            '#c' => 'counter'
        ];
        $params = [
            'TableName'                 => 'counter',
            'Key'                       => $key,
            'UpdateExpression'          => 'ADD #c :increment',
            'ExpressionAttributeValues' => $eav,
            'ExpressionAttributeNames'  => $ean,
            'ReturnValues'              => 'UPDATED_NEW'
        try {
            $result = $this->client->updateItem($params);
            $attr = $result['Attributes'] ?? [];
            $counter = json_decode(
                $marshaler->unmarshalJson($attr),
                true
            );
            if ($counter) {
                return $counter['counter'];
            } else {
                throw new DomainException(sprintf('Counter not found: %s', 'users'));
        } catch (DynamoDbException $exception) {
            throw $exception;
        }
    }

after

    /**
     * @return int
     * @throws Exception
     */
    private function incrementUserId(): int
    {
        $marshaler = new Marshaler();
        $statement = 'UPDATE counter '
                   . 'SET counter = counter + ? '
                   . 'WHERE id = ? RETURNING ALL NEW *;';
        $parameters = [
            [
                'N' => 1
            ],
            [
                'S' => 'users'
            ]
        ];

        try {
            $result = $this->client->executeStatement([
                'Parameters' => $parameters,
                'Statement'  => $statement
            ]);
            $items = $result->get('Items') ?? [];
            if (!$items) {
                throw new DomainException(sprintf('Counter not found: %s', 'users'));
            } else {
                $result_item = json_decode(
                    $marshaler->unmarshalJson($items[0]),
                    true
                );
                return $result_item['counter'];
            }
        } catch (Exception $exception) {
            throw $exception;
        }
    }

感想

Python/Java/Node.jsはNoSQL workbenchでコード生成してくれますが、PHPは生成してくれません。
無念。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?