Download & Extend

Creating field collection programatically

Project:Field collection
Version:7.x-1.x-dev
Component:Documentation
Category:support request
Priority:normal
Assigned:Unassigned
Status:closed (fixed)

Issue Summary

Creating field collection programatically

Comments

#1

Hello!
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:

  $some_node = node_load($nid);
  $fc_values = array();
  $fc_values['field_name'] = 'name_of_the_field';
  $fc_values['is_new'] = 1;
  ......
  $fc_values['others'] = 'values';

  $fc = new FieldCollectionItemEntity($fc_values);

  $fc->setHostEntity('node', $some_node);
  $fc->save();

but I get the following exception from save function:

SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '122' for key 'PRIMARY'

Am I missing something?
Thanks in advance!

#2

subscribe

#3

Status:active» fixed

This worked for me:

<?php
// $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;
?>

#4

Thanks!
I finally got the time to test it and it worked for me too.
Anyway, my code should also be OK.

#5

Status:fixed» closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

#6

This solution does not work anymore.

#7

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.

<?php
/**
* Create a Field Collection field and attach collection node reference
*/
function _create_field_collection() {
 
$t = get_t();
 
 
$fields_array = array(
    array(
     
'field' => array(
       
'field_name' => 'bir_collection',
       
'label' => $t('BIR Collection Nodes'),
       
'cardinality' => -1,
       
'type' => 'field_collection',
      ),
     
'instance' => array(
       
'field_name' => 'bir_collection',
       
'entity_type' => 'node',
       
'bundle' => 'article',
       
'label' => $t('BIR Collection Nodes'),
       
'description' => '',
       
'widget' => array('type' => 'field_collection_embed'),
       
'required' => 1,
      ),
    ),
    array(
     
'field' => array(
       
'field_name' => 'bir_collection_node',
       
'type' => 'node_reference',
       
'label' => '',
       
'cardinality' => 1,
       
'settings' => array(
         
'referenceable_types' => array(
           
'bir_specimen' => 'bir_specimen',
           
'bir_image' => 'bir_image',
           
'bir_locality' => 'bir_locality',
           
'bir_view' => 'bir_view',
          ),
        ),
      ),
     
'instance' => array(
       
'field_name' => 'bir_collection_node',
       
'entity_type' => 'field_collection_item',
       
'bundle' => 'bir_collection',
       
'label' => '',
       
'cardinality' => 1,
       
'description' => '',
       
'widget' => array('type' => 'node_reference_autocomplete'),
       
'display' => array('default' => array('type' => 'node_reference_default')),
      )
    ),
  );
 
 
// Loop through fields array and create field and instance
 
foreach ($fields_array 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']);
    }
  } 
}
?>

And as stated above, this worked when needing to add data programmatically.

<?php
// Create node
$node = new stdClass();
$node->type = 'article';
node_object_prepare($node);
$node->title = 'Testing Article Creation';
$node->language = LANGUAGE_NONE;
$node->status = 1;
$node->uid = 1;
node_save($node);

// Create and save field collection for node
$field_collection_item = entity_create('field_collection_item', array('field_name' => 'bir_collection'));
$field_collection_item->setHostEntity('node', $node);
$field_collection_item->bir_collection_node[LANGUAGE_NONE][]['nid'] = 99999;
$field_collection_item->save();
?>

#8

Thanks! Very helpful

#9

Thank you!!

#10

@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']);
    }
  }
}
?>

#11

@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.

<?php
// 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);
}
?>

#12

@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.

#13

@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.