Transactions and Multiple Data Sources

Single DAO

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

Transactions Across Multiple DAOs

Start a global transaction with DataSourceManager, and every data source used afterward automatically participates in the transaction.

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;
}

Commits and rollbacks are performed in the reverse order in which connections were registered.

Assigning a Data Source to a DAO

Override the DATA_SOURCE_ID constant.

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

Connection Failures

When a data source cannot be reached, the DAO constructor throws _Flexagon\DB\ConnectionException (with the default _Global::$DB_CONNECT_FAILURE_FATAL = true). Left uncaught, the request ends with a 500, or exit code 255 on the command line. Catch it to choose the response yourself.

// 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;
});

The message names only the data source ($e->getDataSourceId(), the key in _Global::$DATA_SOURCES). The driver message, which includes host and account, goes to the error log; the original PDOException is available from getPrevious(). Keep display_errors off in production: an uncaught exception printed on screen includes the previous exception too.

Set $DB_CONNECT_FAILURE_FATAL = false to have connect() return false instead of throwing.