I want to be able pick selected fields for a node and edit them in a custom form. So for example I have a custom content type which includes many fields but I want a simple edit form which can be called by Re-writing the url for the edit link on view to show just say 3 fields with a node_save button.

Please can someone get me started on this. Thanks.

Comments

Jaypan’s picture

I wrote a tutorial that covers this:

https://www.drupal.org/node/2743225

sprite’s picture

spritefully yours
Technical assistance provided to the Drupal community on my own time ...
Thank yous appreciated ...

Jaypan’s picture

Oops, thanks for the proper link :)

peterk900’s picture

I am on the trail with this. This certainly doesn't look like junior school stuff but if it's like Jaypan's other tutorials, it will explain what is going on and the code will definitely work !. Thanks again.

peterk900’s picture

When I replied to your post I was looking at this tutorial which I had actually found yesterday. Was this the one you meant rather than the one relating to Multi-Step forms ?

Jaypan’s picture

You can use that one, but the one I linked to is actually the method I would use.

The tutorial itself relates to multistep ajax forms, but the relevant point in it is how an object (node in this case) is passed into a form, then when the form is saved, that node is manipulated.

Here is a really simple example, that's easier than the tutorial:
Hook menu:

function hook_menu()
{
  $menu['my/node/edit/%node'] = array
  (
    'title' => 'Edit node',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('my_node_edit_form', 3),
    'access callback' => TRUE,
  );

  return $menu;
}

The menu item above will load a full node object (due to the wildcard %node), and pass it to the form definition.

Next the form definition:

function my_node_edit_form($form, &$form_state, $node)
{
  // Save the node to the form state for use in the submit function
  $form_state['node'] = $node;

  // Set a field where the title can be edited
  $form['title'] = array
  (
    '#type' => 'textfield',
    '#title' => t('Title'),
    '#default_value' => $node->title,
    '#required' => TRUE,
  );

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

  return $form;
}

Now in your submit function, you can manipulate the node and save it with the submitted values

function my_node_edit_form_submit($form, &$form_state)
{
  // Retrieve the node
  $node = $form_state['node'];

  // Set the submitted title to the node
  $node->title = $form_state['values']['title'];

  // Save the node
  node_save($node);
}

The key point to note here is how the node object is passed to the form definition, stored for later usage, then has the values changed and saved in the submit function.

peterk900’s picture

Thank you for the 'simplified' version. And yes, I agree with your original tutorial suggestion. I was confusing that with another Javascript multi-page form tutorial of yours that I remember using in the past. The other tutorial, multiple records, with filters, being edited, which includes theming a form as a table is a teaching goldmine - at least for me. Thanks again for such a good level of help on this.

peterk900’s picture

I have run the simple example code and get this error when I enter the url of a valid node with a title e.g. my_node/edit/5

Notice: Trying to get property of non-object in my_node_edit_form() (line 27 of /home/sites/xxxxxxx.org/public_html/drn1/sites/all/modules/my_node/my_node.module).

The form displays the title field but it is empty.

Here is the module code

<?php

function my_node_menu()
{
  $menu['my_node/edit/%node'] = array
  (
    'title' => 'Edit node',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('my_node_edit_form', 3),
    'access callback' => TRUE,
  );

  return $menu;
}

function my_node_edit_form($form, &$form_state, $node)
{
  // Save the node to the form state for use in the submit function
  $form_state['node'] = $node;
  dpm($node);

  // Set a field where the title can be edited
  $form['title'] = array
  (
    '#type' => 'textfield',
    '#title' => t('Title'),
    '#default_value' => $node->title,   // Line  27
    '#required' => TRUE,
  );

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

  return $form;
}

function my_node_edit_form_submit($form, &$form_state)
{
  // Retrieve the node
  $node = $form_state['node'];

  // Set the submitted title to the node
  $node->title = $form_state['values']['title'];

  // Save the node
  node_save($node);
}


dpm($node); is empty.

I've tried to see what might be wrong - the script is so straightforward and your explanation of what it is doing seems clear that it seems like it ought to run ok. I wondered if the offending line should be $node['title'] but this generates two errors !

Have I misunderstood how your sample script should be set up ?

PS I looked again and wondered if the line should be:

$menu['my_node/edit/node/%'] = array

as this would place the nid at array position 3 but the same error occurs.

Jaypan’s picture

In your case, the nid is at the 2nd index, not the third, so you need to do this:

    'page arguments' => array('my_node_edit_form', 2),

Then clear your cache.

peterk900’s picture

Thanks.

I can see now why the index should have been 2 but am puzzled why it should be %node not just % . For example if you want the url to be node/5 you use node/% .

For completeness here is the final code. I think the simple node update code Jaypan supplied is important because I couldn't find anything like it, despite some extensive searches on drupal.org and elsewhere.

my_node.info

name = My Node
description = Node Update.
core = 7.x

my_node.module

<?php

function my_node_menu()
{
  $menu['my_node/edit/%node'] = array
  (
    'title' => 'Edit node',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('my_node_edit_form', 2),
    'access callback' => TRUE,
  );

  return $menu;
}

function my_node_edit_form($form, &$form_state, $node)
{
  // Save the node to the form state for use in the submit function
  $form_state['node'] = $node;
  //dpm($node);
 
  echo "<pre>";
  echo print_r($node);
  echo "</pre>";

  // Set a field where the title can be edited
  $form['title'] = array
  (
    '#type' => 'textfield',
    '#title' => t('Title'),
    '#default_value' => $node->title,
    '#required' => TRUE,
  );

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

  return $form;
}

function my_node_edit_form_submit($form, &$form_state)
{
  // Retrieve the node
  $node = $form_state['node'];

  // Set the submitted title to the node
  $node->title = $form_state['values']['title'];

  // Save the node
  node_save($node);
}


Here is the print_r output showing the variables available in $node

stdClass Object
(
    [vid] => 5
    [uid] => 1
    [title] => Testing 9903bbb
    [log] => 
    [status] => 1
    [comment] => 1
    [promote] => 1
    [sticky] => 0
    [nid] => 5
    [type] => article
    [language] => und
    [created] => 1499793500
    [changed] => 1499928339
    [tnid] => 0
    [translate] => 0
    [revision_timestamp] => 1499928339
    [revision_uid] => 1
    [body] => Array
        (
            [und] => Array
                (
                    [0] => Array
                        (
                            [value] => Body 9904 XXXXXXXXXXXXXX
                            [summary] => 
                            [format] => filtered_html
                            [safe_value] => 
Body 9904 XXXXXXXXXXXXXX



                            [safe_summary] => 
                        )

                )

        )

    [field_tags] => Array
        (
        )

    [field_image] => Array
        (
        )

    [rdf_mapping] => Array
        (
            [field_image] => Array
                (
                    [predicates] => Array
                        (
                            [0] => og:image
                            [1] => rdfs:seeAlso
                        )

                    [type] => rel
                )

            [field_tags] => Array
                (
                    [predicates] => Array
                        (
                            [0] => dc:subject
                        )

                    [type] => rel
                )

            [rdftype] => Array
                (
                    [0] => sioc:Item
                    [1] => foaf:Document
                )

            [title] => Array
                (
                    [predicates] => Array
                        (
                            [0] => dc:title
                        )

                )

            [created] => Array
                (
                    [predicates] => Array
                        (
                            [0] => dc:date
                            [1] => dc:created
                        )

                    [datatype] => xsd:dateTime
                    [callback] => date_iso8601
                )

            [changed] => Array
                (
                    [predicates] => Array
                        (
                            [0] => dc:modified
                        )

                    [datatype] => xsd:dateTime
                    [callback] => date_iso8601
                )

            [body] => Array
                (
                    [predicates] => Array
                        (
                            [0] => content:encoded
                        )

                )

            [uid] => Array
                (
                    [predicates] => Array
                        (
                            [0] => sioc:has_creator
                        )

                    [type] => rel
                )

            [name] => Array
                (
                    [predicates] => Array
                        (
                            [0] => foaf:name
                        )

                )

            [comment_count] => Array
                (
                    [predicates] => Array
                        (
                            [0] => sioc:num_replies
                        )

                    [datatype] => xsd:integer
                )

            [last_activity] => Array
                (
                    [predicates] => Array
                        (
                            [0] => sioc:last_activity_date
                        )

                    [datatype] => xsd:dateTime
                    [callback] => date_iso8601
                )

        )

    [cid] => 0
    [last_comment_timestamp] => 1499793500
    [last_comment_name] => 
    [last_comment_uid] => 1
    [comment_count] => 0
    [name] => peterkaye
    [picture] => 0
    [data] => b:0;
)
1

sprite’s picture

@peterk900 ...

Jaypan is one of the most experienced and helpful people who provide assistance in the drupal.org forums. There are many thousands of very experienced Drupal developers who never, ever, provide any assistance to people in the forums, but jaypan is not one of them. Jaypan is not one of the Drupal universe who can only be found in the "Issue Queues". However I believe that Drupal's past and future can only operate if there is pragmatic assistance and advice available to all in the forums. Jaypan is part of that group of people that keeps Drupal vibrant. Drupal is a very deep development environment, way beyond "just a CMS", as reflected in part by the way Acquia markets Drupal commercially. Given that depth, it is essential for experienced people to guide others through the vast Drupal forest.

spritefully yours
Technical assistance provided to the Drupal community on my own time ...
Thank yous appreciated ...

Jaypan’s picture

Thanks for the comments Sprite.

peterk900’s picture

I have been very appreciative, and hopefully this comes out in the posts that I make, for regular assistance from these forums and Jaypan has probably provided more help than anyone as I attempt to understand more about Drupal. There is nothing in what you write with which I have any disagreement. However if there is something in this post which I have said which you consider inappropriate then please let me know. I have no doubt that my future progress in understanding Drupal depends on posting in a way that encourages others to continue to offer help.

Jaypan’s picture

I can see now why the index should have been 2 but am puzzled why it should be %node not just % . For example if you want the url to be node/5 you use node/% .

When a wildcard has a string after it, such as %node, the associated _load() function is called. So for %node, the value in the place of the wildcard will be passed to node_load(), and the return value is passed to your page callback. So if you have a 2 in the wildcard location, the 2 is passed to node_load(), and the node with a value of 2 is loaded. This is why your page callback receives a fully loaded node, rather than just the value that is passed in the wildcard.

peterk900’s picture

Got it. Thanks again.

I continue to struggle however with updating a custom field in the code which started the post which now has fields other than title - for example field_price.

When I print_r($form_state) I get :

[field_price] => Array
                                (
                                    [und] => Array
                                        (
                                            [0] => Array
                                                (
                                                    [value] => 9008.00
                                                )

                                        )

                                )



And so I would have thought this line in the submit function should have worked:

$node->field_price = $form_state->field_price['und'][0]['value'];

But it gives this error..

Notice: Trying to get property of non-object in my_node_edit_form_submit() (line 70 of /home/sites/xx.co.uk/public_html/drn1/sites/all/modules/my_node/my_node.module).
Notice: Undefined index: und in my_node_edit_form() (line 38 of /home/sites/xx.co.uk/public_html/drn1/sites/all/modules/my_node/my_node.module).

The second error is a consequence I think of the first.

Sorry but there's obviously something I'm still missing on this as I really can't see why this code should give an error. As ever, any pointers you can give are appreciated.

peterk900’s picture

The update line should be:

$node->field_price['und'][0]['value'] = $form_state['values']['price'];

So my take-away on this is that for custom fields the variable (object ?) for the value that you want in the database comes from pointing at the machine name for the field ( field_price )in the node object $node->field_price and then 'walking the array' to get to the value. You see the array from a print_r statement.
The value that you want to place in the database is the array value for the field in the form. So for a text field defined as $form['price'] , price is array key in $form_state['values'].

I think this now makes sense but please feel free to correct my explanation above. Thanks.

Jaypan’s picture

That sounds right.

To write it out a different way (both for you to double confirm, and for anyone who comes along:

Fields on the node will generally be added to the node object at $node->field_name[LANGUAGE_NONE][0]['value'].
A couple of points here, rather than using 'und', you should use the constant LANGUAGE_NONE. The next key, 0 refers to the field index. For multi-value fields, this number will increment. So if you have two values on the field, the first one will be zero, next one. Finally, while the code above uses 'value', this will depend on the field type. Entity references will use 'target_id'. To find out exactly what to do, dump the node object and see how existing values are save to find out how new values should be save.

The above is for the structure of the node that you will build before calling node_save(). The values themselves will come from the $form_state['values'] array, based on the form that has been built.

peterk900’s picture

Thanks Jaypan. I think I like your words better ! Point about LANGUAGE_NONE rather than undefined noted.