Monday, March 1, 2010

The drupal_get_form() function

The Form API

Here is another crucial part of drupal. Building forms and handling their responses. We are going to take a look at how drupal works its magic.

Form Requests and Form Posts

At the most fundamental level, there are two aspects to form handling: displaying the form for the first time and handling the user response to a form. And both dimensions are handled by the same function - drupal_get_form().

Consider a drupal request to create a new item of content. This is done by clicking on the Create Content link and then selecting the specific type of content to create. Clicking on this last link causes navigation to a url of the form
http://domain/drupal/index.php?q=node/add/contenttype

The menu system routes this request by looking up the page callback for this pattern in the menu_router table. In this case, it is likely to find node_add() in modules/node/node.pages.inc

This function creates the essential node object with user and type information, sets the title of the page and calls drupal_get_form() with 'contenttype_node_form' as the form id. And, of course, the node object it created.

drupal_get_form(): Painting the Form

At this point, drupal_get_form() is going to paint the form and display it. To do this, it constructs the form object which is a hierarchical representation of the page. There are many details here, but the important thing to understand is that they are all oriented to getting a form representation constructed.

The first thing is to get the basic form construct. This is done in drupal_retrieve_form() which first invokes the forms hook on all enabled modules. For our interest, this causes node_forms() to be invoked which provides the node_form() callback for all node types. And this function is invoked.

node_form() is a part of the node module and it constructs the form object required. This form object contains many attributes - node id, version id, user id, created, type etc. It also contains form elements for revision info, author info, publishing options etc. which are familiar options when creating any kind of content in drupal. This is where it happens.

The next step is to render the form. This is done by drupal_render() which recursively iterates over the form object calling the theme() function to do the rendering. This themed output is collected into the $content variable which is displayed by the currently selected theme.

drupal_get_form(): Handling the Response

Now, if you observe closely, the url of the form to add the new content node is the same as that when painting the form. http://domain/drupal/index.php?q=node/add/contenttype

This means that when the form is submitted, it isis going to follow the same path as we have chalked out above. The major difference is that now there is POST input available. Now, drupal_get_form() distinguishes the two requests based on this.

During its processing, drupal executes all submit handlers registered for the form. This includes the submit handlers for the main node itself which take care of things like author info, revision info and publishing options. But it also includes submit handlers for additional information that has been added on by custom content types.

Once all the submit handlers have been executed, the rest of the processing is identical to that for painting the form and voila the form is repainted.

The Whole Picture View

Now that we have seen the pieces, lets look at what has been achieved. Apart from all the value that drupal adds in terms of security (i.e. addresses vulnerabilities) etc., drupal achieves one significant goal.

Drupal wants to encourage the building of custom content types and a richness around them. For example, a content type representing a product - this could be the basis for creating an online store using drupal. So, it says, there is a basic node type that represents content - let the developer extend this basic type.

How does the developer do this? Drupal provides the following procedure: the developer should create the module and provide the form elements for the new content type. Drupal integrates this into the node creation process. And when the user submits the information, drupal calls the submit handler that allows the developers code to create the rich product information as a part of the node creation.

Friday, February 19, 2010

The theme() function

Understanding theme()

The Theme Registry

The theme() function relies on a large body of information which is the theme registry which contains entries for every conceivable element that can be presented. There are entries for :
  • - large items of presentation like "node_list" which will present a list of drupal nodes,
  • - units of presentation like "search_block_form" which presents the search text field and button,
  • - right down to individual elements of presentation like "breadcrumbs" and even "text_textarea"

Each element in the theme registry contains the following information:
  • default argument values for the arguments required for the element
  • the path to the currently selected theme (since template files reside here)
  • the name of the function or template to be used for rendering
  • the list of pre-process functions - more on these later
This information is used to make the call to the function that will render the element.

Recursion

Drupal has organized its structure recursively, so that a call to the larger presentation element results in calls to theme() for smaller presentation elements which results in calls to theme() for even smaller presentation elements.

Functions

Depending on the information in the theme registry entry for the element being processed by them(), either the specified function is called or a template rendering function is called. If it is a function that is called, there is no further processing - the information from the theme registry and the passed arguments are passed onto the function.

or Templates

If it is a template that is invoked, then a set of variables are constructed which will be used by the template. Preprocess functions populate these variables. The preprocess_template() function is called for all templates and it populates variables like is_admin, is_front, is_logged_in and the user object.

If a template require additional variables, then there are special preprocess functions that populate additional variables. For example, the special preprocess function for the "page" element populates over twenty variables.

The Whole Picture

Having understood the pieces, here is the "whole picture" view.

Drupal wants to separate the presentation from the drupal core. So it says - let me create data for whatever can be overridden - and puts it in the theme registry. So, node-lists, search-blocks and breadcrumbs along with a few hundred other elements get into the theme registry.

So what does this do? It allows the elements to be changed. And, in what ways can an element be changed? Since you can change the function that is called for generating the output, this means that you can generate whatever output you want.

And, the beauty of the system is that it allows the change to be made on an element-by-element basis, thus allowing customization over the entire range - from the smallest to the largest elements.