/[drupal]/contributions/modules/pbs/pbs.fieldapi.inc
ViewVC logotype

Diff of /contributions/modules/pbs/pbs.fieldapi.inc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph | View Patch Patch

revision 1.10, Mon Jun 1 00:12:32 2009 UTC revision 1.11, Mon Sep 7 18:24:51 2009 UTC
# Line 1  Line 1 
1  <?php  <?php
2    
3  /**  /**
4     * @file
5     * Per-bundle storage implementation using the Field Storage API "pre"
6     * hooks and other Field API hooks.
7     */
8    
9    /**
10     * @defgroup field_pbs Field API Per-Bundle Storage
11     * @{
12     * Store multiple Field API data fields in a wide table so that they
13     * can be loaded for a single object in a single query instead of
14     * requiring multiple queries or joins.
15     */
16    /**
17     * @} End of "defgroup field_pbs"
18     */
19    
20    /**
21     * If TRUE, shared field data will be stored both in the per-bundle
22     * table for all of the field's bundles plus the default field storage
23     * engine.  If FALSE, shared fields are only stored in per-bundle
24     * tables, just as non-shared fields are.
25     */
26    define('PBS_DUPLICATE_SHARED_FIELDS', FALSE);
27    
28    /**
29   * Generate a table name for a bundle data table.   * Generate a table name for a bundle data table.
30   *   *
31   * @param $name   * @param $name
# Line 108  function pbs_create_bundle_tables($bundl Line 133  function pbs_create_bundle_tables($bundl
133   * Pre-compute the mapping of bundle table column name to field, item,   * Pre-compute the mapping of bundle table column name to field, item,
134   * and delta.  This must be called any time the set of field instances   * and delta.  This must be called any time the set of field instances
135   * changes in any way.   * changes in any way.
136     *
137     * Suppose that a field F with columns C1 and C2 and with cardinality
138     * 2 has instances for bundles B1 and B2.  For field F, the map will
139     * contain:
140     *
141     * $map = array(
142     *   'B1' => array(
143     *     'F_C1_1' => array('F', 'C1', 1),
144     *     'F_C1_2' => array('F', 'C1', 2),
145     *     'F_C2_1' => array('F', 'C2', 1),
146     *     'F_C2_2' => array('F', 'C2', 2),
147     *     // other entries for B1
148     *   ),
149     *   'B2' => array(
150     *     'F_C1_1' => array('F', 'C1', 1),
151     *     'F_C1_2' => array('F', 'C1', 2),
152     *     'F_C2_1' => array('F', 'C2', 1),
153     *     'F_C2_2' => array('F', 'C2', 2),
154     *     // other entries for B2
155     *   ),
156     * );
157   */   */
158  function pbs_precompute_bundle_map() {  function pbs_precompute_bundle_map() {
159    $map = array();    $map = array();
# Line 270  function pbs_field_attach_pre_load($obj_ Line 316  function pbs_field_attach_pre_load($obj_
316   *   *
317   * When an object's fields are saved, update its bundle table and   * When an object's fields are saved, update its bundle table and
318   * add saved field names as keys to $skip_fields.   * add saved field names as keys to $skip_fields.
319     *
320     * If the field is not shared, tell the field storage not to store it
321     * normally.  If the field is shared, let the field storage system
322     * store it.  The idea here is that we want to allow querying the
323     * field to require querying only a single table.  If we stored a
324     * shared field in multiple bundle tables and prevented per-field
325     * storage, querying would have to query multiple per-bundle tables.
326   */   */
327  function pbs_field_attach_pre_update($obj_type, $object, &$skip_fields) {  function pbs_field_attach_pre_update($obj_type, $object, &$skip_fields) {
328    $etid = _field_sql_storage_etid($obj_type);    $etid = _field_sql_storage_etid($obj_type);
# Line 301  function pbs_field_attach_pre_update($ob Line 354  function pbs_field_attach_pre_update($ob
354        }        }
355    
356        // Whether we left it untouched, emptied it, or stored it, we        // Whether we left it untouched, emptied it, or stored it, we
357        // still handled it.        // still handled it.  Tell the field storage module to skip it,
358        if (count($field['bundles']) == 1) {        // but only if it is a non-shared field.
359          if (!PBS_DUPLICATE_SHARED_FIELDS || count($field['bundles']) == 1) {
360          $all_fields[$field_name] = 1;          $all_fields[$field_name] = 1;
361        }        }
362      }      }
# Line 329  function pbs_field_attach_pre_insert($ob Line 383  function pbs_field_attach_pre_insert($ob
383  }  }
384    
385  /**  /**
386     * Implement hook_field_storage_query().
387     *
388     * To query a single field, query all the per-bundle tables in which
389     * that field appears.  Also, if the field's cardinality is greater
390     * than 1, query all the columnname_N columns for each condition.
391     */
392    function pbs_field_attach_pre_query($field_name, $conditions, $result_format, $age, &$skip_field) {
393      $field = field_info_field($field_name);
394      // We do not store unlimited cardinality fields.
395      if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED) {
396        return;
397      }
398      // We store shared fields, but the base storage module does too, and
399      // it will be able to make only one query instead of the multiple
400      // queries we would need.
401      if (PBS_DUPLICATE_SHARED_FIELDS && count($field['bundles']) == 1) {
402        return;
403      }
404    
405      $load_values = $result_format == FIELD_QUERY_RETURN_VALUES;
406      $load_current = $age == FIELD_LOAD_CURRENT;
407      $field_columns = array_keys($field['columns']);
408      $return = array();
409    
410      // Get the fields to load for each bundle.
411      $bundle_fields = variable_get('pbs_field_map', array());
412    
413      // We know there is only one bundle, but this code will work if we
414      // ever decide not to store shared fields in per-field storage and
415      // instead require multiple queries into per-bundle storage.
416      foreach ($field['bundles'] as $bundle) {
417        $table = $load_current ? pbs_tablename($bundle) : pbs_revision_tablename($bundle);
418    
419        // Build the query.
420        $query = db_select($table, 't');
421        $query->join('field_config_entity_type', 'e', 't.etid = e.etid');
422        $query
423        ->fields('e', array('type'));
424        // TODO ->condition('deleted', 0)
425        // Add fields, depending on the return format.
426        if ($load_values) {
427          $query->fields('t');
428        }
429        else {
430          $query->fields('t', array('entity_id', 'revision_id'));
431        }
432        // Add conditions.
433        foreach ($conditions as $condition) {
434          // A condition is either a (column, value, operator) triple, or a
435          // (column, value) pair with implied operator.
436          @list($column, $value, $operator) = $condition;
437    
438          // TODO
439          if ($column == 'bundle') {
440            continue;
441          }
442    
443          // Translate operator.
444          switch ($operator) {
445            case NULL:
446              $operator = is_array($value) ? 'IN' : '=';
447              break;
448    
449            case 'starts_with':
450              $operator = 'LIKE';
451              $value .= '%';
452              break;
453    
454            case 'contains':
455              $operator = 'LIKE';
456              $value = "%$value%";
457              break;
458          }
459          // Translate field columns into prefixed db columns.
460          if (in_array($column, $field_columns)) {
461            $or = db_or();
462            for ($delta = 0; $delta < $field['cardinality']; ++$delta) {
463              $columnname = _field_sql_storage_columnname($field_name, $column) .'_'. $delta;
464              $or->condition($columnname, $value, $operator);
465            }
466            $query->condition($or);
467          }
468          else {
469            $query->condition($column, $value, $operator);
470          }
471        }
472    
473        $results = $query->execute();
474    
475        // Build results
476        foreach ($results as $row) {
477          // If querying all revisions and the entity type has revisions, we need to
478          // key the results by revision_ids.
479          $entity_type = field_info_fieldable_types($row->type);
480          $id = ($load_current || empty($entity_type['revision key'])) ? $row->entity_id : $row->revision_id;
481    
482          if ($load_values) {
483            $item = array();
484    
485            // Initialize the 'pseudo object' if needed.
486            if (!isset($return[$row->type][$id])) {
487              $return[$row->type][$id] = field_attach_create_stub_object($row->type, array($row->entity_id, $row->revision_id, $bundle));
488            }
489    
490            // TODO: refactor with load
491            foreach ($bundle_fields[$bundle] as $column_name => $tuple) {
492              list($field_name, $item_name, $delta) = $tuple;
493              if (!is_null($row->{$column_name})) {
494                // We must explicitly include $delta in the assignment
495                // below.  Consider a field 'foo' with two columns, e.g. value
496                // and format. Without using $delta, the row containing
497                // foo_value_0 and foo_format_0 would end up stored as
498                // $obj->foo[0]['value'] and $obj->foo[1/*BZZZ!*/]['format'].
499                $return[$row->type][$id]->{$field_name}[$delta][$item_name] = $row->{$column_name};
500              }
501              else if (!isset($return[$row->type][$id]->{$field_name})) {
502                $return[$row->type][$id]->{$field_name} = array();
503              }
504            }
505          }
506          else {
507            // Simply return the list of selected ids.
508            $return[$row->type][$id] = $row->entity_id;
509          }
510        }
511      }
512    
513      $skip_field = TRUE;
514    
515      return $return;
516    }
517    
518    /**
519   * Implementation of hook_field_attach_pre_delete.   * Implementation of hook_field_attach_pre_delete.
520   *   *
521   * When an object's fields are deleted, update its bundle table.   * When an object's fields are deleted, update its bundle table.

Legend:
Removed from v.1.10  
changed lines
  Added in v.1.11

  ViewVC Help
Powered by ViewVC 1.1.2