将查询传递给使用第二个数据库的module_hook

时间:2018-12-29 13:22:47

标签: php drupal drupal-7 drupal-modules

我想使用另一个数据库连接,该连接使用我自己的名为“连接器”的模块中的钩子查询数据库。该钩子应带有一个查询参数,该参数将在切换回主Drupal 7数据库之前使用辅助数据库执行。问题似乎是,查询是针对(主要的)Drupal数据库生成的(因为它是在切换到辅助数据库之前创建的),即使SQL本身对我来说看起来很好。我在这里做什么错了?

挂钩:

function connector_api_db($query) {

$result = [];

$database_info = array(
    'database' => variable_get('connector_db_name'),
    'username' => variable_get('connector_db_user'),
    'password' => variable_get('connector_db_pwd'),
    'host' => variable_get('connector_db_host'),
    'driver' => 'mysql',
);

Database::addConnectionInfo('secondary_db', 'default', $database_info);

db_set_active('secondary_db'); // Initialize the connection

    /* This actually works but I can here not use my own query as a parameter in this function. Not what I want. */
    //$query = db_select('registration')->fields('registration')
//->condition('id', 46, '=');

echo $query->__toString() . "<br />"; /* Get query string */
var_dump($query->getArguments()); /* Get the arguments passed to the string */

$result = $query->execute()->fetchAssoc();

echo "<pre>";
print_r($result);
echo "</pre>";


db_set_active(); // without the paramater means set back to the default for the site
drupal_set_message(t('The queries have been made.'));

return $result;

}

调用该钩子:

    $query = db_select('registration')
        ->fields('registration')
        ->condition('id', 46, '=');
$response = module_invoke('connector', 'api_db', $query);

结果如下:

SELECT registration.* FROM {registration} registration WHERE (id = :db_condition_placeholder_0) 

array(1) { [":db_condition_placeholder_0"]=> int(46) }

Additional uncaught exception thrown while handling exception.
Original
PDOException: SQLSTATE[42S02]: Base table or view not found: 1146 Table &#039;drupal.registration&#039; doesn&#039;t exist: SELECT registration.* FROM {registration} registration WHERE (id = :db_condition_placeholder_0) ; Array ( [:db_condition_placeholder_0] =&gt; 46 ) in connector_api_db() (line 70 of /<path-to-drupal>/drupal-7.61/modules/connector/connector.main.inc).

2 个答案:

答案 0 :(得分:1)

严格来讲,这不是答案,而是建议。

问题是您的钩子收到一个已经用原始数据库连接构建的$query,这说明了错误。为了有效,db_set_active()应该在调用Query构造函数之前先 调用。

否则,您将必须同时覆盖 Select 类及其父类 Query 的构造函数,或更确切地说,是“重建” Select语句的“查询”构建器实例(如果需要范围查询等,也可能是其他)。

此外,除非有明确的要求在这种情况下提供专用挂钩,否则没有必要。

例如,执行以下操作会更简单:

connector.module

/**
 * Implements hook_init()
 * 
 * Adds secondary database connection information. 
 */
function connector_init() {
  $database_info = array(
      'database' => variable_get('connector_db_name'),
      'username' => variable_get('connector_db_user'),
      'password' => variable_get('connector_db_pwd'),
      'host' => variable_get('connector_db_host'),
      'driver' => 'mysql',
  );

  Database::addConnectionInfo('secondary_db', 'default', $database_info);  
}

/**
 * Switches from primary to secondary database and the other way round. 
 */
function connector_switch_db($db_key = 'secondary_db') {
  return db_set_active($db_key);
}

示例:

// Set secondary db active. 
$primary_db_key = connector_switch_db();

$query = db_select('registration')
        ->fields('registration')
        ->condition('id', 46, '=');

$result = $query->execute()->fetchAssoc();

// Switch back to the primary database. 
connector_switch_db($primary_db_key);

这种较软的方法还可以防止使用(...)->execute()->fetchAssoc()之类的硬代码,并允许实现自由执行并根据需要获取结果。

答案 1 :(得分:1)

这是我想出的(包括缓存位)供以后参考:

function connector_init() {
    foreach([
        'connector_db_host',
        'connector_db_name',
        'connector_db_user',
        'connector_db_pwd',
    ] as $var) {
        drupal_static($var, variable_get($var, ''));
    }

    Database::addConnectionInfo('secondary_db', 'default', [
        'host' => drupal_static('connector_db_host'),
        'database' => drupal_static('connector_db_name'),
        'username' => drupal_static('connector_db_user'),
        'password' => drupal_static('connector_db_pwd'),
        'driver' => 'mysql',
    ]);
}

function connector_api_db($query) {

    $result = [];

    $sql_query = $query->__toString();
    $arguments = $query->getArguments();

    if (count($arguments) > 0) {
        ksort($arguments);
    }
    $cache_id = 'sql_' . md5($sql_query . '|' . print_r($arguments, 1));

    $cache = cache_get($cache_id, 'cache_connector');

    if ($cache && $cache->expire >= time()) {
        $result = $cache->data;
    } else {

        db_set_active('secondary_db'); // Switch to secondary db */
        $query_failed = FALSE;

        try {
            $db_result = db_query($sql_query, $arguments);
            $db_result = (0 === $db_result->rowCount()) ? [] : $db_result->fetchAssoc();
        } catch (\PDOException $e) {
            $db_result = [];
            $query_failed = TRUE;
        } finally {
            db_set_active(); /* switch back to default */
        }

        $result = (object)[
            'query' => $sql_query,
            'arguments' => $arguments,
            'result' => (object)$db_result
        ];

        if (!$query_failed) {
            cache_set($cache_id, $result, 'cache_connector', strtotime('+10 minutes'));
        }

    }

    return $result;

}