0
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 5 years have passed since last update.

PHP Slim 環境構築(5) PDOとDBアクセス

Last updated at Posted at 2019-09-24

PHP Slim 環境構築(5) PDOとDBアクセス

Introduction

前回は、Autoloader周りを触りましたが、今回はPDOを使ってDBにアクセスしてみたいと思います。

変更点

ソースツリー

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

$(PROJECTROOT)
  /compose
    docker-compose.yml
  /src
    /hoge
      /lib
        /Controller
          PlayerController.php            (NEW!)
          PlayerCreatedLogController.php  (NEW!)
        /Model
          Player.php                      (NEW!)
          PlayerCreatedLog.php            (NEW!)
      /public
        index.php
    composer.json
    composer.lock

docker-compose.yml

web_hogeコンテナから、DBコンテナ(mysqlとpostgresql)にアクセスするために、linksを設定します。
これで、ホスト名"mysql"と"postgresql"でそれぞれのDBを識別することができます。

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

composer.json/composer.lock

Slimフレームワークでは、PDOの生成はDIコンテナから行うのが一般的です。
PSR-11に対応しているDIコンテナとして、php-diをcomposerで追加します。
また、PDOおよびJSONのPHP拡張を使用するので、それらもcomposerで追加しておきます。

$ composer require php-di/php-di:6.0.9
$ composer require ext-pdo
$ composer require ext-json

index.php

19~58行目:
DIコンテナにDBの設定とDBのfactoryを追加しています。本当はソースを分けたりいろいろすべきですが、べらっと書いてしまっています。

62~63行目:
レスポンスに自動的にContent-Lengthを付けるためのMiddlewareを追加しています。
PDO周りとは直接関係ないのですが、PDOから取得したデータをそのまま出すだけだと、Content-Lengthが付かないので、一応お行儀よくしました。

68~70行目:
前回のExampleBeforeMiddlewareとExampleAfterMiddlewareを"/"だけに限定して付けています。
こういうやりかたもできるというテストです。

72~76行目:
GET "/player/{id}"、POST "/player"、PUT "/player/{id}"のCRUD (Dはありませんが)の定義です。
PlayerControllerに処理を移譲しており、この中でMySQLへのアクセス処理を行っています。

78~81行目:
GET "/player_created/{id}"、GET "/player_created"の定義です。
PlayerCreatedLogControllerに処理を移譲しており、この中でPostgreSQLへのアクセス処理を行っています。

/src/hoge/public/index.php
<?php

use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;
use Slim\Routing\RouteCollectorProxy;
use DI\ContainerBuilder;

use Hoge\ExampleAfterMiddleware;
use Hoge\ExampleBeforeMiddleware;
use Hoge\Controller\PlayerController;
use Hoge\Controller\PlayerCreatedLogController;

/** @var Composer\Autoload\ClassLoader $loader */
$loader = require __DIR__ . '/../../vendor/autoload.php';
$loader->addPsr4('Hoge\\', __DIR__ . '/../lib');

$containerBuilder = new ContainerBuilder();
$containerBuilder->addDefinitions([
    'settings' => [
        'userdb' => [
            'host' => 'mysql',
            'dbname' => 'userdb',
            'user' => 'scott',
            'password' => 'tiger'
        ],
        'logdb' => [
            'host' => 'postgresql',
            'dbname' => 'logdb',
            'user' => 'root',
            'password' => 'hogehoge'
        ]
    ],
    'userdb' => function (ContainerInterface $container) {
        $settings = $container->get('settings')['userdb'];
        $dsn = 'mysql:host=' . $settings['host'] . ';dbname=' . $settings['dbname'];
        $options = [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES => false
        ];
        return new PDO($dsn, $settings['user'], $settings['password'], $options);
    },
    'logdb' => function (ContainerInterface $container) {
        $settings = $container->get('settings')['logdb'];
        $dsn = 'pgsql:host=' . $settings['host'] . ';dbname=' . $settings['dbname'];
        $options = [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES => false
        ];
        return new PDO($dsn, $settings['user'], $settings['password'], $options);
    }
]);
$container = $containerBuilder->build();

AppFactory::setContainer($container);
$app = AppFactory::create();

// Middleware
$contentLengthMiddleware = new \Slim\Middleware\ContentLengthMiddleware();
$app->add($contentLengthMiddleware);

$app->get('/', function (Request $request, Response $response, array $args) {
    $response->getBody()->write('Hello, World!');
    return $response;
})
    ->add(new ExampleBeforeMiddleware())
    ->add(new ExampleAfterMiddleware());

$app->group('/player', function (RouteCollectorProxy $group) {
    $group->get('/{id}', PlayerController::class . ':get');
    $group->post('', PlayerController::class . ':post');
    $group->put('/{id}', PlayerController::class . ':put');
});

$app->group('/player_created', function (RouteCollectorProxy $group) {
    $group->get('/{id}', PlayerCreatedLogController::class . ':get');
    $group->get('', PlayerCreatedLogController::class . ':list');
});

$app->run();

PlayerController

httpリクエストを受けて、MySQLのplayerテーブルにアクセスするためのControllerです。
CRUDのCRUに相当するメソッドを実装しています。

例外処理をしていなかったり、コードの重複があったりといろいろ手を抜いていますが、あくまでサンプルということでそのままにしています。

なお、それぞれのメソッドは以下のようにアクセスすることができます。

$ wget http://hoge.localhost/user  // get
$ wget http://hoge.localhost/user --post-data='{"player_name":"名前"}'   // post
$ wget http://hoge.localhost/user/1 --method=put --body-data='{"player_name":"新名前"}'
/src/hoge/lib/Controller/PlayerController.php
<?php

namespace Hoge\Controller;

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

use \PDO;

use Hoge\Model\Player;

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

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

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

        $pdo = $this->container->get('userdb'); /** @var PDO $pdo */
        $stmt = $pdo->prepare('select * from player where player_id=:id');
        $stmt->bindParam(':id', $id, PDO::PARAM_INT);
        $stmt->execute();
        $stmt->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, Player::class);

        $player = $stmt->fetch();  /** @var Player|false $player */
        if (!$player) {
            return $response->withStatus(404);
        }

        $response->getBody()->write(json_encode($player->toArray(), JSON_UNESCAPED_UNICODE));
        return $response;
    }

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

        $pdo = $this->container->get('userdb'); /** @var PDO $pdo */
        $stmt = $pdo->prepare('insert into player(player_name) values(:name)');
        $stmt->bindParam(':name', $name, PDO::PARAM_STR);

        $pdo->beginTransaction();
        $stmt->execute();
        $pdo->commit();

        return $response;
    }

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

        $pdo = $this->container->get('userdb'); /** @var PDO $pdo */
        $stmt = $pdo->prepare('update player set player_name=:name where player_id=:id');
        $stmt->bindParam(':name', $name, PDO::PARAM_STR);
        $stmt->bindParam(':id', $id, PDO::PARAM_INT);

        $pdo->beginTransaction();
        $stmt->execute();
        $pdo->commit();

        return $response;
    }
}

Player

playerテーブルのデータモデルです。

/src/hoge/lib/Model/Player.php
<?php

namespace Hoge\Model;

class Player
{
    private $player_id;
    private $player_name;

    public function toArray()
    {
        return [
            'player_id' => $this->player_id,
            'player_name' => $this->player_name
        ];
    }
}

PlayerCreatedLogController

PostgreSQLのplayer_created_logテーブルにアクセスするためのControllerクラスです。
サンプルのため、特定のidのレコード取得と、全レコード取得を実装しています。
PlayerControllerと同様に、かなり手抜きです。

/src/hoge/lib/Controller/PlayerCreatedLogController.php
<?php

namespace Hoge\Controller;

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

use \PDO;

use Hoge\Model\PlayerCreatedLog;

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

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

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

        $pdo = $this->container->get('logdb'); /** @var PDO $pdo */
        $stmt = $pdo->prepare('select * from player_created_log where player_id=:id');
        $stmt->bindParam(':id', $id, PDO::PARAM_INT);
        $stmt->execute();
        $stmt->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, PlayerCreatedLog::class);

        $logList = $stmt->fetchAll();  /** @var PlayerCreatedLog[] $logList */

        $logArray = array_map(
            function ($log) { /** @var PlayerCreatedLog $log */
                return $log->toArray();
            },
            $logList
        );

        $response->getBody()->write(json_encode($logArray, JSON_UNESCAPED_UNICODE));
        return $response;
    }

    public function list(Request $request, Response $response, array $args) : Response
    {
        $pdo = $this->container->get('logdb'); /** @var PDO $pdo */
        $stmt = $pdo->prepare('select * from player_created_log');
        $stmt->execute();
        $stmt->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, PlayerCreatedLog::class);

        $logList = $stmt->fetchAll();  /** @var PlayerCreatedLog[] $logList */

        $logArray = array_map(
            function ($log) { /** @var PlayerCreatedLog $log */
                return $log->toArray();
            },
            $logList
        );

        $response->getBody()->write(json_encode($logArray, JSON_UNESCAPED_UNICODE));
        return $response;
    }
}

PlayerCreatedLog

player_created_logテーブルのデータモデルです。

/src/hoge/lib/Model/PlayerCreatedLog.php
<?php

namespace Hoge\Model;

class PlayerCreatedLog
{
    private $player_id;
    private $created_at;

    public function toArray()
    {
        return [
            'player_id' => $this->player_id,
            'created_at' => $this->created_at
        ];
    }
}

ここまでのソース

こちらでどうぞ。

0
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
0
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?