function chado_add_node_form_relationships

2.x tripal_core.chado_nodes.relationships.api.inc chado_add_node_form_relationships(&$form, &$form_state, $details)

Provides a form for adding to BASE_relationship and relationship tables

Parameters

$form: The Drupal form array into which the relationship elements will be added

$form_state: The corresponding form_state array for the form

$details: An array defining details needed by this form. Required Keys are:

  • relationship_table: the name of the relationship table (ie: feature_relationship)
  • base_table: the name of the base table (ie: feature)
  • base_foreign_key: the name of the foreign key in the relationship table field linking this table to the non-relationship table (ie: feature_id)
  • base_key_value: the value of the base_foreign_key for the current form (ie: 999 if the feature_id=999)
  • nodetype: the non-translated singular title of this node type

One of the following:

  • cv_id: the id of the ontology to supply terms for the type dropdown
  • cv_name: the name of the ontology to supply terms for the type dropdown

Optional keys include:

  • fieldset_title: the non-translated title for this fieldset
  • additional_instructions: a non-translated string providing additional instructions
  • nodetype_plural: the non-translated plural title of this node type
  • select_options: must be an array where the [key] is a valid cvterm_id and the [value] is the human-readable name of the option. This is generated from the cv_name/id by default
  • base_name_field: the field in your base table you want to be used as the name of the record
  • subject_field_name: the name of the subject field in your relationship table (default: subject_id)
  • object_field_name: the name of the object field in your relationship table (default: object_id)

Related topics

7 calls to chado_add_node_form_relationships()
chado_contact_form in tripal_contact/includes/tripal_contact.chado_node.inc
Implementation of hook_form().
chado_example_form in tripal_example/includes/tripal_example.chado_node.inc
Implementation of hook_form()
chado_feature_form in tripal_feature/includes/tripal_feature.chado_node.inc
Implementation of hook_form().
chado_node_relationships_form in tripal_core/api/tripal_core.DEPRECATED.api.inc
chado_project_form in tripal_project/includes/tripal_project.chado_node.inc
Implementation of hook_form().

... See full list

1 string reference to 'chado_add_node_form_relationships'

File

tripal_core/api/tripal_core.chado_nodes.relationships.api.inc, line 127
API to manage the chado _relationship table for various Tripal Node Types

Code

function chado_add_node_form_relationships(&$form, &$form_state, $details) {

  // make sure the relationship table exists before proceeding.
  if (!chado_table_exists($details['relationship_table'])) {
    drupal_set_message("Cannot add relationship elements to the form. The relationship table, '" .
      $details['relationship_table'] . "', does not exists", "error");
    tripal_report_error('tcrel_form', TRIPAL_ERROR, "Cannot add relationship elements to the form.
      The relationship table, '%name', cannot be found.", array('%name' => $details['relationship_table']));
    return;
  }

  // make sure the specified cv exists
  if (isset($details['cv_name'])) {
    // make sure the cv_name is real
    $result = chado_select_record('cv', array('cv_id'), array('name' => $details['cv_name']));
    if (count($result) == 0) {
      drupal_set_message("Cannot add relationship elements to the form. The CV name, '" .
        $details['cv_name'] . "', does not exists", "error");
      tripal_report_error('tcrel_form', TRIPAL_ERROR, "Cannot add relationship elements to the form.
        The CV named, '%name', cannot be found.", array('%name' => $details['cv_name']));
      return;
    }
    // add the cv_id option to the details array
    $details['cv_id'] = $result[0]->cv_id;
  }
  elseif (isset($details['cv_id'])) {
    // make sure the cv_id is real
    $result = chado_select_record('cv', array('name'), array('cv_id' => $details['cv_id']));
    if (count($result) == 0) {
      drupal_set_message("Cannot add relationship elements to the form. The CV ID, '" .
        $details['cv_id'] . "', does not exist", "error");
      tripal_report_error('tcrel_form', TRIPAL_ERROR, "Cannot add relationship elements
        to the form. The CV ID, '%id', cannot be found.", array('%id' => $details['cv_id']));
      return;
    }
    // add the cv_name option to the details array
    $details['cv_name'] = $result[0]->name;
  }
  else {

    // If we didn't get given a cv identifier, then try retrieving the default one
    // using the new cv defaults api
    $default_cv = tripal_get_default_cv($details['relationship_table'], 'type_id');
    if (!empty($default_cv)) {
      $details['cv_id'] = $default_cv->cv_id;
      $details['cv_name'] = $default_cv->name;
    }
    else {

      $default_form_link = l('vocabulary defaults configuration page', 
      'admin/tripal/chado/tripal_cv/defaults', 
      array('attributes' => array('target' => '_blank')));

      $table = ucwords(str_replace('_', ' ', $details['base_table']));
      $message = "There is not a default vocabulary set for $table Releationship Types. Please set one using the  $default_form_link.";

      tripal_set_message($message, TRIPAL_WARNING);
      tripal_report_error('tcrel_form', TRIPAL_ERROR, "Please provide either a
        'cv_name' or 'cv_id' as an option for adding relationship to the form", array());

      return;
    }

  }

  // Set Defaults for optional fields
  $details['fieldset_title'] = (isset($details['fieldset_title'])) ? $details['fieldset_title'] : 'Relationships';
  $details['additional_instructions'] = (isset($details['additional_instructions'])) ? $details['additional_instructions'] : '';
  $details['nodetype_plural'] = (isset($details['nodetype_plural'])) ? $details['nodetype_plural'] : $details['nodetype'] . 's';
  $details['base_name_field'] = (isset($details['base_name_field'])) ? $details['base_name_field'] : 'uniquename';
  $details['subject_field_name'] = (isset($details['subject_field_name'])) ? $details['subject_field_name'] : 'subject_id';
  $details['object_field_name'] = (isset($details['object_field_name'])) ? $details['object_field_name'] : 'object_id';
  $details['form_element_name'] = (isset($details['form_element_name'])) ? $details['form_element_name'] : $details['base_name_field'];

  // Some relationship tables don't have a rank
  // thus we need to first check this table has a rank before trying to set it
  $table_schema = chado_get_schema($details['relationship_table']);
  $details['table_has_rank'] = (isset($table_schema['fields']['rank'])) ? TRUE : FALSE;

  // Get Relationship Types for the Select List
  if (isset($details['select_options'])) {
    $type_options = $details['select_options'];
  }
  else {
    if (isset($details['cv_name'])) {
      $type_options = array();
      $type_options[] = 'Select a Type';
      $sql = "
        SELECT DISTINCT CVT.cvterm_id, CVT.name, CVT.definition, CV.cv_id as cv_id
        FROM  {cvterm} CVT
          INNER JOIN {cv} CV ON CVT.cv_id = CV.cv_id
        WHERE
          CV.name = :cv_name AND
          NOT CVT.is_obsolete = 1
        ORDER BY CVT.name ASC
      ";
      $cvterms = chado_query($sql, array(':cv_name' => $details['cv_name']));
      while ($term = $cvterms->fetchObject()) {
        $type_options[$term->cvterm_id] = $term->name;
        $details['cv_id'] = $term->cv_id;
      }
    }
    elseif (isset($details['cv_id'])) {
      $type_options = array();
      $type_options[] = 'Select a Type';
      $sql = "
        SELECT DISTINCT CVT.cvterm_id, CVT.name, CVT.definition, CV.name AS cv_name
        FROM  {cvterm} CVT
          INNER JOIN {cv} CV ON CVT.cv_id = CV.cv_id
        WHERE
          CV.cv_id = :cv_id AND
          NOT CVT.is_obsolete = 1
        ORDER BY CVT.name ASC
      ";
      $cvterms = chado_query($sql, array(':cv_id' => $details['cv_id']));
      while ($term = $cvterms->fetchObject()) {
        $type_options[$term->cvterm_id] = $term->name;
        $details['cv_name'] = $term->cv_name;
      }
    }
  }

  // Tell tripal administrators how to add terms to the relationship types drop down.
  if (empty($type_options)) {
    $tripal_message = tripal_set_message(
    t('There are currently no relationship types! To add additional relationship types to the drop
        down list, you need to <a href="@cvtermlink">add a controlled vocabulary term</a>
        to the %cv_name controlled vocabulary.', 
    array(
      '%cv_name' => $details['cv_name'],
      '@cvtermlink' => url('admin/tripal/chado/tripal_cv/cv/' . $details['cv_id'] . '/cvterm/add')
    )
    ), 
    TRIPAL_WARNING, 
    array('return_html' => TRUE)
    );
  }
  else {
    $tripal_message = tripal_set_message(
    t('To add additional relationship types to the drop down list, you need to <a href="@cvtermlink">add
        a controlled vocabulary term</a> to the %cv_name controlled vocabulary.', 
    array(
      '%cv_name' => $details['cv_name'],
      '@cvtermlink' => url('admin/tripal/chado/tripal_cv/cv/' . $details['cv_id'] . '/cvterm/add')
    )
    ), 
    TRIPAL_INFO, 
    array('return_html' => TRUE)
    );
  }

  // Group all of the chado node api fieldsets into vertical tabs.
  $form['chado_node_api'] = array(
    '#type' => 'vertical_tabs',
    '#attached' => array(
      'css' => array(
        'chado-node-api' => drupal_get_path('module', 'tripal_core') . '/theme/css/chado_node_api.css',
      ),
    ),
  );

  $form['relationships'] = array(
    '#type' => 'fieldset',
    '#title' => t($details['fieldset_title']),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
    '#group' => 'chado_node_api',
    '#weight' => 10,
    '#attributes' => array('class' => array('chado-node-api', 'relationships')),
    '#attached' => array(
      'js' => array(
        'chado-node-api-vertical-tabs' => drupal_get_path('module', 'tripal_core') . '/theme/js/chadoNodeApi_updateVerticalTabSummary.js',
      ),
    ),
  );

  $instructions = 'Relationships should be read like a sentence ([subject] [type]
    [object]) in order to determine their direction and thus their meaning. When
    adding a relationship, it is easiest to first select the type of relationship you would
    like to enter and then select whether the current %nodetype is the subject
    or object (based on which "sentence" makes sense). Finally enter the other
    %nodetype in the remaining text box (making sure to select from the
    autocomplete drop-down) before clicking "Add". To remove incorrect relationships, click the
    "Remove" button. Note: you cannot edit previously added relationships
    but instead need to remove and re-add them.';
  $form['relationships']['descrip'] = array(
    '#type' => 'item',
    '#markup' => t('<p><strong>Relate the current %nodetype with others that already exist.</strong></p><p>' . $instructions . $details['additional_instructions'] . '</p>', array('%nodetype' => $details['nodetype'])),
  );

  // this form element is a tree, so that we don't puke all of the values into then node variable
  // it is set as a tree, and keeps them in the $form_state['values']['relationship_table'] heading.
  $form['relationships']['relationship_table'] = array(
    '#type' => 'markup',
    '#tree' => TRUE,
    '#prefix' => '<div id="tripal-generic-edit-relationships-table">',
    '#suffix' => '</div>',
    '#theme' => 'chado_node_relationships_form_table',
    '#weight' => 5
  );

  // Add defaults into form_state to be used elsewhere
  $form['relationships']['relationship_table']['details'] = array(
    '#type' => 'hidden',
    '#value' => serialize($details)
  );

  // We need to provide feedback to the user that changes made
  // are not saved until the node is saved.
  $form['relationships']['relationship_table']['save_warning'] = array(
    '#type' => 'markup',
    '#prefix' => '<div id="relationship-save-warning" class="messages warning" style="display:none;">',
    '#suffix' => '</div>',
    '#markup' => '* The changes to these relationships will not be saved until the "Save" button at the bottom of this form is clicked. <span class="specific-changes"></span>',
    '#attached' => array(
      'js' => array(
        'chado-node-api-unsaved' => drupal_get_path('module', 'tripal_core') . '/theme/js/chadoNodeApi_unsavedNotify.js',
      ),
    ),
  );

  // Add relationships already attached to the node
  //---------------------------------------------
  /* Relationships can come to us in two ways:
   *
   * 1) In the form state in the $form_state['chado_relationships']. Data is in this field
   *    when an AJAX call updates the form state or a validation error.
   *
   * 2) Directly from the database if the record already has _relationships associated.  This
   *    data is only used the first time the form is loaded. On AJAX calls or validation
   *    errors the fields on the form are populated from the $form_state['chado_relationships']
   *    entry.
   */
  if (isset($form_state['chado_relationships'])) {
    $existing_rels = $form_state['chado_relationships'];
  }
  else {
    $existing_rels = chado_query(
    "SELECT
          rel.*,
          rel." . $details['relationship_table'] . "_id as relationship_id,
          rel." . $details['subject_field_name'] . " as subject_id,
          rel." . $details['object_field_name'] . " as object_id,
          base1." . $details['base_name_field'] . " as object_name,
          base2." . $details['base_name_field'] . " as subject_name,
          cvterm.name as type_name
        FROM {" . $details['relationship_table'] . "} rel
          LEFT JOIN {" . $details['base_table'] . "} base1 ON base1." . $details['base_foreign_key'] . " = rel." . $details['object_field_name'] . "
          LEFT JOIN {" . $details['base_table'] . "} base2 ON base2." . $details['base_foreign_key'] . " = rel." . $details['subject_field_name'] . "
          LEFT JOIN {cvterm} cvterm ON cvterm.cvterm_id = rel.type_id
        WHERE rel." . $details['object_field_name'] . " = :base_key_value
            OR rel." . $details['subject_field_name'] . " = :base_key_value", 
    array(':base_key_value' => $details['base_key_value'])
    );
  }

  /* The format of the $existing_rels' array is either:
   *
   * From the chado_relationships array:
   * $form_state['chado_relationships'] = array(
   *   '[type_id]-[_relationship_id]' => array(
   *     'relationship_id' => [the _relationship._relationship_id value OR a temporary value if not yet saved to the database],
   *     'object_id' => [the _relationship.object_id value],
   *     'object_name' => [the base_table.uniquename value linked on base_foreign_key=object_id],
   *     'subject_id' => [the _relationship.subject_id value],
   *     'subject_name' => [the base_table.uniquename value linked on base_foreign_key=subject_id],
   *     'type_id' => [the _relationship.type_id value],
   *     'type_name' => [the cvterm.name value linked on type_id],
   *     'rank' => [the _relationship.rank value OR NULL if not yet saved to the database],
   *   ),
   * );
   *
   * OR
   * Populated from the database:
   * $existing_rels = array(
   *   0 => array(
   *     'relationship_id' => [the _relationship._relationship_id value],
   *     'object_id' => [the _relationship.object_id value],
   *     'object_name' => [the base_table.uniquename value linked on base_foreign_key=object_id],
   *     'subject_id' => [the _relationship.subject_id value],
   *     'subject_name' => [the base_table.uniquename value linked on base_foreign_key=subject_id],
   *     'type_id' => [the _relationship.type_id value],
   *     'type_name' => [the cvterm.name value linked on type_id],
   *     'rank' => [the _relationship.rank value],
   *   ),
   * );
   *
   * NOTE: The main difference is the key
   *
   * Loop on the array elements of the $existing_rels array and add
   * an element to the form for each one.
   */
  $num_relationships = 0;
  foreach ($existing_rels as $relationship) {
    if (array_key_exists($relationship->type_id, $type_options)) {
      $num_relationships++;

      $type_class = str_replace(array(' ', '_'), '-', $relationship->type_name);
      $current_class = 'current-unknown';
      if ($details['base_key_value']) {
        if ($relationship->object_id == $details['base_key_value']) {
          $current_class = 'current-object';
        }
        elseif ($relationship->subject_id == $details['base_key_value']) {
          $current_class = 'current-subject';
        }
      }

      $form['relationships']['relationship_table'][$relationship->type_id]['#type'] = 'markup';
      $form['relationships']['relationship_table'][$relationship->type_id]['#type'] = '';

      $form['relationships']['relationship_table'][$relationship->type_id][$relationship->relationship_id]['#type'] = 'markup';
      $form['relationships']['relationship_table'][$relationship->type_id][$relationship->relationship_id]['#value'] = '';
      $form['relationships']['relationship_table'][$relationship->type_id][$relationship->relationship_id]['#attributes'] = array(
        'class' => array('relationship', 'saved', $type_class, $current_class)
      );

      // Determine whether this relationship is unsaved or not.
      // We can tell this by looking at the relationship_id: if it's not
      // saved yet we will have entered a TEMP###.
      if (preg_match('/^TEMP/', $relationship->relationship_id)) {
        $form['relationships']['relationship_table'][$relationship->type_id][$relationship->relationship_id]['#attributes'] = array(
          'class' => array('relationship', 'unsaved', $type_class, $current_class)
        );
      }

      $form['relationships']['relationship_table'][$relationship->type_id][$relationship->relationship_id]['relationship_id'] = array(
        '#type' => 'markup',
        '#markup' => $relationship->relationship_id
      );

      $form['relationships']['relationship_table'][$relationship->type_id][$relationship->relationship_id]['object_id'] = array(
        '#type' => 'hidden',
        '#value' => $relationship->object_id
      );

      $form['relationships']['relationship_table'][$relationship->type_id][$relationship->relationship_id]['subject_id'] = array(
        '#type' => 'hidden',
        '#value' => $relationship->subject_id
      );

      $form['relationships']['relationship_table'][$relationship->type_id][$relationship->relationship_id]['type_id'] = array(
        '#type' => 'hidden',
        '#value' => $relationship->type_id
      );

      $form['relationships']['relationship_table'][$relationship->type_id][$relationship->relationship_id]['object_name'] = array(
        '#type' => 'markup',
        '#markup' => $relationship->object_name
      );

      $form['relationships']['relationship_table'][$relationship->type_id][$relationship->relationship_id]['type_name'] = array(
        '#type' => 'markup',
        '#markup' => $relationship->type_name
      );

      $form['relationships']['relationship_table'][$relationship->type_id][$relationship->relationship_id]['subject_name'] = array(
        '#type' => 'markup',
        '#markup' => $relationship->subject_name,
        '#prefix' => '<span class="row-unsaved-warning"></span>'
      );

      $form['relationships']['relationship_table'][$relationship->type_id][$relationship->relationship_id]['rank'] = array(
        '#type' => 'markup',
        '#markup' => (isset($relationship->rank)) ? $relationship->rank : NULL
      );

      $form['relationships']['relationship_table'][$relationship->type_id][$relationship->relationship_id]['rel_action'] = array(
        '#type' => 'submit',
        '#value' => t('Remove'),
        '#name' => "relationships_remove-" . $relationship->type_id . '-' . $relationship->relationship_id,
        '#ajax' => array(
          'callback' => 'chado_add_node_form_subtable_ajax_update',
          'wrapper' => 'tripal-generic-edit-relationships-table',
          'effect' => 'fade',
          'method' => 'replace',
          'prevent' => 'click'
        ),
        // When this button is clicked, the form will be validated and submitted.
        // Therefore, we set custom submit and validate functions to override the
        // default node form submit.  In the validate function we validate only the
        // relationship fields and in the submit we remove the indicated relationship
        // from the chado_relationships array. In order to keep validate errors
        // from the node form validate and Drupal required errors for non-relationship fields
        // preventing the user from removing relationships we set the #limit_validation_errors below
        '#validate' => array('chado_add_node_form_subtables_remove_button_validate'),
        '#submit' => array('chado_add_node_form_subtables_remove_button_submit'),
        // Limit the validation of the form upon clicking this button to the relationship_table tree
        // No other fields will be validated (ie: no fields from the main form or any other api
        // added form).
        '#limit_validation_errors' => array(
          array('relationship_table') // Validate all fields within $form_state['values']['relationship_table']
        )
      );
    }
  }

  // Quickly add a hidden field stating how many relationships are currently added.
  $form['relationships']['num_relationships'] = array(
    '#type' => 'hidden',
    '#value' => $num_relationships,
    '#attributes' => array('class' => 'num-relationships')
  );

  // Form elements for adding a new relationship
  //---------------------------------------------
  $form['relationships']['relationship_table']['new']['object_name'] = array(
    '#type' => 'textfield',
    '#autocomplete_path' => 'tripal_ajax/relationship_nodeform/' . $details['base_table'] . '/' . $details['base_name_field'] . '/name_to_id'
  );

  $form['relationships']['relationship_table']['new']['object_is_current'] = array(
    '#type' => 'checkbox',
    '#title' => t('Current ' . $details['nodetype']),
  );

  $form['relationships']['relationship_table']['new']['type_name'] = array(
    '#type' => 'select',
    '#options' => $type_options,
  );

  $form['relationships']['relationship_table']['new']['subject_name'] = array(
    '#type' => 'textfield',
    '#autocomplete_path' => 'tripal_ajax/relationship_nodeform/' . $details['base_table'] . '/' . $details['base_name_field'] . '/name_to_id'
  );

  $form['relationships']['relationship_table']['new']['subject_is_current'] = array(
    '#type' => 'checkbox',
    '#title' => t('Current ' . $details['nodetype']),
  );

  $form['relationships']['relationship_table']['new']['rank'] = array(
    '#type' => 'markup',
    '#markup' => ''
  );

  $form['relationships']['relationship_table']['new']['rel_action'] = array(
    '#type' => 'submit',
    '#value' => t('Add'),
    '#name' => 'relationships-add',
    '#ajax' => array(
      'callback' => 'chado_add_node_form_subtable_ajax_update',
      'wrapper' => 'tripal-generic-edit-relationships-table',
      'effect' => 'fade',
      'method' => 'replace',
    ),
    // When this button is clicked, the form will be validated and submitted.
    // Therefore, we set custom submit and validate functions to override the
    // default node form submit.  In the validate function we validate only the
    // relationship fields and in the submit we add them to the chado_relationships
    // array. In order to keep validate errors from the node form validate and Drupal
    // required errors for non-relationship fields preventing the user from adding relationships we
    // set the #limit_validation_errors below
    '#validate' => array('chado_add_node_form_subtables_add_button_validate'),
    '#submit' => array('chado_add_node_form_subtables_add_button_submit'),
    // Limit the validation of the form upon clicking this button to the relationship_table tree
    // No other fields will be validated (ie: no fields from the main form or any other api
    // added form).
    '#limit_validation_errors' => array(
      array('relationship_table') // Ensure relationship table results are not discarded.
    )
  );

  $form['relationships']['admin_message'] = array(
    '#type' => 'markup',
    '#markup' => $tripal_message,
    '#weight' => 10
  );
}