PHP Slim 環境構築(6) Redis

# PHP Slim 環境構築(6) Redis

## Introduction

[前回](https://qiita.com/dynamitecoolguy/items/325f66ce0370714c6335)は、PDOを使ったDBアクセスを試してみましたが、今回はRedisにアクセスしてみたいと思います。軽めの回です。

## 変更点

### ソースツリー

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

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

### docker-compose.yml

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

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

### composer.json/composer.lock

redis extensionを使用しますので、composerで追加しておきます。

```sh
$ composer require ext-redis
```

### index.php

redis用の設定とビルダーをDIに追加します。

```php:/src/hoge/public/index.php
use Hoge\Controller\RedisController;

..(略)..

$containerBuilder->addDefinitions([
    'settings' => [
        ..(略)..
        'redis' => [
            'host' => 'redis'
        ]
    ],
    ..(略)..
    'redis' => function (ContainerInterface $container) {
        $settings = $container->get('settings')['redis'];
        $redis = new Redis();
        $redis->connect($settings['host']);
        $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY);

        return $redis;
    }
]);

..(略)..

$app->group('/redis', function (RouteCollectorProxy $group) {
    $group->get('/{key}', RedisController::class . ':get');
    $group->post('', RedisController::class . ':post');
    $group->put('/{key}', RedisController::class . ':put');
    $group->delete('/{key}', RedisController::class . ':delete');
});
```

### RedisController

CRUDに対応したメソッドを定義しています。

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

namespace Hoge\Controller;

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Container\ContainerInterface;

use \Redis;

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

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

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

        $redis = $this->container->get('redis'); /** @var Redis $redis */
        $value = $redis->get($key);
        if ($value === false) {
            return $response->withStatus(404);
        }
        $response->getBody()->write($value);
        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;

        $redis = $this->container->get('redis'); /** @var Redis $redis */
        $result = $redis->set($key, $value, 30); // とりあえず30sec固定

        $response->getBody()->write($result ? 'OK' : 'Failure');
        return $response;
    }

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

        $redis = $this->container->get('redis'); /** @var Redis $redis */
        $result = $redis->set($key, $value, ['xx', 'ex'=>30]); // すでにあるときだけ更新する

        $response->getBody()->write($result ? 'OK' : 'Failure');
        return $response;
    }

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

        $redis = $this->container->get('redis'); /** @var Redis $redis */
        $result = $redis->del($key);

        $response->getBody()->write($result ? 'OK' : 'Failure');
        return $response;
    }
}
```

## ここまでのソース

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