Drupal 6 Forms API Screencast

vordude's picture

This is a very introductory look at Forms API in Drupal 6.

The focus is on basic and practical application, and should allow the newest Drupal programmer to be able to go create his or her own form, in Drupal 6.

Attached is the code for your perusal--It's the same as what's demonstrated in the video, with a bunch of comments. (I would have attached a tarball, but it was not an acceptable file format)

The 30-minute screencast is hosted at blip.tv.

NOTE: In the video, I fail to use check_plain() when outputting some user-generated content...For the sake of you and your web site, don't forget this step. The attached code example is i believe doing it right

Corrections, Questions, Comments, and Smart Remarks are always welcome. :-)

AttachmentSize
form_intro-module.txt3.97 KB
form_intro-info.txt270 bytes

Comments

Thanks!

Manuel Garcia's picture

For those with slow computers, or if you want to save it and watch it later:

MPEG4 Video (.mp4):
http://blip.tv/file/get/Vordude-IntroToDrupal6FormsAPI891.MP4

Flash Video (.flv) :
http://blip.tv/file/get/Vordude-IntroToDrupal6FormsAPI891.flv

Thanks so much for putting the time into this!

Thanks for a great

nadam's picture

Thanks for a great introduction.

I wish someone could update the Forms API Quickstart Guide too. I'm not sure how to do it since it's not a Wiki-page.

Thanks

chucklima's picture

Your screencast and code are very helpful.

Great tutorial

patrickfrickel's picture

Hi,
Your screencast is logical, welll structured and VERY helpful....I have looked all over to come across this...indespensible....there is a real need for this level of drupal tutorial for the non-ninja developers!

Newbie Question

nanatulip's picture

If I have already created my database fields in CCK. I have watched your tutorial & the API Forms tutorial, yet I still can not figure out how I indicate which textfield should update which database field?
Tami

Tami

You simply rock!!!!!

jomski's picture

I've been going almost bald (from hair pulling) trying to understand the FAPI. This is by far the best introductory tutorial I have found and very very helpful.
Thanks for the great effort.

Jome.

yes this is superb

djax80's picture

this really is good - even though everyone says drupal is easy to update - people need different methods of explanation to help them get started

more screencasts would be most welcome

many thanks

djax

Question: URL alias duplication issue from $items ['quiz']?

djax80's picture

hi

this isnt a criticism - just a question - your tutorial is superb and i would be nowhere without it

i noticed you accessed the page where the form is displayed at www.example.com/quiz

and that the 'quiz' part of the url is created from the $items ['quiz'] array key below

function form_intro_menu () {
$items = array ();
$items ['quiz'] = array (
'title' => t('Super Cool Quiz'),
'page callback' => 'quiz_page' .............etc

thats great - but i then created a normal page and gave it the url alias of 'quiz' - thus creating a conflict (normally if you try to add a url alias that is already in use drupal flags this up and does not allow the page to be created)

the new 'quiz' page then overides the form 'quiz' page - and it is no longer possible to navigate to the form 'quiz' page

again - this isnt a criticism - i just noticed it when trying to figure out which page the form would display on - i wondered if there was a 'path_alias' key in hook_menu, so it could be set something like

function form_intro_menu () {
$items = array ();
$items ['quiz'] = array (
'title' => t('Super Cool Quiz'),
'page callback' => 'quiz_page',
'path_alias' => 'quiz', .............etc

but 'path_alias' or an equivalent does not seem to exist at http://api.drupal.org/api/function/hook_menu/6

do you have any ideas on this?

djax

One Question

marcus45's picture

First of all cool tutorial, extremely helpful. One problem I'm having is getting my form to submit. I'm writing an e-commerce page using ubercart. The form I created is supposed to add a product to cart. Here is what I have so far.

function custom_add_to_cart()
{
return drupal_get_form('uc_custom_cart');
}

function uc_custom_cart_form($form_state, $args)
{
$form = array();
$form['upgrade_option'] = array (
'#id' => 'upgrade_option_value',
'#type' => 'hidden',
'#default_value' => '-1',
);
        $form['balloon_bouquet'] = array (
'#id' => 'balloon_bouquet_value',
'#type' => 'hidden',
       '#default_value' => $args['bouquet_id'],
        );
        $form['additional_latex'] = array (
'#id' => 'additional_latex_value',
'#type' => 'hidden',
'#default_value' => '-1',
);
$form['additional_mylar'] = array (
'#id' => 'additional_mylar_value',
'#type' => 'hidden',
'#default_value' => '-1',
);
$form['add_teddy_bear'] = array (
'#id' => 'add_teddy_bear_value',
'#type' => 'hidden',
'#default_value' => '-1',
);
$form['submit'] = array (
'#type' => 'submit',
'#attributes' => array('class' => 'add_to_cart_btn'),
);
$form['#submit'][] = 'uc_custom_cart_form_submit';
return $form;
}

function uc_custom_cart_form_validate($form_id, &$form_state)
{

}

function uc_custom_cart_form_submit($form_id, &$form_state)
{
print("Form as been submitted");
       //$form_state['redirect'] = "/";
}

I'm using the following to render the form, in my node-product.tpl.php file.
<?php
     $args
= array('bouquet_id' => "$node->nid");
     print
drupal_get_form('uc_custom_cart_form', $args);      
?>

Could anyone help or point in the right direction, I'm really stuck on this one.

Thanks

I have been looking for this

rwkarsch's picture

Thank you, thank you, thank you. Very helpful. Somehow, videos are much easier then books or documentation. A great starting point. Any plans on a part 2 - interacting with your database?

Hi, Great tutorial!

Summit's picture

Hi,
Great tutorial! absolutely a learning experience!
Would love a tutorial like this showing form - to node save.

greetings,
Martijn

good tutorial, but very important to note that...

mummra1's picture

When creating a form element such as a 'select' or 'radios', that has elements, you must explicitly use associative arrays when creating the #option values if you want the value attributes of the option tag to not be integers, such as 0, 1, 2, etc.

So instead of

#options => array(
t('First Option'),
t('Second Option')

);

....use

#options => array(
'first' => t('First'),
'second' => t('Second')

);

Question on next step

cbearhoney's picture

Thanks to this tutorial I have a working form in my module. Now, once I do some database calls in quiz_form_submit, how do I get the results to display on the same page below the form? Do I use hook_view? hook_load? Any links to examples would be swell!

i am also having the same doubt

dewndrizzle's picture

how do we retrieve or view the form tats once submitted...wat link should be given to redirect to tat page whr all details tat were submitted could be seen.... can any example be given???plz help............

Trying to write to a db

quindio's picture

I am trying to put the values to the DB but it is not working for me. I am doing an INSERT query on the quiz_form_submit as follows:

function quiz_form_submit ($form_id, &$form_state) {
$id = 1;
$name = $form_state['values']['name'];
$toast = $form_state['values']['toast'];

$result = db_queryd("INSERT INTO {form_intro}(id, name, toast) VALUES (%d, '%s', '%s')", $id, $name, $toast);

if ($result ) {
  dsm($id, $name, $toast);
  $output = "Hi " . $name . ", Do you like toast?" . $toast;
  $output .= t(' this info was added to the Database.');
} else {
  dsm("DB write FAILED<br>");
}
//return db_result($result);

}

All I get is a blank page :-(

This is my install:

function form_intro_schema(){
$schema = array();

$schema['form_intro'] = array(
'description' => t('Contain the form intro DB part.'),
'fields' => array(
'id' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'description' => t('Form intro record id'),
),

  'name' => array(
'type' => 'char',
'length' => 40,
    'not null' => TRUE,
    'description' => t('The name of the user'),
  ),

  'toast' => array(
    'type' => 'int',
'length' => 1,
    'not null' => TRUE,
    'description' => t('Do you like Toast?'),
  ),
),
'primary key' => array(
   'id',
),

);

drupal_set_message($schema);
return $schema;
}

/**
* Implementation of hook_install
* perform initial setup database tasks.
*/
function form_intro_install() {
if (!db_table_exists('form_intro')){
drupal_install_schema('form_intro');
}
}

/**
* Implementation of hook_unistall().
*
* Perform initial clean-up tasks.
*/
function form_intro_uninstall() {
if ( db_table_exists('form_intro')) {
drupal_uninstall_schema('form_intro');
}
}

OK, I was able finally to add

quindio's picture

OK, I was able finally to add the record to the database.

My problem was that I was using db_queryd to insert a record but in drupal 7 you need to use
db_insert to insert a record:
$result = db_insert('form_intro')->fields(
array(
'id' => 1,
'name' => $name,
'toast' => $toast,
)
)->execute();

My first ever insert to a database works now....wohooo...

blip.tv mp4 doesn't play

esod's picture

Thanks very much for the excellent screencast! I was able to download the flv file. Once that file was processed with Handbrake into an mp4 file, we were able to watch it on our PCs.

The newly processed mp4 file is 150mb. Please let me know if you would like me to upload the mp4 file to some location in order to correct the issue on blip.tv.

vlc

whatdoesitwant's picture

I can open most .flv files with Videolan's VLC and appreciate it above Windows Mediaplayer. It's open source and respects your privacy.

Help with my next step

blue92877's picture

Thanks to this excellent tutorial, I created my first form and my first module!

My question is, how do I display the form on any page I want? Here is what I've done so far.

I have a classified ads site. I created the module so that I could include a small contact form in the node of every single node page in the site. However I can't get it to display.

Here is what I need.
1) show my form on every single page I want to show it on.
2) I need the form to work and be visible even if the user is anonymous.

Here is how I was trying to accomplish this.
1) create a module with the form, which I did successfully, and it shows up and works fine on the page it's supposed to.
2) I wrote a function called display_form() which simply returned drupal_get_form(contact_poster_form)
3) I put the function on the pages I needed it to display on, however, it will not show up.

Can someone please tell me what I'm doing wrong?
the following is the function I have in my template.php file, and the one I am adding to my templates:

function display_contact_form() {
return drupal_get_form(contact_poster_form);
}

the following is my code for the contact_poster.module:

function contact_poster_menu() {
$items = array();

$items['contact_poster'] = array(
'title' => t('Contact Poster Module'),
'page callback' => 'contact_poster_page',
'access arguments' => array('access content'),
'type' => MENU_SUGGESTED_ITEM,
);

return $items;
}

function contact_poster_page() {
return drupal_get_form('contact_poster_form');
}

function contact_poster_form($form_state) {
$form = array();

$form['email'] = array(
'#type' => 'textfield',
'#title' => t('Email'),
'#required' => TRUE,
);

$form['message'] = array(
'#type' => 'textarea',
'#title' => 'Message',
'#rows' => 4,
'#required' => TRUE,
);

$form['nodetitle'] = array(
'#type' => 'hidden',
'#value' => $node->title,
);

$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Send',
);

return $form;
}

function contact_poster_form_validate($form_id, &$form_state) {
//check if the email field is valid
if(isset($form_values['values']['email'])) {
if (!valid_email_address($form_values['values']['email'])) {
form_set_error('email', t('The e-mail address you specified is not valid.'));
}
}
}

function contact_poster_form_submit($form_id, &$form_state) {
$author = user_load(array('uid' => $node->uid));
$to = $author->mail;
$from = $form_state['values']['email'];
$body = $form_state['values']['message'];
$nodetitle = $form_state['values']['nodetitle'];

drupal_set_message(t('Your message has been successfully submitted.'));

$message = array(
'to' => $to,
'subject' => "Contact from your ad titled {$nodetitle}",
'body' => $body,
'headers' => array('From' => "{$from}"),
);

drupal_send_mail($message);

}

Menu or Blocks

jefkin's picture

Hi blue92877,

It sounds like what you're after is a block (which I'm sure deserves it's own dojo moment :D)

The easy way to extend your module and create a block is as follows:

<?php
/*
** contact_poster_block() - hook_block; builds our block components.  In this
**                          case we're defining one block to use.  And we'll
**                          use the same form function you already wrote.
**  @param $op mixed    - Defined by drupal controling our action, one of
**                        'list', 'configure', 'save', 'view'
**  @param $delta mixed - Delta is the unique block specifier, to control
**                        which block is to be retrieved or operated on.
**                        Ignored when $op is 'list'.  For this module,
**                        $delta   block             
**                        contact  Contact Form  
**  @param $edit assoc  - Holds the form data from a configurattion form if
**                        the $op is 'save'. Ignored otherwise.
**     
**  @return $block mixed  - if $op is 'list': Array describing our Blocks,
**                          but if $op is 'configure' a confgureation form
**                          API array, when $op is 'save' return nothing,
**                          and if $op is 'view' return a array with the
**                          block configuration used by Themes.
**
*/
function contact_poster_block($op = 'list', $delta = 0, $edit = array())
{
 
$block = array ( );

  if (
'list' == $op)
  {
   
$contact  = array ( 'info'  =>t('Contact Poster')
                    ,  
'weight'=>0
                   
,   'cache' =>BLOCK_CACHE_PER_PAGE
                   
, );
   
$block  = array ( 'contact' =>$contact
                 
, );
  }
  elseif (
'configure' == $op) // NO-OP at the moment
 
{
  }
  elseif (
'save' == $op) // Another NO-OP -- no configuration
 
{
  }
  elseif (
'view' == $op)
  {
    switch(
$delta)
    {
      case
'contact':
      {
       
$content  = contact_poster_page();
        break;
      }
    }
   
$block  = array ( 'content' =>$content
                 
, );
  }
  return
$block;
}
?>

This should give you a block called Contact Poster which you will find in your site's Block administration /admin/build/block.

You will be able to move it around to the page region you want it, and configure it to display on any pages you want.

HTH! Good luck!

Jefkin, I am working on

jsfour's picture

Jefkin, I am working on adapting your code for my own solution, however, for some reason I cannot get the block to show up in the "blocks" section of the site building menu... My guess is that there is something wrong with my declaration function below:

<?php
// $Id: $

function questionsblock_block ($op = 'list', $delta = 0, $edit = array()){

$block = array();

if ($op == 'list'){
//this makes the listing
$question = array ( 'info' => t('Question'),
'weight' => 0,
'cache' => BLOCK_CACHE_PER_PAGE ,
);
$block = array ( 'question' => $question, );
}//if
else if ($op == 'view') {
//stick the form here
}//elseif

}//block

Is the above right?

Better late then never?

jefkin's picture

jsfour, I have no idea why I didn't see your response before, but I see it now ... (yes I know almost a year late)

Anyway, if, by chance, you still have the bug :) ... you were missing the return statement, which was why you weren't getting the block showing up.

Likely, you already know this, but just in case, I figured I'd answer.

Again, a thousand apologies for not responding way, way sooner.

Jeff

Minor corrections and you're good to go

esod's picture

Coder (http://drupal.org/project/coder) -- a must have module for developers -- found a dozen or so minor errors like this in form_intro-module.text:

Line 23: Functions should be called with no spaces between the function name and opening parentheses

$items = array ();

Those errors were easily corrected and your module became a "suggested" menu item.

Then, by adding hook_block implementation(), a block was made available to Drupal. Of course one probably wouldn't implement a module as both a menu item and a block.

Anyway, here is the module with minimal commenting in order to save space.

form_intro.info

; $Id$
name = "form_intro"
description = "Makes simple form, that doesn't do much of anything."
package = "My Modules"
core = 6.x

form_intro.module

<?php
// $Id:$

/**
* Implementation of hook_menu
*
*/

function form_intro_menu() {
 
$items = array();
 
$items ['quiz'] = array(
   
'title' => t('Super Cool Quiz'),
   
'page callback' => 'quiz_page',
   
'access arguments' => array('access content'),
   
'type' => MENU_SUGGESTED_ITEM,   
  );

  return
$items;
}

/**
* page callback created in the Implementation of hook_menu
*/
function quiz_page() {
  return
drupal_get_form('quiz_form');
}

/**
* Creating the actual form itself happens here
*/
function quiz_form($form_state) {
 
$form = array();
 
 
//make a texfield
 
$form['name'] = array(
   
'#type' => 'textfield',
   
'#title' => t('Please enter your name'),
   
'#size' => 30,
   
'#maxlength' => 128,

   
'#required' => TRUE,
  );
 
 
//a pair of "radio buttons"
 
$form ['toast'] = array(
   
'#type' => 'radios',
   
'#title' => t('Do you like toast?'),
   
'#options' => array(t('No'), t('Yes')),
  );
 
 
//a "submit" button
 
$form['submit'] = array(
   
'#type' => 'submit',
   
'#value' => t('Make it so...'), 
  );

  return
$form;
}

/**
* Implementation of hook_block
*/
function form_intro_block($op = 'list', $delta = 0, $edit = array()) {
  switch (
$op) {
    case
'list':
     
$blocks[0]['info'] = t('Form Intro Block');
      return
$blocks;
        
    case
'view':
     
$block['subject'] = t('Form Intro Block');
     
$block['content'] = drupal_get_form('quiz_form');
      return
$block;
    break;
  }
}

/**
* Validation function, only users who select "yes" are able to submit the form
*/
function quiz_form_validate($form_id, &$form_state) {
 
$toast = $form_state['values']['toast']; 
  if (!
$toast) {
   
form_set_error('toast', t('You must like toast to take this quiz.'));
  }
}

/**
* form_submit function, once the form passes the validation (above) this runs
*/
function quiz_form_submit($form_id, &$form_state) {
 
$name = $form_state['values']['name'];
 
$toast = $form_state['values']['toast'];
 
$output = t('Hi ');
 
$output .= check_plain($name);
 
$output .= t(' the form was submitted successfully');

 
drupal_set_message($output);
}
?>

A few notes:

Please be certain to flush the menu cache after making changes to menu items. There are numerous way to flush the menu cache along with the other caches. I tend to use "Flush all caches" which is under the favicon in the Administration menu (http://drupal.org/project/admin_menu).

I'm certain I read somewhere -- I'm thinking either in "Pro Drupal Development", on the coding standards page (http://drupal.org/node/318), or in "Drupal 6 Module Development". I can't seem to locate the reference at this moment -- that the underscore (_) should not be used in module names. As such, the module would be better named formintro.info and formintro.module; both files being placed, of course, in sites/all/modules/formintro/

I hope this helps.

Access Denied?

jsfour's picture

Hey Guys, I am having a bit of trouble here, for some reason IO am getting an "access denied" message when i do the "/quiz" call on the site. Does anyone know why I would be getting this? Here is my code:

<?php
// $Id: $

function form_intro_menu () {
$items = array ();
$items ['quiz'] = array (
'title' => t('Super Cool Quiz'),
'page callback' => 'quiz_page',
'acess arguments' => array('access content'),
'type' => MENU_SUGGESTED_ITEM,
);
return $items;
}

function quiz_page () {
return drupal_get_form('quiz_form');
}

function quiz_form ($form_state) {
$form = array();

$form ['name'] = array (
'#type' => 'textfield',
'#title' => t('PLease enter Name'),
'#required' => TRUE,
);

$form['submit'] = array (
'#type' => 'submit',
'#value' => t('Make it so...');

)

return $form;

}

could be because of

mummra1's picture

in your code, I see:

'acess arguments' => array('access content'),

need to say 'access arguments'

Caught a few errors

esod's picture

Here's the corrected module. Don't mind the indenting. It's still your code.

<?php
// $Id: $

function form_intro_menu() {
 
$items = array ();
 
$items ['quiz'] = array (
   
'title' => t('Super Cool Quiz'),
   
'page callback' => 'quiz_page',
   
'access arguments' => array('access content'),
   
'type' => MENU_SUGGESTED_ITEM,
  );
  return
$items;
}

function
quiz_page() {
  return
drupal_get_form('quiz_form');
}

function
quiz_form($form_state) {
 
$form = array();
 
 
$form ['name'] = array(
   
'#type' => 'textfield',
   
'#title' => t('PLease enter Name'),
   
'#required' => TRUE,
  );

 
$form['submit'] = array(
   
'#type' => 'submit',
   
'#value' => t('Make it so...'),
  );

  return
$form;
}
?>

These were the errors:
line 4 remove space before open parentheses
line 9 'acess arguments' changed to 'access arguments'
line 15 remove space before open parentheses
line 19 remove space before open parentheses
line 21 remove space before open parentheses
line 27 remove space before open parentheses
line 29 changed ; to ,
line 30 add ; after )

The typo on line 9 was probably the reason for the "access denied" message. The punctuation errors on lines 29 and 30 would probably have produced Fatal Errors afterwards.

Thanks guys, the typo is what

jsfour's picture

Thanks guys, the typo is what was causing the problem.

Overall, this is great how-to.

Thanks!

Thanks!

mike simos's picture

Great tutorial introduction to forms..
within 30 minutes! (it could be briefer)
Thank you !

url redirecting to splash.php

nirmesh44's picture

i have tried this on my local system but everytime i am trying to hit "http://localhost/drupal/quiz" its redirecting to "http://localhost/xampp/splash.php" i dont know why . can u guys please help me out.

Localhost

nicodecba's picture

In localhost "clean URL" it doesn't work, you need to use: "http://localhost/drupal/?q=quiz" to see the page created.

Nico From Argentina.