The Robertson Team


 

 
 
 
 
 
  Programmers' Tools >  The Free Stuff >  Free ColdFusion Tutorials >  How Do I Update One-To-Many-Record Forms?
 

How Do I Update One-To-Many-Record Forms?

We'll concentrate on the fundamentals of the problem at hand.  Stuff like validating form inputs, testing referers and whatnot are beyond the scope of this tutorial, although we do make use of cfqueryparam, which should be a mandatory part of all ColdFusion database operations. 

Lets begin:  You have a single form that is meant to update stored data (for example, a household, with the names of all of the household residents inside).  This data is stored in two tables:  The main address info table and a related child table that holds each resident in a unique record, like so:

Address

123 Main St.

City

Springfield

State

Illinois

Residents

Name

Relationship

Employed?

Criminal Record?

Homer Father Yes Yes
Marge Mother No Yes
Lisa Daughter No No
Maggie Daughter No No
Bart Son No Yes

Notes


Approach Homer with extreme caution when carrying donuts. 
Watch Bart's hands at all times



We'll use two simple queries to pull this form's data:

<cfquery
   name="Addr"
   datasource="#request.myDSN#">

   SELECT 
      Addr.Address,
      Addr.City,
      Addr.StateProvince,
      Addr.Notes
   FROM Addr
   WHERE 
   Addr.ID=
   <cfqueryparam 
      cfsqltype="CF_SQL_NUMERIC" 
      value="#url.ID#">
</cfquery>
<cfquery
   name="ResList"
   datasource="#request.myDSN#">

   SELECT 

      Residents.ID,
      Residents.Name,
      Residents.Role,
      Residents.Employment,
      Residents.Criminal
   FROM Residents
   WHERE 
   Residents.ParentID=
   <cfqueryparam
      cfsqltype="CF_SQL_NUMERIC"
      value="#url.ID#">
</cfquery>

Next comes the form itself.  The first thing you need to do is plug in a hidden form field that tells us how many residents there are.  We'll need that later on our form processing page.  Note that we are passing the primary key of the parent record via the url here.  You could also do this via a hidden form variable, client variable or session variable:

<cfoutput>
<form
   action="myformprocessor.cfm?ID=#url.ID#"
   method="POST">
 
<input
   type="Hidden"
   name="ResCount"
   value="#ResList.RecordCount#">

Next come the form field entries for the address, city and state data.  These are placed as usual.  Nothing special here:

<input
   type="Text"
   name="Address"
   value="#Addr.Address#">
<input
   type="Text"
   name="City"
   value="#Addr.City#">
<input
   type="Text"
   name="StateProvince"
   value="#Addr.StateProvince#">

</cfoutput>

Now comes the first tricky part; outputting the multiple child records from the Residents table.  Only its not so tricky.  We'll need to output the results of the ResList query from inside of a cfquery output loop, and find a way to make the form field names unique to each row.  ColdFusion's CurrentRow value -- automatic returned with all query output -- gives us a unique -- and, much more importantly, consecutive -- number that we can use for this purpose.

<cfoutput
   query="ResList">
<p>
Resident ## #ResList.CurrentRow#<br>
<input
   type="Hidden"
   name="ResID#ResList.CurrentRow#"
   value="#ResList.ID#">

<input
   type="Text"
   name="Name#ResList.CurrentRow#"
   value="#ResList.Name#">
<br>
<input
   type="Text"
   name="Role#ResList.CurrentRow#"
   value="#ResList.Role#">
<br>

<input
   type="Text"
   name="Employment#ResList.CurrentRow#"
   value="#ResList.Employment#">
<br>
<input
   type="Text"
   name="Criminal#ResList.CurrentRow#"
   value="#ResList.Criminal#">
</p>
</cfoutput>
<p>
<input
   type="Submit"
   value="Submit Form">
</p>
</form>

OK, we're done with the input form.  Note the hidden ID field that is a part of each child record above.  We'll use that later to identify which resident record needs to get updated.   

Now its time to build our form processor, myformprocessor.cfm.  We have a block of form fields representing our child table contents, and they all have unique names.  The next tricky part will be to attach those names to fields, grouped properly as discrete records, during the form processing step.  And this time it is a little tricky, but not that tricky.  First on our form processor we'll update the parent record:

<!--- myformprocessor.cfm --->
<cfquery
   datasource="#request.myDSN#">

   UPDATE Addr
   SET 
   Addr.Address=
      <cfqueryparam
         cfsqltype="CF_SQL_VARCHAR"
         value="#form.Address#"
         null="#YesNoFormat(not Len(form.Address))#">
,
   Addr.City=
      <cfqueryparam 
         cfsqltype="CF_SQL_VARCHAR" 
         value="#form.City#" 
         null="#YesNoFormat(not Len(form.City))#">
,
   Addr.StateProvince=
      <cfqueryparam 
         cfsqltype="CF_SQL_VARCHAR" 
         value="#form.StateProvince#" 
         null="#YesNoFormat(not Len(form.StateProvince))#">
,
   Addr.Notes=
      <cfqueryparam 
         cfsqltype="CF_SQL_LONGVARCHAR" 
         value="#form.Notes#" 
         null="#YesNoFormat(not Len(form.Notes))#">

   WHERE 
      Addr.ID=
      <cfqueryparam
         cfsqltype="CF_SQL_NUMERIC"
         value="#url.ID#">
</cfquery>

OK, so much for the easy part (of the tricky part).  Now we update the child records.  We'll need to run a loop, first of all. The hidden recordcount value we stored earlier will tell us how many iterations of that loop to perform.  At this point we see why it is so important to have consecutive numbers appended to the fieldnames: We're going to be using the loop index value to match up with the appended numbers.  An entirely separate process that fortunately for us matches up precisely with the one we laid down on the form itself.

<cfloop
   index="LoopCount"
   from=1
   to=#Val(form.ResCount)#>

   <cfset variables.ResID="form.ResID#Evaluate(LoopCount)#">
   <cfset variables.ResID="#Evaluate(variables.ResID)#">
   <cfset variables.ResName="form.Name#Evaluate(LoopCount)#">
   <cfset variables.ResName="#Evaluate(variables.ResName)#">
   <cfset variables.ResRole="form.Role#Evaluate(LoopCount)#">
   <cfset variables.ResRole="#Evaluate(variables.ResRole)#">
   <cfset variables.ResEmpl="form.Employment#Evaluate(LoopCount)#">
   <cfset variables.ResEmpl="#Evaluate(variables.ResEmpl)#">
   <cfset variables.ResCriminal="form.Criminal#Evaluate(LoopCount)#">
   <cfset variables.ResCriminal="#Evaluate(variables.ResCriminal)#"> 
   <cfquery 
      datasource="#request.myDSN#">

      UPDATE Residents
      SET 
      Residents.Name=
      <cfqueryparam 
         cfsqltype="CF_SQL_VARCHAR" 
         value="#variables.ResName#"
         null="#YesNoFormat(not Len(variables.ResName))#">
,
      Residents.Role=
      <cfqueryparam
         cfsqltype="CF_SQL_VARCHAR"
         value="#variables.ResRole#"
         null="#YesNoFormat(not Len(variables.ResRole))#">
,
      Residents.Employment=
      <cfqueryparam
         cfsqltype="CF_SQL_VARCHAR"
         value="#variables.ResEmpl#"
         null="#YesNoFormat(not Len(variables.ResEmpl))#">
,
      Residents.Criminal=
      <cfqueryparam
         cfsqltype="CF_SQL_VARCHAR"
         value="#variables.ResCriminal#"
         null="#YesNoFormat(not Len(variables.ResCriminal))#">
      WHERE 
         Residents.ID=
         <cfqueryparam
            cfsqltype="CF_SQL_NUMERIC"
            value="#variables.ResID#">

   </cfquery>
</cfloop>
<!--- end of myprocessor.cfm --->

The first part of the loop above gives us a usable field name and value in two steps.  The first step creates a temp value, and puts the form field variable name into it.  The first value held by variables.ResID will thus be "form.ResID1" for the first record we pull.  Te second step is to simply evaluate the value we created on the previous line, which for the first iteration of our example will be "Evaluate (form.ResID1)".  If you get the idea of combining these two steps into a single one, be advised this won't work without more gymnastics than it takes to just run the thing in two steps as shown.

And that, as they say, is that.

Hope this helps,
--------------
Matt Robertson
--------------

 


The Robertson Team, TheKing@mysecretbase.com
1.559.360.1717 


HostMySite.com is a leader in ColdFusion web hosting and managed dedicated servers.