トランザクションと複数データソース

単一の DAO

$userDAO->startTransaction();
try {
    $userDAO->insert($user);
    $userDAO->commit();
} catch (Throwable $e) {
    $userDAO->rollback();
    throw $e;
}

複数の DAO にまたがるトランザクション

DataSourceManager でグローバルトランザクションを開始すると、それ以降に使用されるすべてのデータソースが自動的にそのトランザクションに参加します。

use _Flexagon\Libs\DataSourceManager;

DataSourceManager::startTransaction();
try {
    $userDAO->insert($user);
    $orderDAO->insert($order);
    $pointDAO->update($point);
    DataSourceManager::commit();
} catch (Throwable $e) {
    DataSourceManager::rollback();
    throw $e;
}

コミットとロールバックは、接続が登録された順序の逆順で実行されます。

DAO へのデータソースの割り当て

DATA_SOURCE_ID 定数をオーバーライドします。

class StatsDAO extends BaseMySqlDAO {
    const DATA_SOURCE_ID = 'analytics';   // _Global::$DATA_SOURCES['analytics']
    protected string $tableName = 'daily_stats';
}

接続の失敗

データソースに接続できない場合、DAO のコンストラクタは _Flexagon\DB\ConnectionException をスローします(デフォルトの _Global::$DB_CONNECT_FAILURE_FATAL = true の場合)。捕捉しなければ、リクエストは 500 で終了し、コマンドラインでは終了コード 255 になります。レスポンスを自分で決めたい場合は、この例外を捕捉してください。

// public/_entry.php
set_exception_handler(function (Throwable $e) {
    if ( $e instanceof \_Flexagon\DB\ConnectionException ) {
        http_response_code(503);
        TemplateLoader::show('maintenance');
        return;
    }
    throw $e;
});

メッセージにはデータソース名($e->getDataSourceId()、つまり _Global::$DATA_SOURCES のキー)だけが含まれます。ホストやアカウントを含むドライバーのメッセージはエラーログに出力され、元の PDOException は getPrevious() で取得できます。本番環境では display_errors を無効にしておいてください。捕捉されなかった例外が画面に表示されると、前の例外の内容も含まれてしまいます。

例外をスローする代わりに connect() が false を返すようにするには、$DB_CONNECT_FAILURE_FATAL = false を設定します。