Using the REST service to create a node that includes an 'entity reference' field.

We encourage users to post events happening in the community to the community events group on https://www.drupal.org.
DUG1138's picture

I'm using cURL in PHP to import data with a one-to-many relationship: One "Place" can be related to Many "Documents" (using the "Entity Reference" module).

The syntax I'm looking for is for the last key/value pair in this array:

$node_data = array(
'title' => $title,
'type' => 'place',
'body[und][0][value]' => $body,
'field_place_country[und][0][value]' => $field_place_country,
'field_legacy_id[und][0][value]' => $field_legacy_id,
'field_place_alt_name[und][0][value]' => 'test a',
'field_place_related_document[und][0][target_id]' => $field_place_related_document_id,
);

What should the syntax be, rather than: field_place_related_document[und][0][target_id]

?

Thank you,
Doug

Comments

An answer, but not a simple one...

Caza's picture

I've been struggling with this problem for a while now, and while I have an answer, it isn't simple and requires some module work. (I'm working in Drupal 7, fwiw)

Looking at the code carefully, it appears that the REST server puts the input data through the standard drupal form input paths. That's reasonable, but along with the form goodness this path picks up whatever widget you defined when you created your entity reference field. The problem is that entityreference does not allow you to provide a straight numeric id - it will only accept the name of the entity as input. In order to get around this problem, you have to write some code.

I did a couple things here - I hooked field_widget_properties_alter to change the widget associated with my entityreference to be straight numeric, and I hooked FORM_ID_form_alter to change the validation associated with the field. So my code looks something like this:

// hooking FORM_ID_form alter for the poi_form. 'poi' is my new node type that
// includes a couple of entity references.
function test_wine_walk_form_poi_node_form_alter(&$form, &$form_state, $form_id){
  $form['field_walkmap']['und'][0]['#element_validate'][] = 'poi_node_entityreference_validate';
$form['field_walk']['und'][0]['#element_validate'][] = 'poi_node_entityreference_validate';
    unset($form['field_walkmap']['und'][0]['value']['#element_validate']);
unset($form['field_walk']['und'][0]['value']['#element_validate']);
}

// new validator. Notice that we're setting the target_id from the #value that's
// been processed by the form.
function poi_node_entityreference_validate($element, &$form_state, $form)
{
  if (!empty($element['value']['#value'])) {

       // first, check that the value is an integer
       $value = $element['value']['#value'];
      $regexp = '@[^-0-9]@';
       if ($value != preg_replace($regexp, '', $value)) {
           $message = t('Only numbers are allowed in entity reference %field.', array('%field' => $instance['label']));
          form_error($element, $message);
            return;
        }
             
       // set target_id, which is where the entityreference module expects to find
        // the entity id
       $target = array('target_id' => $element['value']['#value']);
      form_set_value($element, $target, $form_state);
    }
}

// hook_widget_properties_node_alter
// Set the widget associated with the entity reference to be a number rather than
// whatever the hell it was before
function test_wine_walk_field_widget_properties_node_alter(&$widget, &$context) {  
   if ($context["instance"]["bundle"] == 'poi' &&
     ($context["instance"]["field_name"] == 'field_walkmap' || $context["instance"]["field_name"] == 'field_walk')) {
       $widget["type"]   = 'number';
      $widget["module"] = 'number';
   }
}

I still ended up running into one more problem - for some reason, my data wasn't getting submitted. I don't quite get why this is true. it seems like node_form_submit ought to get called if you don't have a customized submitter for your content type, however, I'm new to Drupal and don't claim to understand its intricacies. To get around that problem, I defined my own submitter for my poi content that just calls node_form_submit.

Here's my code:

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

  // remove the submit handler that called this function.
    // Since node_form_submit calls the handlers, if we leave
  // the submit function in, we'll end up with infinite recursion.  
   foreach ($form['#submit'] as $idx => $handler) {
      if ($handler == 'poi_node_form_submit') {
            unset($form['#submit'][$idx]);
       }
  }
 
   $node = node_form_submit($form, $form_state);
 
   return $node;
}

At this point, my code correctly creates entity references and nodes through REST.

To answer your original question, though, the syntax of the REST request uses value, not target_id. The form processing expects to see the input in value. The validator in the above code takes the value (which is placed in '#value' by the form processing, and creates the target_id field, which is used later to insert into the database. This is the same process that the EntityReference module would perform, if I were using its validator (remember that I cant use its validator, since it expects the input to be the reference name rather than the reference id). So, what you're looking for is
'field_place_related_document[und][0][value]' => $field_place_related_document_id
plus the hooks above.

Some things I haven't figured out yet (any suggestions would be appreciated) -
* How do I tell that a form is being processed by REST? What I've got now operates even if someone is trying to create the node through the standard browser interface. What hook can I use to set state, or is there already some state information I can look at?
* When validating, I'd like to make sure that the entity reference matches the type of reference I'm expecting. I can pull the bundle type out of the database easily enough, but how can I find the expected type?
* Is this really all necessary? I didn't see an on-line answer to this question that came close to working for me, but, dear god, figuring that stuff out was a hell of a lot of work for functionality that one could reasonably expect to be built in to EntityReference and the Services modules. I'm not complaining if this really is the case, but I wonder if there isn't an easier way.

Thanks,
--C

Services

Group organizers

Group categories

Group notifications

This group offers an RSS feed. Or subscribe to these personalized, sitewide feeds:

Hot content this week