// $node->nid must be valid at this point
// $node->field_my_field_collection is the field collection field
$field_collection_item = entity_create('field_collection_item', array('field_name' => 'field_my_field_collection'));
$field_collection_item->setHostEntity('node', $node);
$field_collection_item->field_a_field_collection_field[LANGUAGE_NONE][]['value'] = ...;
...
$field_collection_item->save();
$node->field_my_field_collection[LANGUAGE_NONE][]['value'] = $field_collection_item->item_id;
Thought I would post my solution for programmatically creating a field collection, along with an attached field, during a module install. The example is specific to my use, but easily modified to add the field types you need. Of course, if there is an easier way please feel free to add your thoughts. This works with the current 7.x-1.x-dev branch.
@rbruhn: Sorry to reactivate an old thread. I was curious if you knew of any issues with field collection creation in the traditional field array method as you have described when in a batch operation. The following throws a HTML general error and has had me stumped for the past few days.
<?php
// Create Both the Aircraft Utility Field Collection in the Commerce Order Entity
function batch_aircraft_commerce_collection2($aid, $aml, $nodeid, $max, $operation_details, &$context)
{
$id = $aid;
$name = 'field_' . $aid;
$t = get_t();
$collected = array(
array(
'field' => array(
'field_name' => $name,
'label' => $t('test'),
'cardinality' => -1,
'type' => 'field_collection',
),
'instance' => array(
'field_name' => $name,
'entity_type' => 'commerce_order',
'bundle' => 'commerce_order',
'label' => $t('test'),
'description' => '',
'widget' => array('type' => 'field_collection_embed'),
'required' => 1,
),
),
);
// Loop through fields array and create field and instance
foreach ($collected as $field) {
// Check if field already exists
if (!field_info_field($field['field']['field_name'])) {
field_create_field($field['field']);
}
// Check if instance exists
if (!field_info_instance($field['instance']['entity_type'], $field['instance']['field_name'], $field['instance']['bundle'])) {
field_create_instance($field['instance']);
}
}
}
?>
@JMOmandown - Well, I took your code above and changed a few things so I could run it in a test.php file using drush on my system. The code you posted is simply attempting to create Field Collections, and not attaching fields to those collections. So I'm assuming that is what you wanted. In the code below:
1) I changed the entity_type and bundle because I do not have commerce installed.
2) I refactored the code since you are only creating one Field Collection at a time when calling the function. So there is really no need to use a foreach loop if you don't have to.
3) As well, since I'm merely testing the function, I removed the other unneeded variables being passed to the function so it would not throw an error while running the test script. I know you are doing this in a batch so yours is different, but perhaps the below will help you find out what is wrong.
Anyway, the below creates the Field Collections for me. I see them added in my UI, as well as being present on the Article node form.
Hope this helps.
// Create Both the Aircraft Utility Field Collection in the Commerce Order Entity
function batch_aircraft_commerce_collection2($aid) {
$id = $aid;
$name = 'field_' . $aid;
$t = get_t();
$collection = array();
$collection['field'] = array(
'field_name' => $name,
'label' => $t('test'),
'cardinality' => -1,
'type' => 'field_collection',
);
$collection['instance'] = array(
'field_name' => $name,
'entity_type' => 'node',
'bundle' => 'article',
'label' => $t('test'),
'description' => '',
'widget' => array('type' => 'field_collection_embed'),
'required' => 1,
);
if (!field_info_field($collection['field']['field_name'])) {
field_create_field($collection['field']);
}
// Check if instance exists
if (!field_info_instance($collection['instance']['entity_type'], $collection['instance']['field_name'], $collection['instance']['bundle'])) {
field_create_instance($collection['instance']);
}
}
$test = array(123,1234,12345,123456);
foreach ($test as $aid) {
batch_aircraft_commerce_collection2($aid);
}
@rbruhn: Thanks for the quick response. As I feared it is still not functional which is so weird to me. If I comment out the function above (one of many operations in the batch) then the batch runs smoothly. With it enabled I get and error with no repsonse text:
<?php
An AJAX HTTP error occurred. HTTP Result Code: 500 Debugging information follows. Path: /batch?id=483&op=do StatusText: Service unavailable (with message) ResponseText:
?>
So weird, but I thank you for your quick response and attempt.
@JMOmandown: Yes, I hate those messages that don't really give you anything to work with. I think they are trying to fix some of those in D8. When I run into your problem, I use to place some code to spit out to a file or something so I can at least see at what point it's failing. For example, writing to file when it creates the field or instance and the name of the field. From there, following to each function called so I can get the exact point. I could usually figure out what is going wrong.
Might be possible to use drush to run your batch (http://drupal.org/node/873132) with debug enabled. Since I've started using drush, errors have been a lot easier to ascertain.
I just had an issue where trying to add a new field collections to the 'user' entity would delete any existing items. The solution was to directly load the user entity and not depend on the global $user as that one doesn't have the field collection information.
$thisUser = entity_load('user', array($user->uid));
$thisUser = $thisUser[$user->uid]; //make thisUser the user object, not an array of user objects
$newFieldCollectionItem = array();
$newFieldCollectionItem['field_name'] = 'field_download';
$newFieldCollectionItem['field_download_filename'][LANGUAGE_NONE][0]['value'] = $src;
$newFieldCollectionItem['field_download_date'][LANGUAGE_NONE][0]['value'] = time();
$newFieldCollection = entity_create('field_collection_item', $newFieldCollectionItem);
$newFieldCollection->setHostEntity('user', $thisUser);
$newFieldCollection->save();
I want to create it on an easier leavel for testing purposes. My goal is to create a taxonomy reference dropdown and a textfield.
I manage to reproduce the field_collection type. Hower, I can't reproduce any field collections item fields. Let's say I want a textfield in my field collection.
creating an ordinary textfield field collection item field, this would make sense to me (but it doesn't work). Can someone tell me what I do wrong? Instead of appearing inside my field collection it appears as a field inside my node which contains my field collection field
Here is the code for multiple values inside the field collection entity:
// Create a node with submission
$node = new stdClass();
$node->title = $title;
$node->type = "article";
node_object_prepare($node); // Sets some defaults. Invokes hook_prepare() and hook_node_prepare().
$node->language = LANGUAGE_NONE; // Or e.g. 'en' if locale is enabled
//$node->uid = $user->uid; // you can set the author for a node if you want
$node->status = 1; //(1 or 0): published or not
$node->promote = 0; //(1 or 0): promoted to front page
$node->comment = 0; // 0 = comments disabled, 1 = read only, 2 = read/write
$node = node_submit($node); // Prepare node for saving
// field collection items.
for ($i = 0; $i < $total_values; $i++) {
$field_collection_item = entity_create('field_collection_item', array('field_name' => 'field_answers'));
$field_collection_item->setHostEntity('node', $node);
$field_collection_item->field_question_body[LANGUAGE_NONE][] = array('value' => $questions[$i]);
$field_collection_item->field_answer_body[LANGUAGE_NONE][] = array('value' => $answers[0]);
$field_collection_item->save();
}
node_save($node);
// Create a node with submission
$node = new stdClass();
$node->title = $title;
$node->type = "article";
node_object_prepare($node); // Sets some defaults. Invokes hook_prepare() and hook_node_prepare().
$node->language = LANGUAGE_NONE; // Or e.g. 'en' if locale is enabled
//$node->uid = $user->uid; // you can set the author for a node if you want
$node->status = 1; //(1 or 0): published or not
$node->promote = 0; //(1 or 0): promoted to front page
$node->comment = 0; // 0 = comments disabled, 1 = read only, 2 = read/write
$node = node_submit($node); // Prepare node for saving
// field collection items.
for ($i = 0; $i < $total_values; $i++) {
$field_collection_item = entity_create('field_collection_item', array('field_name' => 'field_answers'));
$field_collection_item->setHostEntity('node', $node);
$field_collection_item->field_question_body[LANGUAGE_NONE][] = array('value' => $questions[$i]);
$field_collection_item->field_answer_body[LANGUAGE_NONE][] = array('value' => $answers[0]);
$field_collection_item->save();
}
node_save($node);
Great solution for me. But I got a new problem. This method only save one record for $field_collection_item['und'][0]['value']. If I want to save $field_collection_item['und'][1]['value']. What should I do? Please help! Thank you.
If anyone else needs something similar, the below script will make copies of any field collections on your site. In this case, it creates 2 copies for foreign languages (Spanish and Portuguese). It also adds field instances that were on the original field collection.
// loop through all field collections on the whole site
$field_collection_fields = db_query("SELECT * FROM {field_config} AS fc WHERE type = :type ORDER BY id ASC", array(':type' => 'field_collection'));
foreach ($field_collection_fields as $field_collection_field) {
// load the full objects -- the DB query only returns raw data from the DB
$field = field_info_field($field_collection_field->field_name);
$instance_object = db_query("SELECT * FROM {field_config_instance} AS fi WHERE field_id = :field_id ORDER BY id ASC", array(':field_id' => $field['id']))->fetchObject();
$instance = field_info_instance($instance_object->entity_type, $instance_object->field_name, $instance_object->bundle);
// create new fields based on sp_ and pt_ prefixes
$field_name_parts = explode('ield_', $field['field_name']);
$field_sp = $field_pt = $field;
$field_sp['field_name'] = 'field_sp_' . $field_name_parts[1];
$field_pt['field_name'] = 'field_pt_' . $field_name_parts[1];
// unset the id property so they're treated like new fields
unset($field_sp['id']);
unset($field_pt['id']);
// create fields
field_create_field($field_sp);
field_create_field($field_pt);
// now create instances to go along with the fields
$instance_sp = $instance_pt = $instance;
// prepare the slightly adjusted properties for saving
$instance_sp['field_name'] = $field_sp['field_name'];
$instance_pt['field_name'] = $field_pt['field_name'];
$instance_sp['label'] = $instance['label'] . ' (Spanish)';
$instance_pt['label'] = $instance['label'] . ' (Portuguese)';
$instance_sp['widget']['weight'] = $instance['widget']['weight'] + 1; // SP field goes directly below original
$instance_pt['widget']['weight'] = $instance['widget']['weight'] + 2; // PT field goes 2 weights below original
// now save new field instances
field_create_instance($instance_sp);
field_create_instance($instance_pt);
// save field instances that belong to each field collection
$field_collection_instances = db_query("SELECT * FROM {field_config_instance} AS fc WHERE entity_type = :entity_type AND bundle = :bundle ORDER BY id ASC", array(':entity_type' => 'field_collection_item', ':bundle' => $field['field_name']));
foreach ($field_collection_instances as $field_collection_instance) {
$fc_subfield_instance = field_info_instance($field_collection_instance->entity_type, $field_collection_instance->field_name, $field_collection_instance->bundle);
$fc_subfield_instance_sp = $fc_subfield_instance_pt = $fc_subfield_instance;
// prepare properties for saving
$fc_subfield_instance_sp['bundle'] = $instance_sp['field_name'];
$fc_subfield_instance_pt['bundle'] = $instance_pt['field_name'];
$fc_subfield_instance_sp['required'] = $fc_subfield_instance_pt['required'] = FALSE;
field_create_instance($fc_subfield_instance_sp);
field_create_instance($fc_subfield_instance_pt);
}
}
Hello
There is useful things here, but I dont find how actually creating a fc in a module : where do we declare the fc, I mean what is the right hook_???_info for fc ? Is there any beginner sample module code somewhere ?
artatum: You would declare appropriate functions in the module and then have them called for specific circumstances. These circumstances could include the submission of a form, a call to Services API, a cron job retrieving data from a database, etc. The answer all depends on what you are trying to create and under what conditions.
Comments
Comment #1
tcalin commentedHello!
I am trying to create a field_collection from the code (a line on an invoice to be more exactly)
The sequence looks like this:
but I get the following exception from
savefunction:SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '122' for key 'PRIMARY'
Am I missing something?
Thanks in advance!
Comment #2
ogi commentedsubscribe
Comment #3
ogi commentedThis worked for me:
Comment #4
tcalin commentedThanks!
I finally got the time to test it and it worked for me too.
Anyway, my code should also be OK.
Comment #6
tcalin commentedThis solution does not work anymore.
Comment #7
rbruhn commentedThought I would post my solution for programmatically creating a field collection, along with an attached field, during a module install. The example is specific to my use, but easily modified to add the field types you need. Of course, if there is an easier way please feel free to add your thoughts. This works with the current 7.x-1.x-dev branch.
And as stated above, this worked when needing to add data programmatically.
Comment #8
phreestilr commentedThanks! Very helpful
Comment #9
jibize commentedThank you!!
Comment #10
jmomandown commented@rbruhn: Sorry to reactivate an old thread. I was curious if you knew of any issues with field collection creation in the traditional field array method as you have described when in a batch operation. The following throws a HTML general error and has had me stumped for the past few days.
Comment #11
rbruhn commented@JMOmandown - Well, I took your code above and changed a few things so I could run it in a test.php file using drush on my system. The code you posted is simply attempting to create Field Collections, and not attaching fields to those collections. So I'm assuming that is what you wanted. In the code below:
1) I changed the entity_type and bundle because I do not have commerce installed.
2) I refactored the code since you are only creating one Field Collection at a time when calling the function. So there is really no need to use a foreach loop if you don't have to.
3) As well, since I'm merely testing the function, I removed the other unneeded variables being passed to the function so it would not throw an error while running the test script. I know you are doing this in a batch so yours is different, but perhaps the below will help you find out what is wrong.
Anyway, the below creates the Field Collections for me. I see them added in my UI, as well as being present on the Article node form.
Hope this helps.
Comment #12
jmomandown commented@rbruhn: Thanks for the quick response. As I feared it is still not functional which is so weird to me. If I comment out the function above (one of many operations in the batch) then the batch runs smoothly. With it enabled I get and error with no repsonse text:
So weird, but I thank you for your quick response and attempt.
Comment #13
rbruhn commented@JMOmandown: Yes, I hate those messages that don't really give you anything to work with. I think they are trying to fix some of those in D8. When I run into your problem, I use to place some code to spit out to a file or something so I can at least see at what point it's failing. For example, writing to file when it creates the field or instance and the name of the field. From there, following to each function called so I can get the exact point. I could usually figure out what is going wrong.
Might be possible to use drush to run your batch (http://drupal.org/node/873132) with debug enabled. Since I've started using drush, errors have been a lot easier to ascertain.
Comment #14
thtas commentedI'm having success with this method
Comment #15
dgtlmoon commentedTo set multiple field collection values, just put the field_collection entity stuff in a loop
hope it helps someone!
Comment #16
codesmithI just had an issue where trying to add a new field collections to the 'user' entity would delete any existing items. The solution was to directly load the user entity and not depend on the global $user as that one doesn't have the field collection information.
Comment #17
Thomas83 commentedfirst of all, English is not my native language so sorry if this reads bad.
I'm trying to program a field collection with field collection item fields like the code as given by rbruhn here: https://drupal.org/comment/4653514#comment-4653514
I want to create it on an easier leavel for testing purposes. My goal is to create a taxonomy reference dropdown and a textfield.
I manage to reproduce the field_collection type. Hower, I can't reproduce any field collections item fields. Let's say I want a textfield in my field collection.
creating an ordinary textfield field collection item field, this would make sense to me (but it doesn't work). Can someone tell me what I do wrong? Instead of appearing inside my field collection it appears as a field inside my node which contains my field collection field
Comment #18
pratip.ghosh commentedFor anyone wondering how to save a fieldcollection data which is within another field collection data, it may come to some help...
Comment #19
petu commentedHere is the code for multiple values inside the field collection entity:
Comment #20
jwilson3Thanks to all previous commenters, this thread helped me get what I needed done. However I ran into error message #1822844: Notice: Undefined index: revision_id in field_collection_field_get_entity() (line 1586 of field_collection/field_collection.modu and found that if I used:
Instead of:
The errors went away.
Comment #21
ethanLee commentedGreat solution for me. But I got a new problem. This method only save one record for $field_collection_item['und'][0]['value']. If I want to save $field_collection_item['und'][1]['value']. What should I do? Please help! Thank you.
Comment #22
DieterAtWork commentedFor a good tutorial on how to actually do this, with explanations, look at this: https://www.drupal.org/node/1842304#comment-9295475
Comment #23
jienckebd commentedIf anyone else needs something similar, the below script will make copies of any field collections on your site. In this case, it creates 2 copies for foreign languages (Spanish and Portuguese). It also adds field instances that were on the original field collection.
Comment #24
artatum commentedHello
There is useful things here, but I dont find how actually creating a fc in a module : where do we declare the fc, I mean what is the right hook_???_info for fc ? Is there any beginner sample module code somewhere ?
Comment #25
kenorb commentedSee: Entity metadata wrappers
Comment #26
logicp commentedartatum: You would declare appropriate functions in the module and then have them called for specific circumstances. These circumstances could include the submission of a form, a call to Services API, a cron job retrieving data from a database, etc. The answer all depends on what you are trying to create and under what conditions.
Comment #27
napche commentedA little sidenote :
I was struggling with this to prepopulate an entityForm using the prepareEntity function.
In the end I found out that setHostEntity actually saves the host entity, so it might be wise to save that function till the end.