Articles


Our first module, part 5: adding a form

Difficulty: Medium

Next we need to allow users to place their bids for an auction item. First, let’s remember how our module has been specified to work:

  • each auction item is associated with a channel entry which stores all the the information about the item. Our module will store details about any bids for the item.
  • only registered and logged in members can place a bid

In our module class (mod.auction.php) we need to add a tag to create the form to handle the bid: {exp:auction:form}. In our mod.auction.php file we need to a form() method (remember, the module’s function name matches the tag’s third segment).

public function form() {
    

ExpressionEngine forms (for example, the comment form) assume nothing about how the form is laid out. All we need to do is add the form’s opening and closing tags and assume that the developer using the form includes all the necessary form elements (with the correct names)

{exp:auction:form}
<p>
    &
pound; <input type="text" name="bid_amount" value="0.00" size="5" />
    <
input type="submit" name="submit" value="Place bid" />
</
p>
{/exp:auction:form} 

Making sure we have all the data

Let’s make sure we have all the data we need to save the bid. When we designed the exp_auction database table earlier we decided we’d need:

  • which entry the bid is for
  • the current user
  • the bid amount
  • the current time

The bid amount is an element of the form, so will be passed when the form is submitted.

The current user and current time we’ll set when the bid is actually submitted (rather than when the form is created) so we’ll find these later.

The only data we still need to add to the form is the item’s entry_id. There are 2 ways we could do this:

  1. we could nest the {exp:auction:form} tag within the {exp:channel:entries} tag and supply it as a parameter to form tag (like we did with the {exp:auction:summary} tag)
  2. we could get the {exp:auction:form} tag to work out which entry it should use from the current URL. This will only work if the form is used on a single-entry page, but this is often the case and is a useful technique to use.

For simplicity’s sake we’ll use the first option and pass it as a parameter. We’ll look at the second option later.

As with the summary tag we can now use $entry_id = $this->EE->TMPL->fetch_param(‘entry_id’); to fetch the entry id.

So we have all the data we require for now, so let’s build the form.

Building the form

To add the opening <form> tag we use the function: $this->EE->functions->form_declaration(). Using the provided function has a few advantages that we’ll see later.

This is how our form method in mod.auction.php will now look:

public function form() {
    
    
// Find the entry_id of the auction to add the form for
    
$entry_id $this->EE->TMPL->fetch_param('entry_id');
    if( 
$entry_id === FALSE {
        
return "";
    
}
    
    
// Build an array to hold the form's hidden fields
    
$hidden_fields = array(
        
"entry_id" => $entry_id
    
);
    
    
// Build an array with the form data
    
$form_data = array(
        
"id" => $this->EE->TMPL->form_id,
        
"class" => $this->EE->TMPL->form_class,
        
"hidden_fields" => $hidden_fields
    
);

    
// Fetch contents of the tag pair, ie, the form contents
    
$tagdata $this->EE->TMPL->tagdata;

    
$form $this->EE->functions->form_declaration($form_data) . 
        
$tagdata "</form>";

    return 
$form;

We pass the $form_data array to the form_declaration() function to construct the <form> tag. The $form_data array itself contains an array of hidden form variables (as if we’d used ` tags) which stores the value of the entry_id that we fetched from the parameter.

If we look at the HTML that this generates, we’ll see:

<form method="post" action="http://site.com/index.php">
    <
div class='hiddenFields'>
        <
input type="hidden" name="XID" value="564dfaeed1f70b0ae983d3f..." />
        <
input type="hidden" name="entry_id" value="5" />
        <
input type="hidden" name="site_id" value="1" />
    </
div>
    <
p>
        &
pound; <input type="text" name="bid_amount" value="0.00" size="5" />
        <
input type="submit" name="submit" value="Place bid" />
    </
p>
</
form

The form_declaration() function has added another 2 hidden fields: XID and site_id

The site_id refers to site if you are using the Multi Site Manager

The XID variable is used to prevent spam and protect against Cross Site Request Forgery (CSRF) - we’ll look at this in more detail later.

The other thing to note is that the form’s action parameter has been set to the site’s index.php page. Obviously, at the moment, this page is not expecting to handle the auction’s form submission. We need to tell the form where to send its data by adding an action.

Actions

An action allows us to set up a URL that will call a specific module’s function. The URL will all have the format: http://site.com/index.php?ACT=id where id will tell EE which action to use.

Actions are stored in the exp_actions table which has a simple structure containing 3 fields: id, class, method.

When EE receives a request with the ACT= query string, it will look up the id in the exp_actions table and (if it exists) call the appropriate class method it finds.

We need to add an action for our module so let’s go back to our upd.auction.php file and modify the install method to add it when the module is installed.

public function install()
{
    $mod_data 
= array(
        
'module_name' => $this->module_name,
        
'module_version' => $this->version,
        
'has_cp_backend' => "y",
        
'has_publish_fields' => 'n'
    
);
    
$this->EE->db->insert('modules'$mod_data);
    
    
$this->EE->load->dbforge();
    
    
$fields = array(
        
'id' => array(
            
'type' => 'int',
            
'constraint' => '10',
            
'unsigned' => TRUE,
            
'auto_increment'=> TRUE
        
),
        
'entry_id' => array(
            
'type' => 'int',
            
'constraint' => '10',
            
'unsigned' => TRUE,
            
'null' => FALSE
        
),
        
'member_id' => array(
            
'type' => 'int',
            
'constraint' => '10',
            
'unsigned' => TRUE,
            
'null' => FALSE
        
),
        
'bid_amount' => array(
            
'type' => 'decimal',
            
'constraint' => '7,2',
            
'default' => '0.00',
            
'null' => FALSE
        
),
        
'bid_date' => array(
            
'type' => 'int',
            
'constraint' => '10',
            
'unsigned' => TRUE,
            
'default' => '0',
            
'null' => FALSE
        
)
    );
    
$this->EE->dbforge->add_field($fields);
    
$this->EE->dbforge->add_key('id'TRUE);
    
$this->EE->dbforge->create_table('auction');
    
    
$data = array(
        
'class'     => 'Auction',
        
'method'     => 'place_bid'
    
);
    
$this->EE->db->insert('actions'$data);
    
    return 
TRUE;

When we’ve added this, remove and install the module again to create the action. This will remove any existing data (if we had any) which would obviously be a bad thing to do in a live environment, but as we are still in development it is not a problem yet. We’ll look at how to safely make updates in a later article.

This should have added a line to the exp_actions table.

This new action points to the Auction class’s method place_bid so we need to this function to the mod.auction.php file:

public function place_bid() {
    

Finally, to tell the form to send its data to this action we need to update the form() method and we do this by adding another hidden variable:

public function form() {
    
    
// Find the entry_id of the auction to add the form for
    
$entry_id $this->EE->TMPL->fetch_param('entry_id');
    if( 
$entry_id === FALSE {
        
return "";
    
}
    
    
// Build an array to hold the form's hidden fields
    
$hidden_fields = array(
        
"entry_id" => $entry_id,
        
"ACT" => $this->EE->functions->fetch_action_id'Auction''place_bid' )
    );
    
    
// Build an array with the form data
    
$form_data = array(
        
"id" => $this->EE->TMPL->form_id,
        
"class" => $this->EE->TMPL->form_class,
        
"hidden_fields" => $hidden_fields
    
);

    
// Fetch contents of the tag pair, ie, the form contents
    
$tagdata $this->EE->TMPL->tagdata;

    
$form $this->EE->functions->form_declaration($form_data) . 
        
$tagdata "</form>";

    return 
$form;

The $this->EE->functions->fetch_action_id() provides an easy way to fetch the action id given the class and method name (the action id will change if the module is re-installed, whereas the class and method name won’t).

Summary

To recap, we’ve created a template tag to build a form. We then added an action and told the form to post its results to it. On receiving the form the action then tells the system which class and method should handle the response.

In the next article we’ll add the data from the form to the auction database table.

Comments

Hi.

Great stuff here, however i’m having trouble with :
ACT” => $this->EE->functions->fetch_action_id( ‘Auction’, ‘place_bid’ )”

when i view the source the form is still ‘site.com/index/php’ without an action. It does however add a hidden field with the name as ACT and a blank value.

I’ve checked the exp_actions table and it contains the entry. any ideas?

Posted by Johnathon on January 29, 2013

Ah! Sorry disregard,
my sloppy typing cause an error. i spelled the method incorrectly in my script .

Thanks for the tutorials though keep them coming!

Posted by Johnathon on January 29, 2013

The form does not appear on my published page. In fact in re-reading the instructions many, many times, I struggle to actually see where to place the code on the template. But I’m getting nothing out. 

{exp:auction:form}

  £ <input type=“text” name=“bid_amount” value=“0.00” size=“5” />
  <input type=“submit” name=“submit” value=“Place bid” />

{/exp:auction:form}

Posted by Vince on April 25, 2013

Note to all Newbs:  I found an oversight in the tutorial code, which caused me to call for help. Chpt 5, “adding a form” - the template code is missing the entry_id which of course results in the TMP-> fetch_param to fail, the return =”“, and the form fails to appear.  I’ll post this on chpt 5 as well.

Posted by Vince on April 27, 2013

Thanks Vince. Your comments solved my problem!

Posted by Su on October 25, 2013

Thanks very much for the tutorial.
N.B. If you don’t obtain your entry_id successfully this will also cause the form to fail.

Posted by Dorothy on January 29, 2014

Form code with correct entry_id otherwise it fails.

{exp:auction:form entry_id=”{entry_id}”}

  £ <input type=“text” name=“bid_amount” value=“0.00” size=“5” />
  <input type=“submit” name=“submit” value=“Place bid” />

{/exp:auction:form}

Posted by Dharmesh on May 21, 2014

this is not the form

Posted by abc on August 18, 2014

Add a comment

(Your email address will not be displayed on the site, but will be used for your gravatar)

Notify me of follow-up comments?