/**
  * Builds and inserts taxonomy index entries for a given node.
  *
  * The index lists all terms that are related to a given node entity, and is
  * therefore maintained at the entity level.
  *
  * @param $node
  *   The node object.
  *
  * @see taxonomy_build_node_index()
  */
 protected function buildNodeIndex($node)
 {
     // We maintain a denormalized table of term/node relationships, containing
     // only data for current, published nodes.
     $status = NULL;
     if (variable_get('taxonomy_maintain_index_table', TRUE)) {
         // If a node property is not set in the node object when node_save() is
         // called, the old value from $node->original is used.
         if (!empty($node->original)) {
             $status = (int) (!empty($node->status) || !isset($node->status) && !empty($node->original->status));
             $sticky = (int) (!empty($node->sticky) || !isset($node->sticky) && !empty($node->original->sticky));
         } else {
             $status = (int) (!empty($node->status));
             $sticky = (int) (!empty($node->sticky));
         }
     }
     // We only maintain the taxonomy index for published nodes.
     if ($status) {
         // Collect a unique list of all the term IDs from all node fields.
         $tid_all = array();
         foreach (field_info_instances('node', $node->type) as $instance) {
             $field_name = $instance['field_name'];
             $field = field_info_field($field_name);
             if (!empty($field['settings']['target_type']) && $field['settings']['target_type'] == 'taxonomy_term' && $field['storage']['type'] == 'field_sql_storage') {
                 // If a field value is not set in the node object when node_save() is
                 // called, the old value from $node->original is used.
                 if (isset($node->{$field_name})) {
                     $items = $node->{$field_name};
                 } elseif (isset($node->original->{$field_name})) {
                     $items = $node->original->{$field_name};
                 } else {
                     continue;
                 }
                 foreach (field_available_languages('node', $field) as $langcode) {
                     if (!empty($items[$langcode])) {
                         foreach ($items[$langcode] as $item) {
                             $tid_all[$item['target_id']] = $item['target_id'];
                         }
                     }
                 }
             }
             // Re-calculate the terms added in taxonomy_build_node_index() so
             // we can optimize database queries.
             $original_tid_all = array();
             if ($field['module'] == 'taxonomy' && $field['storage']['type'] == 'field_sql_storage') {
                 // If a field value is not set in the node object when node_save() is
                 // called, the old value from $node->original is used.
                 if (isset($node->{$field_name})) {
                     $items = $node->{$field_name};
                 } elseif (isset($node->original->{$field_name})) {
                     $items = $node->original->{$field_name};
                 } else {
                     continue;
                 }
                 foreach (field_available_languages('node', $field) as $langcode) {
                     if (!empty($items[$langcode])) {
                         foreach ($items[$langcode] as $item) {
                             $original_tid_all[$item['tid']] = $item['tid'];
                         }
                     }
                 }
             }
         }
         // Insert index entries for all the node's terms, that were not
         // already inserted in taxonomy_build_node_index().
         $tid_all = array_diff($tid_all, $original_tid_all);
         // Insert index entries for all the node's terms.
         if (!empty($tid_all)) {
             $query = db_insert('taxonomy_index')->fields(array('nid', 'tid', 'sticky', 'created'));
             foreach ($tid_all as $tid) {
                 $query->values(array('nid' => $node->nid, 'tid' => $tid, 'sticky' => $sticky, 'created' => $node->created));
             }
             $query->execute();
         }
     }
 }
/**
 * Write field data for an entity.
 *
 * This hook is invoked from field_attach_insert() and field_attach_update(),
 * to ask the field storage module to save field data.
 *
 * @param $entity_type
 *   The entity type of entity, such as 'node' or 'user'.
 * @param $entity
 *   The entity on which to operate.
 * @param $op
 *   FIELD_STORAGE_UPDATE when updating an existing entity,
 *   FIELD_STORAGE_INSERT when inserting a new entity.
 * @param $fields
 *   An array listing the fields to be written. The keys and values of the
 *   array are field IDs.
 */
function hook_field_storage_write($entity_type, $entity, $op, $fields)
{
    list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
    if (!isset($vid)) {
        $vid = $id;
    }
    foreach ($fields as $field_id) {
        $field = field_info_field_by_id($field_id);
        $field_name = $field['field_name'];
        $table_name = _field_sql_storage_tablename($field);
        $revision_name = _field_sql_storage_revision_tablename($field);
        $all_languages = field_available_languages($entity_type, $field);
        $field_languages = array_intersect($all_languages, array_keys((array) $entity->{$field_name}));
        // Delete and insert, rather than update, in case a value was added.
        if ($op == FIELD_STORAGE_UPDATE) {
            // Delete languages present in the incoming $entity->$field_name.
            // Delete all languages if $entity->$field_name is empty.
            $languages = !empty($entity->{$field_name}) ? $field_languages : $all_languages;
            if ($languages) {
                db_delete($table_name)->condition('entity_type', $entity_type)->condition('entity_id', $id)->condition('language', $languages, 'IN')->execute();
                db_delete($revision_name)->condition('entity_type', $entity_type)->condition('entity_id', $id)->condition('revision_id', $vid)->condition('language', $languages, 'IN')->execute();
            }
        }
        // Prepare the multi-insert query.
        $do_insert = FALSE;
        $columns = array('entity_type', 'entity_id', 'revision_id', 'bundle', 'delta', 'language');
        foreach ($field['columns'] as $column => $attributes) {
            $columns[] = _field_sql_storage_columnname($field_name, $column);
        }
        $query = db_insert($table_name)->fields($columns);
        $revision_query = db_insert($revision_name)->fields($columns);
        foreach ($field_languages as $langcode) {
            $items = (array) $entity->{$field_name}[$langcode];
            $delta_count = 0;
            foreach ($items as $delta => $item) {
                // We now know we have someting to insert.
                $do_insert = TRUE;
                $record = array('entity_type' => $entity_type, 'entity_id' => $id, 'revision_id' => $vid, 'bundle' => $bundle, 'delta' => $delta, 'language' => $langcode);
                foreach ($field['columns'] as $column => $attributes) {
                    $record[_field_sql_storage_columnname($field_name, $column)] = isset($item[$column]) ? $item[$column] : NULL;
                }
                $query->values($record);
                if (isset($vid)) {
                    $revision_query->values($record);
                }
                if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED && ++$delta_count == $field['cardinality']) {
                    break;
                }
            }
        }
        // Execute the query if we have values to insert.
        if ($do_insert) {
            $query->execute();
            $revision_query->execute();
        }
    }
}
 /**
  * Given a relationship, gets the stub entity for the source.
  *
  * @param array $relationship The relationship array.
  *
  * @return bool|mixed Either the stub entity or false on error.
  */
 protected function getSourceStubEntity(array $relationship)
 {
     $field_name = $relationship['field_name'];
     $source_entity_ids = entity_get_id_by_uuid($relationship['source_type'], array($relationship['source_uuid']));
     $source_entity_id = count($source_entity_ids) > 0 ? reset($source_entity_ids) : null;
     $source_entity_vids = entity_get_id_by_uuid($relationship['source_type'], array($relationship['source_vuuid']), true);
     $source_entity_vid = count($source_entity_vids) > 0 ? reset($source_entity_vids) : false;
     if (!$source_entity_id) {
         return false;
     }
     // Get the bundle for the source entity.
     $source_entity_bundle = Entity::getBundleFromID($relationship['source_type'], $source_entity_id);
     if (!$source_entity_bundle) {
         return false;
     }
     // Get the stub entity for the source.
     $source_stub = Entity::getStub($relationship['source_type'], $source_entity_id, $source_entity_bundle, $source_entity_vid);
     // Load the existing field onto the entity.
     $field_instance_info = field_info_instance($relationship['source_type'], $field_name, $source_entity_bundle);
     if ($field_instance_info === false) {
         return false;
     }
     try {
         // First, load the revisions.
         field_attach_load_revision($relationship['source_type'], array($source_entity_id => $source_stub), array('field_id' => $field_instance_info['field_id']));
         // Second, get the original deltas for the revision.
         $deltas = db_select('field_revision_' . $field_name, 'fr')->fields('fr', array('language', 'delta'))->condition($source_entity_vid ? 'revision_id' : 'entity_id', $source_entity_vid ? $source_entity_vid : $source_entity_id)->condition('language', field_available_languages($relationship['source_type'], $field_instance_info), 'IN')->orderBy('delta')->execute();
         // Assign the deltas to their languages.
         $deltas_languages = array();
         foreach ($deltas as $delta) {
             if (!array_key_exists($delta->language, $deltas_languages)) {
                 $deltas_languages[$delta->language] = array();
             }
             $deltas_languages[$delta->language][] = $delta->delta;
         }
         // Reset the deltas to their correct orders.
         $field_value =& $source_stub->{$field_name};
         foreach ($deltas_languages as $language => $deltas) {
             $new_value = array();
             for ($i = 0; $i < count($field_value[$language]); $i++) {
                 $new_value[$deltas[$i]] = $field_value[$language][$i];
             }
             $field_value[$language] = $new_value;
         }
     } catch (\Exception $ex) {
         // If an exception was thrown, that probably means the field doesn't
         // exist, so we should return false.
         return false;
     }
     return $source_stub;
 }