Slim(PHP Framework)事始め(3)

## あらまし
配属先で[Slim]を使っているので、使い方などをメモしている。
[前回](http://qiita.com/yookihiroo/items/8222f4c44c7b0e4fe686)

### Whoops補足

前回、Whoopsの導入で、[dopesong/Slim-Whoops](https://github.com/dopesong/Slim-Whoops)を導入して[デバッグバー(kitchenu/Slim-Debugbar)](https://github.com/kitchenu/Slim-DebugBar)を導入する際に

```php
12    // ob_start以降のエラーをハンドルする
13    $container['phpErrorHandler']/* = $container['errorHandler']*/ = function($c) {
14        return new Dopesong\Slim\Error\Whoops($c->get('settings')['displayErrorDetails']);
15    };
```
のようにerrorHandlerをコメントアウトしていたのは、デバッグバーの受け取るエラーが合わない為でした
なので、vendor以下を修正する事になってしまいますが、

```php:vendor/dopesong/slim-whoops/src/Whoops.php
...
 24 class Whoops extends \Slim\Handlers\Error // デバッグバー側で受け取るエラーを継承させる
 25 {
...
118    protected function determineContentType(ServerRequestInterface $request)
119    {// Slim\Handlers\Error::determineContentTypeのアクセス修飾子に合わせる
...
```
のようにするとコメントを外しても動作します

### [Phpmig](https://github.com/davedevelopment/phpmig)導入

マイグレーションツールとして、Phpmigを導入してみます
マイグレーションツールとしては、[Phinx](https://github.com/robmorgan/phinx)も有名ですが、少しいじってみた感じとして、データベースにマイグレーションの情報が書き込まれるのがどうだろうな、と思ったりした為です
ちなみに、Phpmigですと、マイグレーション情報として初期化したディレクトリ内に、.migrations.logというファイルが作られて、その中に情報が書き込まれます

#### インストール

```shell-session
$ composer require davedevelopment/phpmig
```
#### マイグレーション用ディレクトリ作成

```shell-session
$ mkdir data
```
デフォルトだとプロジェクトルートにmigrationsというフォルダを作成してその中に色々作られますが、フィクスチャなど関連したデータをまとめたかったので、data/というディレクトリを作成しました

#### コマンドライン用に登録

```json:composer.json
...
	"scripts": {
...
            "phpmig": "cd data && phpmig"
...
	}
...
```

#### 初期化

```shell-session
$ composer phpmig init
```
data/phpmig.phpが出来るので、それを色々修正して、自分のシステムに合わせるようにします

```php:data/phpmig.php
<?php

use \Phpmig\Adapter;

// get environment variables from phpdotenv
$dotenv = new Dotenv\Dotenv(__DIR__. '/../');
$dotenv->load();
// disable web debug tools
putenv('DEBUG=0');

$settings = require __DIR__ . '/../src/settings.php';

// we shold not need Slim\App, but want container...
$app = new Slim\App($settings);

require __DIR__ . '/../src/dependencies.php';

$container = $app->getContainer();

// replace this with a better Phpmig\Adapter\AdapterInterface
$container['phpmig.adapter'] = new Adapter\File\Flat(__DIR__ . DIRECTORY_SEPARATOR . 'migrations/.migrations.log');
...
```
こんな感じに書き換えて、slim側の設定を読み込むようにします

#### マイグレーションファイル作成

```shell-session
$ composer phpmig generate AddTableTest
> cd data && phpmig 'generate' 'AddTableTest'
+f ./migrations/20161112055904_AddTableTest.php
```
まだデータベースとの接続などを導入していないのでここまでです

### Idiorm & Paris導入

データベースアクセスに何を使おうか？とここしばらく考えていましたが、軽量と聞いていた[Idiorm & Paris](http://j4mie.github.io/idiormandparis/)を導入してみます
IdiormがORM、ParisがActiveRecordのようです

```shell-session
$ composer require j4mie/idiorm j4mie/paris
```

データベース設定

```vim:.env
# database settings
DB_PHPMIG1_DSN = mysql:host=localhost;dbname=phpmig1
DB_PHPMIG1_USER = root
DB_PHPMIG1_PASS = 

DB_PHPMIG2_DSN = mysql:host=localhost;dbname=phpmig2
DB_PHPMIG2_USER = root
DB_PHPMIG2_PASS = 
```

```php:src/settings.php
<?php
return [
    'settings' => [
...
        // database settings
        'db' => [
            'phpmig1' => [
                'dsn'  => getenv('DB_PHPMIG1_DSN'),
                'user' => getenv('DB_PHPMIG1_USER'),
                'pass' => getenv('DB_PHPMIG1_PASS'),
            ],
            'phpmig2' => [
                'dsn'  => getenv('DB_PHPMIG2_DSN'),
                'user' => getenv('DB_PHPMIG2_USER'),
                'pass' => getenv('DB_PHPMIG2_PASS'),
            ],
        ],
    ],
];
```
職業柄か、複数DBを使用する事が多いので、2台設定しています



```php:src/dependencies.php
...
$container = $app->getContainer();

if ($container->get('settings')['debug']) {
...
    // debugbar setting
    $settings = $container->get('settings')['debugbar'];
    $provider = new Kitchenu\Debugbar\ServiceProvider($settings);
    $provider->register($app);

    // デバッグバーに、データベース項目を追加する
    ORM::configure('logging', true);
    $collector = new DebugBar\DataCollector\PDO\PDOCollector();
    $container['debugbar']->addCollector($collector);
}
...
// databases
foreach ($container->get('settings')['db'] as $dbname => $settings) {
    // idiorm settings
    ORM::configure([
        'connection_string' => $settings['dsn'],
        'username' => $settings['user'],
        'pass' => $settings['pass'],
    ], null, $dbname);
    // デバッグ時には、デバッグバーに、コネクションを登録する
    if ($container->get('settings')['debug']) {
        $pdo = ORM::get_db($dbname);
        $debugbar = $container['debugbar'];
        $collector = $debugbar->getCollector('pdo');

        $source = new DebugBar\DataCollector\PDO\TraceablePDO($pdo);
        $collector->addConnection($source, $dbname);
    }
    // コンテナからもコネクションを取得出来るように設定
    $container["db.$dbname"] = function($c) use ($dbname) {
        return ORM::get_db($dbname);
    };
}
```
\$container["db.\$dbname"]としている為に、\$container->db.\$dbnameという取得の仕方が出来ません
あまりコンテナから取得する事は無いだろう、という事と、表記的に、db以下にコネクションをまとめたい、という事からこんな事をしていますが、取得したい場合は
\$container->get("db.\$dbname")という感じで取得出来ます

#### マイグレーションファイル設定

上の、マイグレーションファイル作成で生成されたファイルを編集します

```php:migrations/20161112055904_AddTableTest.php
<?php

use Phpmig\Migration\Migration;

class AddTestTable extends Migration
{
    /**
     * Do the migration
     */
    public function up()
    {
        $this->get('db.phpmig1')->exec("
            CREATE TABLE `test1` (
              `id` int(11) NOT NULL AUTO_INCREMENT,
              `name` varchar(32) NOT NULL,
              `value` int(11) NOT NULL,
              `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
              PRIMARY KEY (`id`)
            )
        ");
        $this->get('db.phpmig2')->exec("
            CREATE TABLE `test2` (
              `id` int(11) NOT NULL AUTO_INCREMENT,
              `name` varchar(32) NOT NULL,
              `value` int(11) NOT NULL,
              `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
              PRIMARY KEY (`id`)
            )
        ");
    }

    /**
     * Undo the migration
     */
    public function down()
    {
        $this->get('db.phpmig1')->exec("DROP TABLE `test1`");
        $this->get('db.phpmig2')->exec("DROP TABLE `test2`");
    }
}
```

マイグレーション適用

```shell-session
$ composer phpmig migrate
> cd data && phpmig 'migrate'
 == 20161112055904 AddTableTest migrating
 == 20161112055904 AddTableTest migrated 0.0169s
```
これで、テーブルが作成されます


```vim:data/migrations/.migrations.log
20161112055904
```
適用されたマイグレーションの番号が書き込まれて行きます

ロールバック

```shell-session
$ composer phpmig rollback
> cd data && phpmig 'rollback'
 == 20161112055904 AddTableFoo reverting
 == 20161112055904 AddTableFoo reverted 0.0222s
```
これで、直近のマイグレーションが取り消されて、テーブルが削除されます

### 感想
Whoopsの件は、修正をプルリクしておくべきなのかもしれませんが、そもそも今回使用してみたデバッグバー側でエラーを補足しなければ問題無かったはず（エラーしたらWhoopsが捕捉して表示するのでデバッグバー側で処理する必要が無い）なので、どのように修正するのか難しいところです