I've got a content type 'cars' with a field 'passengers' that can have unlimited values. On the cars node form, I can click "add more items" to add one more passenger.

However, I know beforehand how many passengers I want, and I'd like to show that many passenger fields in the node form.

My first approach was hook_form_alter and copying/setting deltas, default_values and *force* the form to show more fields, but I'm sure there is a better way.

If only I could change the cardinalty of a field before the form is rendered... any ideas?

Comments

Begun’s picture

Normally you set the number of fields to show when creating the content type definition. This can either be achieved through the UI or programmatically in the case that you are defining the content type within a module.

The following is the content of a module install file ("namecards_namecard.install") showing how to set cardinality. A value of "-1" represents unlimited. Use a positive value to set a specific number of fields. This is used as part of defining the content type within a module.

Hope this helps.

/**
* Implements hook_install().
*/
function namecards_namecard_install() {
  node_types_rebuild();
  $types = node_type_get_types();

  // Create all the fields we are adding to our content type.
  foreach (_namecards_namecard_installed_fields() as $field) {
    field_create_field($field);
  }

  // Create all the instances for our fields.
  foreach (_namecards_namecard_installed_instances() as $instance) {
    $instance['entity_type'] = 'node';
    $instance['bundle'] = 'namecards_namecard';
    field_create_instance($instance);
  }
}

/**
* Return a structured array defining the fields created by this content type.
*/
function _namecards_namecard_installed_fields() {
  $t = get_t();
  return array(
    'namecards_namecard_phone' => array(
      'field_name' => 'namecards_namecard_phone',
      'label' => $t('Phone'),
      'type' => 'text',
      'cardinality' => -1,
    ),
    'namecards_namecard_fax' => array(
      'field_name' => 'namecards_namecard_fax',
      'label' => $t('Fax'),
      'type' => 'text',
      'cardinality' => -1,
    ),
  );

}

/**
* Return a structured array defining the field instances associated with this content type.
*/
function _namecards_namecard_installed_instances() {
  $t = get_t();
  return array(
    'namecards_namecard_phone' => array(
      'field_name' => 'namecards_namecard_phone',
      'type' => 'text',
      'label' => $t('Phone'),
        'widget' => array(
          'type' => 'text_textfield',
        ),
      'display' => array(
        'example_node_list' => array(
          'label' => $t('Phone'),
          'type' => 'text',
        ),
      ),
      'description' => $t('Phone number format must include country and area codes.  The correct format is: <b>+86 (10) 64460982</b>. Extensions should be separated from the main phone number with a hyphen (e.g. +86 (10) 64460982<b>-123</b>).'),
    ),
    'namecards_namecard_fax' => array(
      'field_name' => 'namecards_namecard_fax',
      'type' => 'text',
      'label' => $t('Fax'),
        'widget' => array(
          'type' => 'text_textfield',
        ),
      'display' => array(
        'example_node_list' => array(
          'label' => $t('Fax'),
          'type' => 'text',
        ),
      ),
      'description' => $t('Fax number format must include country and area codes.  The correct format is: <b>+86 (10) 64220282</b>. Extensions should be separated from the main phone number with a hyphen (e.g. +86 (10) 64220282<b>-123</b>).'),
    ),
  );
}

/**
* Implements hook_uninstall().
*/
function namecards_namecard_uninstall() {
  // Gather all the example content that might have been created while this
  // module was enabled.
  $sql = 'SELECT nid FROM {node} n WHERE n.type = :type';
  $result = db_query($sql, array(':type' => 'namecards_namecard'));
  $nids = array();
  foreach ($result as $row) {
    $nids[] = $row->nid;
  }
  // Delete all the nodes at once
  node_delete_multiple($nids);
  // Loop over each of the fields defined by this module and delete
  // all instances of the field, their data, and the field itself.
  foreach (array_keys(_namecards_namecard_installed_fields()) as $field) {
    field_delete_field($field);
  }
  // Loop over any remaining field instances attached to the namecards_namecard
  // content type (such as the body field) and delete them individually.
  $instances = field_info_instances('node', 'namecards_namecard');
  foreach ($instances as $instance_name => $instance) {
    field_delete_instance($instance);
  }
  // Delete our content type
  node_type_delete('namecards_namecard');
  // Purge all field infromation
  field_purge_batch(1000);
}
odegard’s picture

Thanks for replying. My problem isn't how to set the cardinality in the content type, but to change it to a specific number upon add/edit of a node to node basis.

I should probably set the value to unlimited (-1), but how can I display 7 fields on the node form in one case, and 9 times in a different case?

da_solver’s picture

Hi,
Your solution should be architected in same fashion a relational database works. You add records (instances of a content type), not fields.

For example, a student has zero to many classes. Each student has one "student" record and many related class records.

You relate the student record to the class records by storing a common field value in all records. For example the student's social security number.

While it is true that Drupal 7's field api does support non-relational storage engines, your are more likely than not going to be using a relational database (for storage). Usually MySql or Postgres. In those cases (relational database), you don't let the end user's add columns to the database table structure. Your code let's the user's add records.

Hope that makes sense.

odegard’s picture

Thanks for replying! I am in fact utilizing a many-to-many relationship in this case, but I've cut down on the details pertaining to this use case.

The database is taken care of by Drupal. I'm not forcing Drupal to do anything weird. You can set the number of fields in the content type from 1 to 10 and unlimited. When set to unlimited, the user can add as many as they like manually by clicking a button "One more item" or something like that. What I want to do is "pre-click" that button X times when the form loads.

To continue my car analogy. If I *know* the user want to create a new content type car type SUV, I can load 7 extra passenger fields. If I *know* the car is a ... ehm.. Morris Mini or something very small, I can load 2 extra fields.

da_solver’s picture

Hi,
Your statement:

but how can I display 7 fields on the node form in one case, and 9 times in a different case?

. You are describing a content type (the logical equivalent of a record) with variable number of fields? That's not how it works.

  • The content type definition dialogs are intended for a system administrator (not visitors to your site).
  • A cardinality of unlimited does not mean variable per node instance. That's the issue.

Why not just implement the classic solution. 2 related content types. The logical equivalent of 2 related records (one to many).

Perhaps you can elaborate. Do you mean the system administrator defines a content type with 20 "class" fields (note 20 is static across all instances of that content type, not variable). What you could do is vary the display? My opinion that's a questionable approach at best. Now each instance of the content type (node) has to process 20 "class" fields. In the classic approach, the system only carries the number of "classes" as needed. Thus, I recommend re-architect to the standard solution.

Perhaps you can elaborate:)

odegard’s picture

Thanks for your time, I appreciate your effort.

A content type describes a table (or rather the other way around). Each record is a row, each field is a column as defined in the content type. I think we agree on this. The node table isn't really like this, but that's the theory.

This is also the case with any field you make. Each field get its own table. This is the actual case when you make a new field (remember this is Drupal 7).

When you go to field settings on, say, a text field, you can choose "Number of values" from a drop down list. One of those options is "unlimited".

Ok, now you add a node. You want to use the "unlimited" text field 3 times. You click on the "Add one more" two times to show three fields, fill in your info and hit save.

What happens is that the three fields are all saved in the fields table with different deltas.

When you load a node, it checks what fields are attached, the joins all field values from that field table on the nid of the node.

This is my understanding of how things work. It's not that I try to add more columns in a table. I'm not writing any SQL code, because everything is taken care of behind the scenes.

Are you with me? Is there something I've misunderstood?

ifux’s picture

Can't believe that it's not possible to change the cardinality of the field of a content-type programatically?!?!?

SameerJ’s picture

I am a total novice at D7, but this is a thought - suppose you want to "pre-click" the "add one more field" button 3 times. Can you create 4 separate text fields, with 3 of them having a cardinality of 1, and the 4th one having an unlimited cardinality?

jordiserra’s picture

in /modules/field_ui/field_ui.admin.inc there's the following code:

  $form['field']['cardinality'] = array(
    '#type' => 'select',
    '#title' => t('Number of values'),
    '#options' => array(FIELD_CARDINALITY_UNLIMITED => t('Unlimited')) + drupal_map_assoc(range(1, 10)),
    '#default_value' => $field['cardinality'],
    '#description' => $description,
  );

There you can see how the limit of 10 is setted.

So I used this function in my custom module to change field cardinality:

<?php
function YOUR_MODULE_form_alter(&$form, &$form_state, $form_id) {
	
	if($form_id=="field_ui_field_edit_form"){
		$form['field']['cardinality']['#options'] = array(FIELD_CARDINALITY_UNLIMITED => t('Unlimited')) + drupal_map_assoc(range(1, 30));	
	}
}
?>

I think it's really easy to change with a form, and if I have time I'm planning to do a very simple module to allow change this value in admin section.

Hope that helps!!

griz’s picture

I've been pondering the same problem and just found this: http://drupal.org/node/1592814
Maybe it's the widget you need to alter, not the form?

StephenRobinson’s picture

Ha Ha I came up with a much simpler option so I could set one image field as many in one content type and limit it to one on my other content types:

function mymodule_form_alter(&$form, &$form_state, $form_id) {
  if(isset($form['#node_edit_form'])){
    if( $form_id=='mycontenttype1_node_form' ){
      unset($form['field_image']['und'][1]);
    }
  }
}
blainelang’s picture

I needed to only allow a user with specific permissions to upload more then 1 image. I found that you also had to remove the theme attribute for the image field and now there is no add another option when adding new content.

unset($form['file_ad_item_images'][LANGUAGE_NONE][0]['#theme']);

Focusing on Business Applications but heck we do anything Drupal

ropic’s picture

I have a content type "animal", content administrator will save animals, each animal has to be evaluated (1-5) on different aspects from a taxonomy term.

This is totally posible creating new content type relating each animal with taxonomy term and its value but the challenge is having the edit form for animal having all the taxonomy terms with the input text field for value (1-5).

I'm trying with field collection and i want to alter the form to repeat rows as many taxonomy terms i have showing only one checkbox according to delta value (checked and hidden).

varghese’s picture

db_query("UPDATE field_config SET cardinality = 5 WHERE field_name = 'FIELD_NAME');
field_cache_clear();