I'm trying to understand the form API from several pieces of documentation that are incomplete, and, to me, not very clear. I'm going to summarize what I have learned so far to test my understanding, and then ask some questions about pieces of the puzzle that are still missing.

As I understand it, one displays a form by calling drupal_get_form('formname'), where formname is both the internal name of the form and the name of a function which returns an array which defines the form.

This makes the form API call formname() to get the information from which it builds the form. Then it displays the form.

When the user clicks a submit button defined in the form, the form API calls a validation function named formname_validate(), if that function exists. The validation function receives the form definition array and a result array. If it finds errors it handles them by calling form_set_error().

If the validation function finds no errors, or if there is no validation function, the form API calls a submit function named formname_submit(). This function receives the same two arrays, and can perform any appropriate processing on the results.

Right so far?

Now, the questions.

1. Where can I find information about what goes into the form definition array? I have read several examples which describe some of the attributes of some types of fields, but I haven't found anything that is complete enough to be useful for designing a real form, or anything that is organized for reference.

2. Several of the documents I have read speak in terms like "...your module will need a .info file too..." as if a form has got to be defined in a module. Is that true? If so, why... if not, what are these documents trying to say?

3. Where can I find information about how to modify a predefined (core) form?

4. I have seen some examples in which the key of one element of the form definition array appears to be a pathname that will display the form. Is that right? If so, how does it fit in (if it does fit in) with the procedure I described? If form processing is initiated by a call to drupal_get_form(), a pathname has no role to play. If it is initiated by a reference to a pathname, I don't see how Drupal can know what form definition function to call.

My immediate task is to produce versions of the forms for creating and editing/deleting a story, which assign fixed values to certain fields (omitting those fields from the form) and assign default values to others. Any tips or shortcuts you can suggest will be welcomed.

Comments

orthoducks’s picture

Thank you -- the first reference looks like a complete answer to my first question. (The second is a document I've already read, but I've read many, so that's hardly surprising. It is, by the way, the one that made the most sense to me.)

The other three questions are still pending. The one about how to modify a form is critical.

I found references which say that the way to modify a form is to call hook_form_alter(). I read the API description of that function, but it really doesn't explain how to use the thing. For instance, does the function make a one-time or a permanent change? And how do I get the name of the (core) form that I want to alter? Finding no answers to these obvious questions makes me wonder what else I haven't found, but don't know I need.

It occurred to me that I might be able to modify a form by calling its form definition function myself, modifying the array it returns, then calling drupal_get_form() with the modified array. Would that work; is it a reasonable alternative to using this partly documented function?

nevets’s picture

Let me try and answer your other questions,

Regarding 2, I always define form using modules and when you consider you typically want to handle both validation and submittion it makes sense to use a module. Some people put the code in the body of the node but then it is hard to maintain and also hard to use on another site. As for modules, they need at least two file a .module file which contains the code for the module (in Drupal 6 more properly this would be the "core" code for the module). You also need a .info file that "describes" the module. If your module needs to do things on installation such as create database tables you will also want a .install file As for your more general question "what are these documents trying to say" without a specific question/reference it is hard to say.

Regarding 3, I have a small module that implements hook_form_alter and prints out form ID and form structure, I enable it when I need to modify a form and am unsure of the form id and/or structure. With the module enabled I visit the form in question to get the information I need. Note if you have the search box enable or you are not logged in you may see information for more than one form. Depending on your goal you may want to look at the form as admin, a "regular" logged in user and/or someone not logged in as the form information may very based on a users permissions.

Regarding 4, there are two concepts here, one is a form id which iis passed to drupal_get_form() to build the form. The other is in general a form is displayed on a page and menu callback is used to generate a page. In general terms the menu callback is any function that returns html. As part of the callback you can provide callback arguments. So one can define a menu callback which is associated with a path (when that path is visit the callback function is called) and use drupal_get_form() as the callback using the form id as a callback argument.

As for your immediate task, be careful. Do not start modify forms based on what you see as user 1, you are going to want most of those fields available just in case. Most users see a small submit, the 'title' and the 'body', You can handle the title with Automatic Nodetitles module.

orthoducks’s picture

Thank you -- your answers make this clearer. They are, however, raising more questions.

Regarding the matter of defining modules, I haven't written any yet, so I may be missing some of the finer points (the ones that don't hit you over the head). I think of writing modules like encapsulating code in any language: it it only makes sense if it makes programming easier, or if the code may foreseeably be reused. Since Drupal code can also be encapsulated by putting it in a function in template.php, good practice seems to require creating a module only if the code may be reused in other projects, or perhaps if it does not lend itself to encapsulation in a single function.

In this case I'm looking at a couple of pretty simple pieces of code which might even be combined into one. They are unlikely ever to be reused without unforeseeable changes. In this situation I don't see a lot of value in writing a module. Is that a fair assessment, or am I missing something?

As I learn Drupal, I am trying to tackle just one new component of the system at a time. Sometimes that is impossible, but it makes both learning and doing much easier. For that reason, I don't want to put my first form (or my first modified form) in a module if I possibly can help it.

I have a small module that implements hook_form_alter and prints out a form ID and form structure...

But isn't "hook" in hook_form_alter a placeholder for the form ID? If that's correct, you need to know the form ID to make Drupal call the function! What am I missing here?

Regarding the menu callback, I see what you're saying, but I think it applies to me in only one case (add). My edit/delete form will be called directly by a set of links embedded in another page. In that case, I infer, the pathname entry may be omitted from the array?

nevets’s picture

Drupal hooks need to be in module otherwise they will not be called. When implementing a hook like hook_form_alter(), hook is replaced by the module name. So if your module was called mymodule, you would write a function called mymodule_form_alter.

Internal links refer to paths defined by some module, so you only need to implement hook_menu if you are adding new callbacks.

orthoducks’s picture

I wrote a module that dumps the parameters of hook_form_alter(). Yikes! -- I got over 2300 lines of stuff. I'm feeling a little overwhelmed. I'll start tracking through it tomorrow morning.

I feel like asking whether there are any tricks I should know, but probably not. Just do the work.

Later -- I spent some time studying the dump this evening, and I'm unsure about one thing. The fields that I don't want to display on the form -- how do I handle those in the array? I originally supposed that I could simply delete them, but I assume that the core is moving the values in the form to the database, and if they disappear from the form I suspect it won't know what to do. That's a particular concern in the case of the Create form, where I need to set default values.

nevets’s picture

You can change the fields to type 'value' or 'hidden' and use '#default_value' to set '#value', something like this

$value = $form['some_field']['#default_value'];
$form['some_field'] = array(
  '#type' => 'value',
  '#value' => $value
);
orthoducks’s picture

Thank you -- I hadn't figured out the default values yet, but I found the technique for hiding the fields on my own. (I actually have to set display: none, since some of these elements are not input fields.) It works like a charm. Most of the time.

Some of the fields and fieldsets in the "Edit a node" form have no ID selectors, so I used the #prefix/#suffix elements to wrap them in <div> blocks. Then things got weird: a couple of the fieldsets, "Menu settings" and "URL path settings," don't exist in $form at all. I'm baffled about how they appear in the form, and more practically, I'm baffled about how I can control them.

In one case ("Menu settings") the visual element happens to be wrapped in a <div> block with a unique class, and I was able to use the class to hide the element. In the other case I seem to have no leverage at all.

Later -- I realized that "URL path settings" is defined by the Path module, and is added to the form by another call to hook_form_alter(), presumably after my call.

Knowing that does not help me see how to fix the problem, though. Any ideas?

nevets’s picture

I think you are trying to do things the hard way and possibly headed for trouble. Hiding those fields from everyone including user 1 means you have no way of manually setting those values if needed.

Since you have not said why you are doing this I will point out you there are permissions for each of those so you can control who can edit them, for 'Menu Settings" there is 'administer menu' and for 'URL path settings' there is 'URL path settings'.

As for order of hook calls I am guessing your module starts with a letter less than 'p'. You can change the weight for you module in the drupal table called 'system' so you module runs after the path module (a bigger weight runs later).

Instead of using CSS (display: none) I would simply set things correctly with hook_form_alter() and there is no need for #prefiix/#suffix.