Search code examples
phpmysqlsqldrupaldrupal-7

What is wrong with these Drupal SQL queries using db_or()?


The institution I work for has an existing and working integration between our Drupal CMS and an LDAP server. One feature of that integration is the ability to have records pulled from the LDAP to populate fields in Drupal. We tell Drupal what fields and content types to fill using custom modules that were originally designed to only accommodate (1) a single machine name for a content type and (2) a single machine name per field.

We are now trying to modify those configuration items to exist as comma-delineated lists, so we can supply LDAP information to an arbitrary number of content types that may not necessarily be using shared fields. The modification of (2), the ability to enter more than one machine name for a field and still have it fill with the desired data, is already working. However, the modifications required to finish (1), which will allow the records to feed out to multiple content types, depends on a SQL query whose syntax I believe I must be getting wrong.

The configuration item where the machine name(s) for the content type gets stored is internally called dart_netid_content_type by Drupal. Here is the old code, which works for a single machine name identifying a single content type:

// All nodes of type from dart_netid_content_type, with either no LDAP lookup time, or over 24 hours ago
$query = db_select('node');
$query->leftjoin('dart_ldap_update', 'ldap', 'node.nid = ldap.nid');
$query->fields('node', array('nid'))
  ->fields('ldap', array('nid', 'ldap_updated'))
  ->condition('type', variable_get('dart_netid_content_type', ''), '=')
  ->condition('status', '1', '=')
  ->condition(db_or()->condition('ldap_updated', NULL, 'IS')->condition('ldap_updated', $limit, '<'))
  ->orderby('ldap_updated', 'ASC');

Here is the new query, which is supposed to work for multiple content types. Interestingly, the query is not completely broken; if you enter a single machine name identifying a single content type, the query continues to work. However, if you store a comma-delineated list inside of dart_netid_content_type, which is already working on other configuration items such as the machine names of the fields that are supposed to be populated with data, then the query fails and the module will not identify any nodes or populate any of their fields.

/*
 * Break up dart_netid_content_type into an array
 */
$dart_netid_content_type_array = explode(",", variable_get('dart_netid_content_type', ''));

/*
 * For each item in the array, create an 'or' conditional in a db_query that
 * checks for all of those content types
 */
$content_type_or = db_or();
foreach($dart_netid_content_type_array as $content_type) {
  $content_type_or->condition('type', $content_type, '=');
}

// All nodes of type from dart_netid_content_type, with either no LDAP lookup time, or over 24 hours ago
$query = db_select('node');
$query->leftjoin('dart_ldap_update', 'ldap', 'node.nid = ldap.nid');
$query->fields('node', array('nid'))
  ->fields('ldap', array('nid', 'ldap_updated'))
  ->condition($content_type_or)
  ->condition('status', '1', '=')
  ->condition(db_or()->condition('ldap_updated', NULL, 'IS')->condition('ldap_updated', $limit, '<'))
  ->orderby('ldap_updated', 'ASC');

To articulate the intended differences between these two versions in a way that will hopefully be more intuitive: dart_netid_content_type may now be one content type, or more than one. The previous query was searching for something akin to WHERE type = dart_netid_content_type. What I am trying to coax it to do now instead is to look at the individual items in the dart_netid_content_type_array and make a query looking something like WHERE (type = dart_netid_content_type_array[0]) OR (type = dart_netid_content_type_array[1]) OR ... OR (type = dart_netid_content_type_array[sizeof(dart_netid_content_type_array)]).

Something appears to be wrong with my logic when wrapping this in Drupal's db_query and db_or syntax.

If need be, I can provide more context by way of sharing more of the custom modules that drive the LDAP-to-node functionality.

Thank you in advance for your help.


Solution

  • How about using IN to determine if the content type is in the array of values? Something like this:

    $dart_ct_array = explode(",", variable_get('dart_netid_content_type', ''));
    $dart_netid_content_types = "('" . implode("','", $dart_ct_array) . "')";
    
    $query = db_select('node');
    $query->leftjoin('dart_ldap_update', 'ldap', 'node.nid = ldap.nid');
    $query->fields('node', array('nid'))
      ->fields('ldap', array('nid', 'ldap_updated'))
      ->condition('type', $dart_netid_content_types, 'IN')
      ->condition('status', '1', '=')
      ->condition(db_or()->condition('ldap_updated', NULL, 'IS')->condition('ldap_updated', $limit, '<'))
      ->orderby('ldap_updated', 'ASC');