2

I have a content-type seasonal_prices with field-collection containing an entity reference field (referencing content-type room) and a number field; the field-collection is multi-value with unlimited cardinality.

When adding new content of type seasonal_prices, I want the form to be pre-populated with an instance of the field-collection for every existing content of type room.

An example: If there are 5 nodes of type room, the create-form of seasonal_prices should show me 5 instances of the field-collection, where each reference-field is pre-populated with its reference to room, so all I have to do is fill out the number-field.

UPDATE: Here is the code i finally used in a custom module, hope this is helpful:

<?php

function seasonal_prices_form_field_ui_field_edit_form_alter(&$form, &$form_state, $form_id) {
  // Increase the max cardinality to 20
  $range = drupal_map_assoc(range(1, count(get_roomtype_ids())));
  $new_options =  array(FIELD_CARDINALITY_UNLIMITED => t('Unlimited')) + $range;
  $form['field']['cardinality']['#options'] = $new_options;
}

function seasonal_prices_form_alter(&$form, $form_state, $form_id) {
  // update cardinality in field settings
  $field_name = 'field_prices_and_roomtype';
  $field = field_info_field($field_name);
  $field['cardinality'] = count(get_roomtype_ids());
  // field_update_field() throws an exception on failure
  try {
    field_update_field($field);
  }
  catch (Exception $e) {
    drupal_set_message(t('Could not update cardinality of field.'), 'error');
  }
  // generate fieldcollection instances 
  if($form_id == 'seasonal_prices_node_form') {
    $items_count = count(get_roomtype_ids());
    $form_theme = $form[$field_name][LANGUAGE_NONE]['#theme'];
    // remove delete and add more buttons
    $form[$field_name][LANGUAGE_NONE][0]['remove_button']['#access'] = FALSE;
    $form[$field_name][LANGUAGE_NONE]['add_more']['#access'] = FALSE;
    $items = &$form_state['field'][$field_name][LANGUAGE_NONE];
    // generate required number of fields collection
    if ($items_count > 1 and $items['items_count'] != $items_count) {
      $items['items_count'] = $items_count;
      $items['field']['cardinality'] = $items_count;
      $form[$field_name][LANGUAGE_NONE] = field_multiple_value_form($items['field'], $items['instance'], LANGUAGE_NONE, array(), $form, $form_state);
      // reset theme function, as field_multiple_value_form hijacks it
      $form[$field_name][LANGUAGE_NONE]['#theme'] = $form_theme;
    }
    // fill generated instances with default values and disable access
    for ($delta = 0; $delta < ($items_count); $delta++) {
      $form['field_prices_and_roomtype'][LANGUAGE_NONE][$delta]['field_ref_roomtype'][LANGUAGE_NONE]['#default_value'] = get_roomtype_ids()[$delta];
      $form['field_prices_and_roomtype'][LANGUAGE_NONE][$delta]['field_ref_roomtype']['#access'] = FALSE;
      // designate Fieldcollection Items correctly
      $form['field_prices_and_roomtype'][LANGUAGE_NONE][$delta]['#prefix'] = '<div class="roomtype-form-label"><b>' . get_node_title(get_roomtype_ids()[$delta]) . '</b>';
      $form['field_prices_and_roomtype'][LANGUAGE_NONE][$delta]['#suffix'] = '</div>';
    }

  }
}

// Helper function to get the node title without loading the whole node object.
function get_node_title($nid) {
return db_query('SELECT title FROM {node} WHERE nid = :nid', array(':nid' => $nid))->fetchField();
}

// Helper function
function get_roomtype_ids() {
    $query = new EntityFieldQuery();
    $query
    ->entityCondition('entity_type', 'node')
    ->entityCondition('bundle', 'roomtype')
    ->propertyCondition('status', 1)
    ->propertyOrderBy('created', 'ASC');
    $result = $query->execute();
    return array_keys($result['node']);
}
3
  • Can you post your code for the seasonal_prices entity form? It seems that bulk of the solution involves setting the field collection's entityreference field before calling field_attach_form().
    – angheloko
    Commented Sep 19, 2014 at 9:55
  • At the moment there's no code, because the whole process is unclear to me.
    – Madam Adam
    Commented Sep 19, 2014 at 11:27
  • I had a very similar requirement. This is how I solved it: drupal.stackexchange.com/a/84505/10176
    – Elin Y.
    Commented Sep 22, 2014 at 12:01

2 Answers 2

1
+50

You will need to create a custom module that alters the node creation form before it rendered using hook_form_FORM_ID_alter().

You want to edit the form_state which stores the information on how a form is filled out.

It should look something like this:

<?php

/**
 * Module to prepopulate season price list with rooms
 */

/**
 * Implements hook_form_FORM_ID_alter(&$form, &$form_state)
 */

function mymodule_form_seasonal_prices_node_form_alter(&$form, &$form_state){

    // Load all of the room node ids into an array
    $node_type = "room";
    $result = db_query("SELECT nid FROM node WHERE type = '%s' ", $node_type);
    $nids = array();
  while ($obj = db_fetch_object($result)) {
    $nids[] = $obj->nid;
  }

  // Set the form state to include each room
  foreach($nids as $nid){
    $form_state->field_collection_you_want_to_edit[] = $nid
  }
}

You will need to do a bit of sleuthing to figure out what to put in the last line as it is specific to your build, and I don't know whether you need to set it to the $nid or node_load($nid) or something else. But this should give you a framework to go on with.

If you haven't come across the Devel module yet, install it, and use dpm($form_State); to look at the structure of the variable so that you know what to change.

Finally, make sure you change the name of the function from 'mymodule' to whatever name you have given your module, make sure the machine names for your content types and form ids are correct (I just guessed them), and clear the cache so that Drupal picks up the hook.

0
1

Darvanen suggest a very good solution.

You need a form_alter to pre-populate your room collection field But while fetching all rooms from database, I will recommend if you will use db_select instead of db_query in form_alter as it is better way to interact with drupal database.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.