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

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Drupal Lightning FAPI Jumpstart

6,165

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,165
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

    ×