2

I have a form processing that I am sure can be done more efficiently and there is an error in the result set although not "life threatening" just not correct.

The purpose of the page is to associate an item with a program and at the same time associate a designation within the program -- B and F fields are checkboxes allowing the item to be associated with multiple programs and designations within that specific program. (edited for clarity) Example:

Item: Lightsaber

  • Program: Jedi Training
  • Designation: Tool(b) (yes)
  • Designation: Weapon(f) (no)

  • Program: Jedi Master

  • Designation: Tool(b) (yes)
  • Designation: Weapon(f) (yes)

  • Program: Smuggler

  • Designation: Tool(b) (no)
  • Designation: Weapon(f) (no)

Form:

 <form action="#CGI.SCRIPT_NAME#" method="post" name="program">
    <label>Item</label>
    <select id="item" name="item">
    <!---//loop through and display items --->
    <cfloop query="getitems">
        <option value="#itemid#">#itemname#</option>
    </cfloop>
    </select>   
    <table>
    <!---//loop through and display programs --->
    <cfloop query="getprogram">
    <tr>
    <td>#programname#</td>
    <td><input type="checkbox" id="B#programid#" name="B#programid#"></td>
    <td><input type="checkbox" id="F#programid#" name="F#programid#"></td>
    </tr>
    </cfloop>
    </table>
    <input type="submit">
    </form>

Action Page:

<!---// is there is a form being processed --->
<cfif #CGI.REQUEST_METHOD# is 'post'>
<!---// create program list from query --->
 <cfset pl = ValueList(query.var, ','>
<!---// set addtl form var's --->
 <cfset listassid = 'form.item'>
<!---// loop over program list ---> 
 <cfloop list="#pl#" index="i">
<!---// loop over form fields --->
  <cfloop list="form.fieldnames" index="field">
   <cfif #field# EQ 'B'&#i#>
<!---// if field is B and var, set designation true --->     
    <cfset b = 1>
   <cfelse>
<!---// it's not, set to null --->
     <cfset b = 'null'>
   </cfif>
   <cfif #field# EQ 'F'&#i#>
<!---// if field is F and var, set designation true --->  
     <cfset f = 1>
   <cfelse>
<!---// it's not, set to null --->
     <cfset f = 'null'>
   </cfif>
   <cfif b EQ 'null' AND f EQ 'null'>
<!---// if both are null then skip --->   
    //do nothing
   <cfelse>
<!---//insert record into table --->
     insert into table (table fields)
     (#i#, #listassid#, #b#, #f# )
   </cfif>
  </cfloop>
 </cfloop> 
</cfif>

The result should be:

id  item_id program_id  B   F
1   24      1           x   
2   32      2           x   x

The actual result is:

id  item_id program_id  B   F
1   24      1           x   
2   32      2           x   
3   32      2           x

Thank you in advance for any clarification and efficiencies you can suggest.

Leigh
  • 28,765
  • 10
  • 55
  • 103
pacific
  • 23
  • 4
  • While I *think* I understand the overall issue, it would be a lot easier to assist if you posted a [*small* repro case](http://stackoverflow.com/help/mcve) ;-) Without seeing the form code, my guess would be that a slightly different naming convention for the fields would do the trick. – Leigh Jun 20 '16 at 19:13
  • 1
    You should post this to the [Code Review](http://codereview.stackexchange.com/) site for tips on efficiencies you could make in your code – duncan Jun 20 '16 at 20:58
  • Thanks duncan I will – pacific Jun 21 '16 at 10:54
  • Thanks Leigh - I am retyping this from another system hence why it was slightly incomplete. – pacific Jun 21 '16 at 10:55
  • What do "B" and "F" represent: Tool (yes) and Weapon (yes) ? Also, since it is a single selection list, the form only allows one "item" to be associated with the other selections? – Leigh Jun 21 '16 at 13:52
  • yes the lightsaber was just an example illustration -- yes the single item can be associated with multiple programs and designations within that program ---- lightsaber (item) associated with jedi trainer, jedi master (programs) -- and within that program either a tool or weapon(B, F) or both – pacific Jun 21 '16 at 17:08
  • *the lightsaber was just an example* No real lightsabers? I am totally disappointed now ;-) – Leigh Jun 21 '16 at 19:38

1 Answers1

0

I think a slightly different naming convention would help resolve the current issue, and improve the readability of the code.

One option is to store all of the project id's in a hidden form field. Be sure to use the same "name", so the id's are submitted as a CSV list. Also, I would recommend more meaningful names for the checkboxes, to improve readability. For example, "isSomething" rather than just "B" or "F":

<select id="item" name="itemID">
    <cfoutput query="getitems">
        <option value="#itemid#">#itemname#</option>
    </cfoutput>
</select>   
...
<cfoutput query="getPrograms">
    <input type="hidden" name="programIDList" value="#programID#">
    ...
    <!--- Set checkbox values to 1 / Yes --->
    <td><input type="checkbox" name="isTool_#programID#" value="1"></td>
    <td><input type="checkbox" name="isWeapon_#programID#" value="1"></td>
    ...
</cfoutput>

When the form is submitted, loop through the list of project id's and use the current id to extract the values of each set of checkboxes. If either box was checked, insert a record into the database.

* Note: Checkboxes are only submitted when "checked". Before accessing the field, either use structKeyExists to verify the field exists OR use cfparam to set a default value for the field.

<!--- Loop through list of available program id's --->
<cfloop list="#form.programIDList#" index="variables.programID">

    <!--- Ensure fields exist. Set default value  = 0 / No --->
    <cfparam name="form.isTool_#variables.programID#" default="0">
    <cfparam name="form.isWeapon_#variables.programID#" default="0">

    <!--- Extract the current values --->
    <cfset variables.isTool     = FORM["isTool_"& variables.programID ] >
    <cfset variables.isWeapon   = FORM["isWeapon_"& variables.programID ] >

    <!--- DEBUG ONLY: Display current values --->
    <cfoutput>
        <hr>variables.programID = #variables.programID#
        <br>variables.isTool = #variables.isTool#
        <br>variables.isWeapon = #variables.isWeapon#
        <br>form.itemID = #form.itemID#
    </cfoutput>


    <!--- If either box was checked, save to database --->
    <cfif variables.isTool || variables.isWeapon>
        ... run cfquery here 
    </cfif>

</cfloop>

Two important notes about database queries:

  • Never use raw client values in SQL. Instead use cfqueryparam. It helps protects your database against sql injection. It also improves query performance when a statement is executed multiple times (as in this case)

  • When executing multiple (related) queries, be sure to wrap them in a cftransaction to ensure consistency, ie All statements succeed or fail as a unit.

Leigh
  • 28,765
  • 10
  • 55
  • 103
  • Leigh - Thank you, that worked and agree completely with the cfqueryparam, I use it all the time - I was just lazy retyping that back in on the question portion -- will start to employ the cftransaction, although I have used cflock in the past for similar adventures, I did some digging however and I will need to smack myself for using cflock instead of cftrasaction on everything other than login and session setting. Thanks again! – pacific Jun 23 '16 at 12:08
  • Ah, okay. In future, feel free to add a note like "cfqueryparam omitted for brevity..." to stave off *helpful* hints like mine ;-) Yes, cftransaction is better. Though I have seen cflock used as a sort of make-shift transaction mechanism, I have never been a fan of that technique. Primarily because it does not prevent some other application from updating the db. Plus it does not handle the whole ACID issue. Anyway, glad I could help :) – Leigh Jun 23 '16 at 15:09