Ready to watch this entire course?
Become a member and get unlimited access to the entire skills library of over 4,900 courses, including more Developer and personalized recommendations.Start Your Free Trial Now
- View Offline
- Creating your first module
- Interacting with hooks
- Working with permissions and roles
- Controlling access
- Adding a menu item to an admin interface
- Using the Form API (FAPI) to quickly create a form
- Creating custom form validation
- Manually creating a custom content type
- Validating user input
- Importing content using feeds
- Creating a block
- Understanding best practices and coding standards
Skill Level Advanced
For web application developers, the act of building forms and processing form submissions is a common occurrence. Numerous coding frameworks contain standardized mechanisms that can be leveraged to create forms, including support for common form elements, prevention of cross-site request forgeries, and so forth. Drupal is no exception, containing a comprehensive system known as the form API or FAPI. The Drupal form API provides a sophisticated network for building, validating, and executing forms.
Using hooks, modules can cleanly manipulate any form within Drupal including those within core, by extending and overriding functionality, instead of needing to rewrite or hack it in. Drupal forms have four distinct steps; creation where the form elements and hierarchal structure are defined using a standardized array, theming which refers to the manipulation of the design. There are multiple levels of theming from individual form elements all the way up to the structural layout of the entire page.
In general, theming is mostly out of scope of this course, but it's helpful to know that the step exists. Validation immediately following form submission, Drupal form system performs generalized built-in validation. The validation step allows a developer to add more specific requirements for validation. And finally submission; once the user input has passed validation custom behavior can be outlined. Otherwise no functional changes occur. In the Wind Farms module, I'm going to specify three settings for an administrative interface: A toggle for enabling Google maps, a default map center latitude and longitude, and a default zoom level of the map.
I will cover seven common form elements: markup, checkbox, fieldset, text, select, and submit. Drupal Core contains additional form elements including date, file, password, radio, and more. The interactions and definitions of these additional form elements are very similar to the elements I will be demonstrating. To create a form, an implementation off hook_form is needed. Similar to hook menu, form elements are declared within an array.
Form elements can be nested allowing for complex hierarchies to be defined. By default, form elements are displayed in the order that they are defined in which enhances code readability. Let's create the form for the administrative settings for the Wind Farms module. Return to the IDE, navigate to the end of the wind farms module and add a dockblock for the form definition; Implements, hook_form, then same as the function name I discussed in the previous segment, create a new function named function windfarms_admin_settings_form.
Looking at the hook form API, I can see the hook_form takes two parameters. The first, the node that is being added or edited. In this context, it won't be used as I'm not actually editing a node. The second, pass by reference, is form_state. Form state passes data about the processed form between steps. Form state is passed by reference to allow its contents to be manipulated and passed between the form creation, validation, and submission.
Next, add a variable named form and set its value to an empty array. The name of the variable is technically unimportant, but it's best practice to name variables based on what their contents are. Add a return statement for the form variable at the end. As it's easy to forget to return the form definition after adding a number of form elements, return form. The first form element I will add is the Drupal form APIs default type markup.
Markup elements generate HTML to display inside a form. As they are default, there is no need to explicitly set a form element as markup. I will use a markup form element to give an administrative user an overview of the purpose of this form. Form elements are added to the form array using the unique name for data to be processed. Think of it as the name attribute in an HTML input and follow the same naming conventions, the value will be an empty array; form overview = array.
The form API uses hash tag names for the parameters to allow Drupal to differentiate between a nested form element and interactions with the API. The only parameter needed for a markup element is hash tag markup. The value should be wrapped in a t function; markup t 'This interface allows administrators to manage general Wind Farm Settings'. There is no restriction on the content that can be added to the markup element.
Prefix is the paragraph tag and suffix is the closing paragraph tag. Typically HTML tags should be avoided in the form definition to allow a separation of logic and design. Instead it should be done in the theme. As theming really isn't covered in this course, and theming does interact with the form API, I feel it's worth demonstrating. Now that the purpose of the page has been given I will add the first functional element; a check box for toggling a Google map that will be created later.
I will use the key wind farms g map for the name of the element to be consistent with how it will be saved in the database. The first parameter will be the title. This will be displayed on the form typically before the element itself. The value of the title should be wrapped in the t function, Enable Google Maps. Next a description, I like to think of this as context and help to the user. Again, the value will be wrapped in the t function, description t, 'When enabled, Google Maps will be rendered if latitude and longitude are known'.
As this form element needs to be a check box, I will need to set the type to specify how this element is handled and rendered; type checkbox. Finally, I will specify a default value. The default value is initially set the first time the form is accessed. If the form fails validation, then the user specified value is used in its place. As the check box is binary, meaning it's on or off, the default value should be set as the integer zero or one.
In a later segment, the value for these settings will be loaded from the database. So for the time being I will just default the element to being on; default_value 1. I'm going to enclose the next form elements in the field set. Field sets are used to give context and group form elements which improves usability and accessibility. The end result will be a nested array, with a field set definition at the head with the individual form elements as children.
The form elements I'm going to put in this field set are the default map center location, so I will name this field set default_center. Form 'default_center' is an array with the title Map Center, a description ('Location of the center of the map of wind farms.') then the type is fieldset.
Field sets have an additional property, the ability to collapse the display. This is optional but can be especially useful when working with a large form. By default, the collapsible property is disabled. To enable it, set it to Boolean true; collapsible set to TRUE. The next property is the default collapse state. I will set it to Boolean false to have it open by default, collapsed set to FALSE.
Now that the fieldset has been defined, I can add the individual form elements within it using nesting. I'm going to use the text field form element for the latitude and longitude of the map center. For the default value, I will use the coordinates of a wind farm in upstate New York, which is close to my office. Form 'default_center' 'windfarms_default_center_lat' = array and the title of (Latitude) and the description.
I'll describe the expected format, Signed degrees format DDD.dddd. Close the parenthesis. The type is textfield and the default value is 42.91455 as it's important to know where the center of the map is, I'm going to make these form elements required; required TRUE.
Next, I'll do the longitude. I am going to paste to save some time, call it default_center_long, for the title Longitude, and for the default_value -75.569851. The last setting I will capture is the default zoom of the Google Maps map. The Google maps API zoom level is between 0 and 20.
As this is a closed domain of options, I will use a select list as the form element. Select list take an additional parameter, the options, which is an array where the keys is the value which in turn maps to the label to be shown to the user. While the option array can be defined in line with the form element, sometimes it's necessary to build the options outside of the form element and pass a variable containing the array of options. I'm going to build the options rate using range to define the elements 0 through 20 then overwrite some individual keys with specific values to give context to the user.
Options = range, the range of 0 to 20 in one step increments, options 0, it's going to be 0 Furthest for some context, and options 20 t 20 Closest. Now that the values are defined, I can specify the form element. Form windfarms_default_gmap_zoom = array, the title of Google Map zoom, and a description default level of zoom between 0 and 20.
For type I will specify select. Options will be set to the options array. The default_value, I will set at 8 and required is TRUE. I'm almost done with the form. One final element is necessary, a submit button. No special name is required for the name, so I will just name it submit; form submit = array, it'll be type.
Only one additional key is required, the value, which will be what is shown as the label to the user, Save. Save the module then return to the browser and navigate to the Wind Farms Settings page. Instead of the error, the newly created form will be shown. If any of the form elements are missing go back and check the types first. The Drupal form API is much more likely to do nothing than to throw a visible error. While I've not specified either form validation or submission handling it, the use of the required fields has been automatically implemented.
Remove the value for latitude and longitude then click Save. You will see a generic error message for each form element that was required. Latitude fields required is required, and so forth, and the elements that is errored is highlighted in red. In the next segment, I will describe how to customize these error messages and provide more granular validation, then how to handle a validated submission.