2

I have built a custom form (in a module) that creates several related nodes in the database when submitted. Now, I need to create a module so that a user can update his/her submitted form data at a later date. Here's my rough plan:

(1) Determine the nodes that the user wants to update via a wildcard in a requested url; (2) retrieve the required data from the database; (3) build a form and insert the retrieved database values via the #value property; (4) bulid a submit function that uses db_update to update the node

Is this the right approach for updating a node via a custom form? Here's my (not working) module code so far:

// Implements hook_menu().
function update_card_menu() {
    $items['update-card/%update_card'] = array(
        'page callback' => 'update_card_form',
        'page arguments' => array(1),
        'access callback' => TRUE,
    );
    return $items;
}

// Database query function.
function update_card_load($nid) {
    $query = db_select('node', 'n');
    $query -> condition('nid', $nid);
    $query -> join('card_number', 'cn', 'n.nid = cn.entity_id');
    $nodes = $query
    -> fields('n', array('nid', 'title'))
    -> fields('cn', array('field_card_name_nid'))
    // This query will get more complex - I need more nodes and more fields
    -> execute();
    foreach ($nodes as $field) {
        $card['nid'] = $field -> nid;
        $card['card number'] = $field -> field_card_number_nid;
    }
    return $card;
}

$card now contains some values I want to populate my update form:

Array ( [nid] => 123 [card number] => 456 )

But how do I get $card into my form builder function, so that I can populate my #value properties from this array? My code does not work.

/**
 * Renderable form array return function.
 */
function update_card_form() {
    return drupal_render(drupal_get_form('update_card_capture'));
}

/**
 * Form builder function.
 */
function update_card_capture($form, &$form_state, $card) {
    $form['nid'] = array(
        '#title' => t('nid'),
        '#type' => 'textfield',
        '#value' => $card->nid
    );
        $form['card number'] = array(
        '#title' => t('Card number'),
        '#type' => 'textfield',
        '#value' => $card->card_number
    );
}
  • Try using #default_value instead of #value... – Clive Apr 15 '13 at 15:25
  • I'll give it a go. It's my page callback in hook_menu that concerns me most. I feel like I'm mixing two separate things together: (1) trying to load values from my db into an array according to a url wildcard value; and (2) trying to render a form. Something is not right with that. – 24ma13wg Apr 15 '13 at 15:36
  • Oh, didn't realise it was a form - you want Charlie S's answer in that case – Clive Apr 15 '13 at 15:36
3

You need to use hook_menu() to register the path and callback function, like so:

<?php
function hook_menu() {
  $items['some-path/%node'] = array( // %node will use the node_load() function that's already provided by node.module.
    'type' => MENU_CALLBACK // This is just a callback item, it will not appear in any "menus"
    'page callback' => 'drupal_get_form'
    'page arguments' => array('the_form_function_name', 1) // Where 1 = the %node wildcard
    'access callback' => 'node_access',
    'access arguments' => array('update', 1), // The user must be able to update the node
  );
  return $items;
}
?>

Then build a simple form that provides fields to alter on the node, and run node_save() in the submit handler to save any modifications you make to the node.

<?php
function the_form_function_name($form, &$form_state, $node) {
  drupal_set_title('Now editing node ' . check_plain($node->title));

  // Just store the entire $node object in a custom form property. Easy.
  $form['#node'] = $node;

  // An example form field that we'll use to update the node's title.
  $form['new_title'] = array(
    '#title' => 'Title',
    '#type' => 'textfield',
    '#default_value' => $node->title,
  );

  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => 'Save Node',
  );

  return $form;
}

function the_form_function_name_submit(&$form, &$form_state) {
  $form['#node']->title = $form_state['values']['new_title'];
  // Save the modified node object (http://api.drupal.org/node_save)
  if (node_save($form['#node'])) {
    drupal_set_message('Updated the node.');
  } else {
    drupal_set_message('Could not update the node.', 'error');
  }
}
?>

This is just sample code – I have not tested, but it should work barring any typos.

You would hit this via: http://example.com/some-path/123 where 123 is the node ID of the node you want to edit.

  • Thanks. This is a simpler approach. However, I need values from many fields and nodes to populate my update form. This is why I think I need to do a database query. I've put the code I have so far in my question. – 24ma13wg Apr 15 '13 at 15:24
  • So you need additional fields from additional nodes beyond the node specified in the URL? – Charlie Schliesser Apr 15 '13 at 20:52
  • Yes - but the other nodes each have a field which contains the nid of their 'root' node, so one wildcard integer is all I need to query the db for all the fields I need. But, I don't think I can use the existing node_load function. I need to write my own load function to do this db query. Your method looks right: page callback drupal_get_form and supply, as arguments, the built form and the values from a load function - if that's what you're suggesting? – 24ma13wg Apr 15 '13 at 21:57
  • Just do it at the top of the form function, not in a custom wildcard loader. Here's a loose example: $other_nodes = db_query("SELECT f.field_value FROM field_my_field_name f LEFT JOIN field_node_reference r ON r.entity_id = f.entity_id WHERE r.field_node_reference_id = :primary_node_id", array(':primary_node_id' => $node->nid))->fetchAll(); – Charlie Schliesser Apr 16 '13 at 15:02
  • I had the same thought today. No need for the load function. – 24ma13wg Apr 16 '13 at 21:23
0

Solution for point 1: Implement hook_menu()

The code should be something like this:

$items['my-view/%'] = array(
  'page callback' => 'mymodule_create_form', // Function name that create the form
  'page arguments' => array(1), // You pass the wildcard(%) value, in this case, the id of the node that you will use to load with node load()
)

Point 2 : Always node_load(). It will give you the chance to act with hooks and you will can add or delete fields without touch the querys.

Point 3: I would reuse the form that you`ve created when a node is added. You will have to fill the fields in form using #value property.

Point 4: node_save(). Updated the node object that you loaded in point 2.

  • I've hesitated to use hook_menu, because 'Update form' won't be a menu item on my site - so I assume hook_menu is used for non-menu related purposes too? - if that made any sense. – 24ma13wg Apr 14 '13 at 12:22
  • With this hook you say to the system that a function will be called to build the code. You could add it to a menu as a menu item with the "menu_name" property, but it's not the case. – Rufein Apr 14 '13 at 12:38
  • Re. the link you sent me to hook_menu(): the paragraph that begins "Registered paths may also contain special "auto-loader..." - is this what I need to be following to load my node from the database (point 2 in my plan)? Thanks – 24ma13wg Apr 14 '13 at 15:07
  • You could implement it. You can choose between 2 ways: function mymodule_create_form($id){$node = node_load($id); // Now build the form and fill it with the values of node. Finally return the form} or you could implement as you say. Firt the hook_menu: $items['my-view/%mymodule_node']array( 'page callback' => 'mymodule_create_form', 'page arguments' => array(1), ) and finally add a function to your module called function mymodule_node_load($id){return $node_load($id);} – Rufein Apr 14 '13 at 16:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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