Your SlideShare is downloading. ×
Drupal Lightning FAPI Jumpstart
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Introducing the official SlideShare app

Stunning, full-screen experience for iPhone and Android

Text the download link to your phone

Standard text messaging rates apply

Drupal Lightning FAPI Jumpstart

6,139
views

Published on

Session presented 10/24 at Pacific Northwest Drupal Summit discussing Drupal form API

Session presented 10/24 at Pacific Northwest Drupal Summit discussing Drupal form API

Published in: Technology, Business

1 Comment
4 Likes
Statistics
Notes
No Downloads
Views
Total Views
6,139
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
97
Comments
1
Likes
4
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide
  • If this doesn't look familiar to you, then you're in for a rough hour. Use t() for titles, descriptions, and values Weight specifies relative vertical location of element on the form canvas D6; use “Save” instead of “Submit” (#287986) Don't forget to return your form!
  • drupal_get_form Primary way of invoking forms Manages all phases of form lifecycle Returns rendered html Can be parameterized to pass default values D7: returns form array, not HTML drupal_execute Programmatic counterpart to drupal_get_form Pass in form_id and $form_state['values'] Returns validation errors D7: becomes drupal_form_submit
  • Easiest way to alter a form Can alter your own or core/contrib forms Called on every form invocation Called after form creation but before rendering Also hook_form_FORM_ID_alter() for specific Remains in Drupal 7 Also exp
  • Goal: match a form's appearance to its context
  • Transcript

    • 1. Lightning (It's gonna be fast paced) FAPI (Expect lots of code) Jumpstart (Feel the power!) Doug Stumberger drupal.org = twitter = facebook = ‘dougstum’ DND Communications www.raceonedesign.com dstumberger @ hotmail.com
    • 2. It's a Drupal talk, so… * * No cats were harmed in the creation of these slides.
    • 3. Don't recognize this? Uh-oh.
    • 4. Something simple to begin with
      • function newsletter_subscribe_form ( $form_state ) {
      • $form = array();
      • $form['email'] = array(
      • '#type' => 'textfield',
      • '#title' => t('E-mail address') , // use t() for localization
      • '#size' => 64,
      • '#maxlength' => 64,
      • '#required' => TRUE,
      • '#weight' => -10,
      • );
      • $form['subscribe'] = array(
      • '#type' => 'radios',
      • '#title' => t('Subscribe'),
      • '#default_value' => 0 ,
      • '#options' => array(t('Yes'), t('No')) ,
      • '#weight' => 5,
      • );
      • $form['submit'] = array(
      • '#type' => 'submit',
      • '#value' => t(' Save '), // D6; use “Save” instead of “Submit” (#287986)
      • '#weight' => 20,
      • );
      • return $form;
      • }
    • 5. Validate & submit
      • Validate
        • Default handler: FORMID_validate ($form, &$form_state)
        • Modify $form['#validate'] array in form definition or hook_form_alter
        • Use form_set_error to signal validation errors associated with the form
        • Also useful: form_get_error($element) and form_get_errors()
        • Required fields are handled automatically, no need to check in validate handler
      • Submit
        • Default: FORMID_submit ($form, &$form_state)
        • Modify $form['#submit'] array in form definition or hook_form_alter
        • Particularly useful for redirecting the user (default is to return to form)
        • drupal_goto ($path = '', $query = NULL, $fragment = NULL, $http_response_code = 302)
        • // Block redirection (returns to form)
        • $form_state['redirect'] = FALSE ;
        • // Redirect to site homepage
        • $form_state['redirect'] = '<front>' ;
        • // Redirect to relative path from base_path
        • $form_state['redirect'] = 'node/' . $node->nid ;
        • // Specify arguments to drupal_goto()
        • $form_state['redirect'] = array($node->path, drupal_get_destination()) ;
    • 6. Passing data to validate & submit
      • Pass values from form definition to validate and submit handlers
        • $form['node'] = array(
        • '#type' => 'value' ,
        • '#value' => $node ,
        • );
      • Once form is submitted, retrieve data from $form_state['values']
        • function subscribe_form_validate($form, & $form_state) {
        • $node = $form_state['values']['node'] ;
        • };
      • Pass values (or modify passed values) from validate to submit handler
        • function subscribe_form_validate($form, & $form_state) {
        • $form_state['values']['node'] ->title = t('Hello World');
        • };
        • -- OR –-
        • function subscribe_form_validate($form, & $form_state) {
        • $node = $form_state['values']['node'];
        • $node->title = t('Hello World');
        • form_set_value ($form['node'], $node, $form_state);
        • };
      • Once form is submitted, retrieve data from $form_state['values']
      • $node = $form_state['values']['node'] ;
    • 7. Fieldsets and trees (and access!)
      • Collapsible groupings of form fields
        • $form['options'] = array(
        • '#type' => ' fieldset ',
        • ' #access ' => user_access('administer nodes') ,
        • '#title' => t('Publishing options'),
        • ' #collapsible ' => TRUE, // Default is False.
        • ' #collapsed ' => TRUE, // Default is FALSE.
        • '#tree' => FALSE,
        • );
        • $form['options']['status'] = array(
        • '#type' => 'checkbox',
        • '#title' => t('Published'),
        • '#default_value' => $node->status,
        • );
      • Default: values are “collapsed” when #tree = FALSE
        • function mymodule_form_submit($form, &$form_state) {
        • $status = $form_state['values']['status'] ;
        • };
      • But when '#tree' = TRUE, you must specify complete path to value
        • $status = $form_state['values']['options']['status'] ;
    • 8. Tabular forms
      • Widely used for admin forms
      • function request_form($form_state){
      • $form = array();
      • foreach ($requests as $request_id => $request) {
      • $form['request'][$request_id]['#tree'] = TRUE;
      • $form['request'][$request_id]['delete'] = array(
      • '#type' => 'checkbox',
      • '#default_value' => 0,
      • );
      • $form['request'][$request_id]['user'] = array(
      • '#value' => theme('username', user_load(array('uid' => $request->uid))),
      • );
      • $form['request'][$request_id]['time'] = array(
      • '#value' => date('j M H:I', $request->timestamp),
      • );
      • $form['request'][$request_id]['operations'] = array(
      • '#value' => l('Add article', 'newsletter/add_article', 'id=' . $request_id)
      • );
      • }
      • }
    • 9. Tabular forms: theme
      • Use form theming to convert into table markup
      • function theme_request_form($form) {
      • $headers = array(
      • '',
      • t('User'),
      • t('Time'),
      • t('Operations')
      • );
      • $rows = array();
      • foreach ($form['add'] as $request_id => $request) {
      • $rows[] = array(
      • drupal_render ($form['request'][$request_id]['delete']),
      • drupal_render($form['request'][$request_id]['user']),
      • drupal_render($form['request'][$request_id]['time']),
      • drupal_render($form['request'][$request_id]['operations']),
      • );
      • }
      • return '<h3>' . t('Requests') . '</h3>'
      • . theme('table', $headers, $addrows)
      • . drupal_render($form);
      • }
      • Remember, FAPI will automatically look for theme_FORMID
    • 10. Custom elements
      • Define a custom element with fields to be merged
      • function fcklite_elements () {
      • return array('fcklite_textarea' => array(
      • ' #input ' => TRUE,
      • '#prefix' => &quot;<div class='fcklite_textarea'>&quot;,
      • '#suffix' => '</div>',
      • ' #rows ' => 20,
      • ));
      • }
      • Theme your element (remember to add to hook_theme)
      • function theme_fcklite_textarea ($element) {
      • return theme('form_element' , array(
      • '#title' => $element['#title'],
      • '#description' => $element['#description'],
      • '#id' => $element['#id'],
      • ),
      • FCKeditor_IsCompatibleBrowser()
      • ? fcklite_open_textarea($element['#id'], $val, $element['#rows'])
      • : t('Incompatible browser')
      • );
      • }
      • D7: hook_elements -> hook_element_info and hook_element_info_alter (#572932)
    • 11. A custom fckeditor element
    • 12. Invocation: drupal_get_form
      • Primary means of invoking Drupal forms programmatically
      • Manages building, theming, presentation, execution
      • Returns HTML (at least in Drupal 6!)
        • $html = drupal_get_form('newsletter_subscribe_form');
        • drupal_set_content(‘column_1’, $html);
      • Frequently used as a page callback for hook_menu items
        • function newsletter_menu () {
        • $items['newsletter/subscribe'] = array(
        • 'page callback' => 'drupal_get_form',
        • 'page arguments' => array(' newsletter_subscribe_form '),
        • 'access arguments' => array('manage subscriptions'),
        • );
        • return $items;
        • }
    • 13. drupal_get_form: default values
      • Use your hook_menu wildcards and loaders!
        • function newsletter_menu () {
        • // Note: %user wildcard loader = user_load
        • $items[' newsletter/subscribe/%user '] = array(
        • 'page callback' => 'drupal_get_form ',
        • 'page arguments' => array('subscribe_form', 2) ,
        • 'access arguments' => array('manage subscriptions'),
        • );
        • return $items;
        • }
      • Remember to use $form_state as first parameter (almost always!)
        • function newsletter_form($form_state, $objUser ) {
        • $form = array(
        • 'email' => array(
        • '#type' => 'textfield',
        • '#title' => t('Email address'),
        • '#default_value' => $objUser->mail ,
        • ));
        • };
    • 14. Add/edit forms for node types
      • Use hooks to change default behavior for two URLs
        • Create URL: www.example.com/node/add/TYPE
        • Edit URL: www.example.com/node/%node/edit
      • Replace standard node create/edit forms
        • Define function TYPE_node_form($form_state, $node)
        • Use node_form_validate & node_form_submit
        • OR customize by specifying $form['#submit][] or $form['#validate'][]
        • To theme, either use default: theme_node_form
        • OR implement theme_TYPE_node_form (don't forget to add to hook_theme)
      • Or add custom fields to standard node create/edit forms
        • Only works with node types defined by a module (not through admin)
        • Implement hook_form ($node, $form_state) // NOTE REVERSED ARGUMENTS
        • Uses node_form_validate & node_form_submit
        • Themed using theme_TYPE_node_form or theme_node_form
    • 15. Form factories using hook_forms
      • Multiple modules can use the same form function
      • Each invocation can use a different form_id
      • Modules can alter submission, validation etc.
      • function node_forms() {
      • $forms = array();
      • if ($types = node_get_types ()) {
      • foreach (array_keys($types) as $type) {
      • $forms[$type .'_node_form']['callback'] = 'node_form';
      • }
      • }
      • return $forms;
      • }
      • Calls to TYPE_node_form will result in calls to function node_form(...) with form_id = TYPE_form
    • 16. Modifying forms: hook_form_alter
      • function newsletter_form_alter( &$form , $form_state, $form_id) {
      • // Switch statement is also commonly used.
      • if ($form_id == 'newsletter_subscribe_form') {
      • // Add elements to the form.
      • $form['name'] = array(
      • '#type' => 'textfield',
      • '#title' => t('Name') ,
      • );
      • // Remove elements from the form.
      • unset($form['phone_number']);
      • // Add a new submit handler (or replace altogether).
      • $form['#submit'][] = 'mymodule_submit_handler';
      • // Replace existing validation with your code (or add validation).
      • $form['#validate'] = array('mymodule_validate_handler');
      • // Move form items around using weights.
      • $form['email']['#weight'] = -3;
      • // Fix tabindex problems, add prefix/suffix, etc.
      • $form['submit']['#attributes']['tabindex'] = '3';
      • }
      • }
      • See for example login_toboggan.module
      • Also explore: #after_build, #pre_render, #post_render
    • 17. Theming forms & elements
      • Goal: match the look of a form to its design or layout context
      • Leave markup the same but modify the CSS ( Use FIREBUG!!! )
      • Use #prefix and #suffix fields on a form or on an individual element. Especially useful when set using hook_form_alter.
        • $form['access'] = array(   '#type' => 'textfield',   '#title' => t('Log'),   ' #prefix ' => '<div class=&quot;inline_title&quot;>',    '#suffix ' => '</div>', );
      • Override the default individual element theme functions
        • function MY_THEME_NAME_radio($element) { ... }
        • A default theme function exists for every visible element type
        • But, this overrides the theming for every element of that type on every form
        • Can also override theme_form and theme_form_element !
    • 18. #theme…the hammer, er, scalpel
      • Specify theme function on whole form or individual element
        • $form = array(
        •    ' #theme ' => 'table_based_form',
        • );
      • Make sure the theme function is defined in hook_theme
      • Remember, you can add '#theme' during hook_form_alter
      • The usual algorithm for theming forms:
        • Loop through all elements in a form
          • Output the element's surrounding markup, description, etc.
          • Render the element with drupal_render($form[$element_id])
        • Render the remainder of the form with drupal_render($form)
      • You can pass parameters into the theming using custom form elements and custom '#' fields on individual elements
      $form['fb_fields'] = array( '#type' => 'value', '#value' => array('title', 'body'), ); $form['sender_email'] = array( '#type' => 'item', '#fb_divider' => TRUE, );
    • 19.  
    • 20. Here, a standard imagefield form becomes both more functional and more consistent in presentation with the rest of the site. Combining form_alter and theming gives you the power to dramatically modify contrib module forms, icluding CCK-based forms. (after_build helps!)
    • 21. Multi-step forms
      • Multi-page surveys, wizards, notifications, etc.
      • First, conditionalize UI elements, especially for form navigation
      • f unction newsletter_subscribe_form($form_state) {
      • $form = array();
      • // Second time through the form builder.
      • if (isset($form_state['storage']['confirm'])) {
      • $form['confirm_message'] = array(
      • '#type' => 'item' ,
      • '#value' => t('Email address !email is subscribed.',
      • array('!email' => $form_state['storage']['email'])),
      • );
      • }
      • // First time through the form builder.
      • else {
      • $form['email'] = array(
      • '#type' => 'textfield',
      • '#title' => t('E-mail address'),
      • );
      • $form['submit'] = array(
      • '#type' => 'submit',
      • '#value' => t('Save'),
      • );
      • }
      • return $form;
      • }
    • 22. Cycle data into next step: form_state
      • Pass data from one step to the next
        • Finally, a purpose for $form_state in form definition signature!
        • Use $form_state['storage'] in submit handler for signals, data
      • function newsletter_subscribe_form_submit($form_state) {
      • // signal that the next form building shows confirmation message.
      • $form_state['storage']['confirm'] = 1;
      • // Pass the email address (assume validated) back to form build.
      • $form_state['storage']['email'] = $form_state['values']['email'];
      • // trigger a form rebuild.
      • $form_state['rebuild'] = TRUE;
      • }
      • Signal rebuild of form or redirect in submit handler
        • $form_state['rebuild'] = TRUE;
        • Optional redirect on final step
    • 23. Drupal 7 Changes
      • drupal_get_form returns structured array, not HTML
      • Specify text format in textarea and textfield elements
      • Attach CSS & JS to forms
      • Javascript element dependency (#557272)
        • $form['element']['#dependencies'] => array(   'enabled' => array(     '#edit-menu-has-menu' => array('checked' => TRUE)   ) ),
      • Use '#markup' not '#value' for elements markup & item (#252013)
      • drupal_execute => drupal_form_submit (#408024)
      • Easier to check for node form in hook_form_alter (#161301)
      • Changes to element definitions re: theming (#355236)
      • hook_elements -> hook_element_info (#572932) + hook_element_info_alter
    • 24. D7: drupal_get_form (#353069)
      • drupal_get_form returns form definition array, NOT HTML
          • D6: $html = drupal_get_form('mymodule_form');
          • D7: $arrayForm = drupal_get_form('mymodule_form');
      • Array structure should be passed back to menu handler
        • All menu handlers should return arrays, not HTML
        • Arrays are passed through new page building stack
        • See hook_page_build for rendering
      • Do not do this!
        • $html = drupal_render(drupal_get_form('mymodule_form'));
      • Can now modify form structure in hook_page_alter but use hook_form_alter just as you do in D6
    • 25. D7: Specify text format (#125315)
      • Specify text format for either textfield or textarea elements
      • $form['comment'] = array(
      • '#type' => 'textarea',
      • '#title' => t('Comment'),
      • // Value from {filter_format}.format or filter_fallback_format() etc.
      • ' #text_format ' => filter_default_format() ,
      • );
      • Form structure is split into value and format sub-arrays
      • // Stores the actual textarea element in subarray
      • $form['comment'][' value ']
      • // Create format picker using filter_form(...) with default
      • $form['comment'][' format ']
      • Submit handler retains split between value and format
      • // Unaltered form element value
      • $comment_body = $form_state['values'][' comment '];
      • // Text format (original value specified in #text_format field)
      • $comment_format = $form_state['values'][' comment_format ']
    • 26. D7: Attach CSS & JS (#323112)
      • Attached CSS and Javascript directly in form definition
      • function mymodule_form() {
      • $form = array();
      • // Attach a css file.
      • $form['#attached']['css'] = array(
      • drupal_get_path('module', mymodule') . '/mymodule.css'
      • );
      • // Attach Javascript 'file', 'inline', 'external' or 'setting'
      • $form['#attached']['js'] = array(
      • // Inline Javascript.
      • 'alert(&quot;Hello World.&quot;);' => array('type' => 'inline'),
      • );
      • return $form;
      • }
    • 27. Resources
      • API Reference
        • http://api.drupal.org/api/drupal/developer--topics--forms_api_reference.html/6
      • Quick start guide
        • http://api.drupal.org/api/drupal/developer--topics--forms_api.html/6
      • Tree and parents
        • Tree and parents: http://drupal.org/node/48643
      • Upgrading modules to D7
        • Upgrading to D7: http://drupal.org/update/modules/6/7
      • Maybe useful contributed modules
        • form_builder: Nate Haug's GUI-based form layout tool
        • Webform: one-off forms such as surveys created as nodes
        • Multistep: add multistep forms to CCK
        • Formblock: forms in blocks
        • Formfilter: form editing/filter including formfilter_filter_form API
        • MerlinofChaos ctools: multistep, AJAX (rumor moving into D7 core?)
        • skip_validation (may provide solution for Cancel problem)
        • Fieldset_helper: Saves the collapsed state of a collapsible fieldset
    • 28. Appendix
    • 29. AHAH
      • “ Asynchronous HTML and HTTP” : Javascript-based dynamic behavior similar to AJAX
      • “… the response from the request is used directly without parsing on the clientside”
      • See poll & upload modules for core samples
        • $form['choice_wrapper']['poll_more'] = array(     '#type' => 'submit',     '#value' => t('More choices'),     '#description' => t(“click here to add more choices.&quot;),     '#weight' => 1,     '#submit' => array('poll_more_choices_submit'), // If no javascript action.     ' #ahah ' => array(       'event' => 'click',       'path' => 'mymodule/js',       'wrapper' => 'myform-wrapper',       'method' => 'replace',       'effect' => 'fade',       'progress' => array(         'type' => 'bar',         'message' => t('Loading...')     )
      •  http://drupalsn.com/learn-drupal/drupal-tutorials/getting-going-ahah-and-drupal-6
    • 30. drupal_execute
      • // See user.module, line 2387.
      • function user_register () {
      • ...
      • }
      • // http://api.drupal.org/api/function/drupal_execute/6
      • // register a new user
      • $form_state = array();
      • $form_state['values']['name'] = 'robo-user';
      • $form_state['values']['mail'] = 'robouser@example.com';
      • $form_state['values']['pass'] = 'password';
      • $form_state['values']['op'] = t('Create new account');
      • drupal_execute ('user_register', $form_state);
    • 31. http://drupal.org/node/165104