RedBeanPHPメモ

## ReadBeanPHPとは

[公式](http://www.redbeanphp.com/index.php)を見ると

```php
require 'rb.php';
R::setup();
R::setAutoResolve( TRUE );        //Recommended as of version 4.2
$post = R::dispense( 'post' );
$post->text = 'Hello World';

$id = R::store( $post );          //Create or Update
$post = R::load( 'post', $id );   //Retrieve
R::trash( $post );                //Delete
```
このようなサンプルがある通り、手軽に使えるライブラリを目指して開発されたもののようです
（何も設定しない場合は、/tmp/red.dbにSQLite形式で保存されるようです）

そんなに知名度が高いライブラリでは無いと思っていたのですが、[Slim マイクロフレームワークで REST アプリケーションを作成する](http://www.ibm.com/developerworks/jp/xml/library/x-slim-rest/)に取り上げられているので、手軽にサンプルを作るのに丁度いい、という扱いで使用されているのではないかと思います

ただ、残念ながら開発が終了したもののようで、[ロードマップ](http://www.redbeanphp.com/index.php?p=/roadmap)を見ると、2020年にサポートも終了するようで、今から使用するには微妙な感じです

### 特徴

[チュートリアル](http://www.redbeanphp.com/index.php?p=/tutorial)を試してみると、だいたいどんなものかは分かるかと思います。

```php
$object = R::dispense(テーブル名);
```
で生成されたオブジェクトのプロパティに、適当に値を設定しておくと、saveした時点で数値の場合はInteger、文字列の場合にはTextで型を設定したテーブルが作成されます。
このままだとスキーマが変わっていってしまうので、チュートリアルの最後に書いてあるように

```php
R::freeze(true);
```
とすると、スキーマが変わらないように出来ます。
RDBというより、NoSQLのように使えるのが特徴かと思います。

Step 6: Playing with models
ここの説明にあるクラス名が違うので、ドキュメントが古いかもしれません

```php:
class Model_Note extends RedBean_SimpleModel {// RedBean_SimpleModel -> RedBeanPHP\SimpleModel  public function update() {
    if ( strlen( $this->bean->note ) < 4 ) 
      die( "Note is too short!\n" );
  }
}
```

### 使ってみる

チュートリアルの通りにダウンロードしてきてもいいですが、composerでもインストールできます

```shell-session
$ composer require gabordemooij/redbean
```

```php:test.php
<?php
require __DIR__ . '/vendor/autoload.php';

use RedBeanPHP\R;
$frozen = false;
R::setup('mysql:host=localhost;dbname=test', 'root', 'password', $frozen);

$test = R::dispense('test');
$test->name = 'bar';
$id = R::store($test);// id, nameがあるtestテーブルが作成される

$test2 = R::load('test', $id);// 今セーブしたレコードを取得
$test2->created_at = date('Y-m-d H:i:s');
R::store($test2);// testテーブルに、created_atカラムが追加されている
```

### 注意点

[Conventions](http://www.redbeanphp.com/index.php?p=/crud)の項目にあるように

```php
$page = R::dispense('page'); //valid

$page = R::dispense( 'Page' ); //invalid: uppercase
$page = R::dispense( 'cms_page' ); //invalid: _
$page = R::dispense( '@#!' ); //invalid
```
- テーブル名にアンダースコアや´を使えないようです。
- プライマリキーは「id」固定

### トランザクション
職業柄か、複数のデータベースを扱う事が多い為、データベース関連を見るとまず最初に2相コミットが出来るか？を調べてみたりするのですが、
だいたいこんな感じで出来るようです

```php:2相コミット
// コネクションラベル, DSN, db_user, password, is_freeze
// 最後のis_freezeは、スキーマを動的に変更するような場合にfalse 通常の使い方だとtrueを指定する
R::addDatabase('test1', 'mysql:host=localhost;dbname=test', 'root', null, true);
R::addDatabase('test2', 'mysql:host=localhost;dbname=test2', 'root', null, true);

try {
    //使用するデータベースを指定してからトランザクション開始
    R::selectDatabase('test1');
    R::begin();

    $obj1 = R::dispense('test1');
    $obj1->name = 'tran test1';
    R::store($obj1);

    //2つ目のDB指定
    R::selectDatabase('test2');
    R::begin();
    $obj2 = R::dispense('test2');
    $obj2->name = 'tran test2';
    R::store($obj2);

    //2つ目のDBコミットしてから、1つ目のDBをコミットする
    R::commit();

    R::selectDatabase('test1');
    R::commit();
} catch (Exception $e) {
    R::selectDatabase('test2');
    R::rollback();
    R::selectDatabase('test1');
    R::rollback();
}
```
テーブルにアクセスする前にデータベースを選択するのはある意味自然な流れなのですが、少々面倒ですね

ちなみに、トランザクションにクロージャを使えるので、次のようなコードでも出き・・・そうなのですが、これはライブラリのバグで、2つ目に指定したデータベースのトランザクションが開始しないので、ロールバック時におかしな事になります。

```php:2相コミット（2番目のトランザクションが働かない）
R::selectDatabase('test1');
R::transaction(function() {
    $obj = R::dispense('test1');
    $obj->name = 'tran test1';
    R::store($obj);
    R::selectDatabase('test2');
    // R::transaction内で$depthをカウントしているが、staticで持っていてコネクションに紐づいていないので
    // コネクションを複数使用する場合には、後からのstartTransactionが動作しない
    R::transaction(function() {
        $obj = R::dispense('test2');
        $obj->name = 'tran test2';
        R::store($obj);
    });
});
```

### 感想
PHPのSlimフレームワークがシンプルで、ちょっとしたアプリケーションのサンプルを作るのに良さそう、と思ったので、
同じくシンプルなORMを探していたら、このようなライブラリを見つけたので少し調べてみました
ただ、先述した通り、開発終了して先が長く無さそうなところが残念なところです