Search code examples
coldfusioncoldfusion-2016

Reshowing database results in inputs when linked back to the page


I am using ColdFusion 16, HTML5, JavaScript and MSSQL for my project.

I have a form with Name, Email, Billing and Shipping Address and Phone number that once the form is submitted it inserts that info into the database. On another page I have a link to go back to that same page in case the user wants to change the info.

How do I display what is in the database back in those fields?

Also how do I have the page look exactly the same as from when it was left.

For example the box checked with the arrow saying billing and shipping is the same. Or have my hidden fields being shown if they were being shown when the page was submitted.

  <div class="row">
    <div class="col-lg-4">
        <div class="form-group">
            <label for="firstname">Name of owner:</label>
            <cfoutput><input type="text" class="form-control input-sm" name="firstname" id="firstname" placeholder="First" maxlength="100" required="yes" value="" /></cfoutput>
          </div>
        </div>
    <div class="col-lg-4">
        <div class="form-group">
            <label>&nbsp;</label>
              <cfoutput><input type="text" class="form-control input-sm" name="middlename" id="middlename" placeholder="Middle" maxlength="100" value="" /></cfoutput>
          </div>
    </div>
    <div class="col-lg-4">
        <div class="form-group">
            <label>&nbsp;</label>
              <cfoutput><input type="text" class="form-control input-sm" name="lastname" id="lastname" placeholder="Last"  maxlength="100" required="yes" value="" /></cfoutput>
          </div>
    </div>
  </div>

Solution

  • When you're on your Address page, you can use a query to populate the page.

    You'll need some sort of id to verify the record you want to edit. I'm assuming the shipping record will be entered based on some sort of session identifier.

    My theoretical database setup:

    BillingAddress

    | ID | Address1      | etc... |
    ===============================
    | 1  | 123 Sesame St | ...... |
    

    ShippingAddress

    | ID | Address1      | etc... |
    ===============================
    | 1  | 123 Sesame St | ...... |
    

    On the initial form, we can query the database to see if there are any records for the user.

    <cfquery name="getShippingAddress" datasource="myDSN">
        SELECT TOP 1 ID, Address1, etc... 
        FROM ShippingAddress
        WHERE ID = <cfqueryparam value="#session.ID#" cfsqltype="cf_sql_integer">
    </cfquery>
    <cfquery name="getBillingDetails" datasource="myDSN">
        SELECT TOP 1 ID, Address1, etc... 
        FROM BillingAddress
        WHERE ID = <cfqueryparam value="#session.ID#" cfsqltype="cf_sql_integer">
    </cfquery>
    

    NOTE: If we can be assured that there will never be more than one row for an ID, then we can skip the TOP 1.

    When we write out our form, we can output existing values into the form.

    <cfoutput>
        <input type="text" name="shippingAddress1"
            value="#encodeForHTMLAttribute(getShippingAddress.Address1)#" />
    </cfoutput>
    

    In the form, there is a checkbox (BillingShippingSame) to determine if the Billing Address is the same as the Shipping Address.

    We can set this value with some additional checks.

    <cfif BillingShippingSame = getShippingAddress.Address1 = getBillingAddress.Address1 AND .... >
    

    This just says that if the Address fields in both queries are the same, then set BillingShippingSame to true otherwise false. If all of the fields in both queries are the same, you can also convert both queries to JSON and compare them. (serializeJSON(getShippingAddress) == serializeJSON(getBillinAddress)). This will save some typing if you have multiple fields that need to be checked, but they'll only serialize the same if the fields in both queries are the same. Then just set the value in you checkbox input.

    <input type="text" name="BillingShippingSame" <cfif BillingShippingSame>checked="checked"</cfif> />
    

    And if you wanted to, you could javascript the page so that if BillingShippingSame is checked, it either disables the Billing form fields, or just continues to populate them with what was returned from the database.

    Back to our workflow. Since HTML won't pass a checkbox if it isn't checked, then just look in the form scope to see if that box was checked. If it was, then set the Billing details to the Shipping details.

    Insert/Update queries:

    <cfset cleanShippingAddress1 = cleanString(form.ShippingAddress)>
    <cfif structKeyExists(form,"BillingShippingSame")>
        <cfset cleanBillingAddress1 = cleanString(form.ShippingAddress)>
        <cfset cleanOtherBillingFields = .....>
    <cfelse>
        <cfset cleanBillingAddress1 = cleanString(form.BillingAddress)>
        <cfset cleanOtherBillingFields = .....>
    </cfif>
    <cfset cleanOtherFields = cleanString(....)>
    
    <cfquery name="UpsertShippingAddress" datasource="myDSN">
        UPDATE ShippingAddress
        SET ShippingAddress1 = <cfqueryparam value="#cleanShippingAddress1#" cfsqltype="cf_sql_varchar">
            , OtherShippingFields = 
                <cfqueryparam value="#cleanOtherShippingFields#" cfsqltype="cf_sql_varchar">
            , ...
        WHERE ID = <cfqueryparam value="#session.ID#" cfsqltype="cf_sql_integer">
    
        IF @@ROWCOUNT=0
          INSERT INTO ShippingAddress ( ID, ShippingAddress1, OtherShippingFields, ....)
          VALUES (
                 <cfqueryparam value="#session.ID#" cfsqltype="cf_sql_integer">
               , <cfqueryparam value="#cleanShippingAddress1#" cfsqltype="cf_sql_varchar">
               , <cfqueryparam value="#cleanOtherShippingFields#" cfsqltype="cf_sql_varchar">
               , ...
          ) ;
    </cfquery>
    <cfquery name="UpsertBillingAddress" datasource="myDSN">
        UPDATE BillingAddress
        SET BillingAddress1 = <cfqueryparam value="#cleanBillingAddress1#" cfsqltype="cf_sql_varchar">
            , OtherShippingFields = 
                <cfqueryparam value="#cleanOtherBillingFields#" cfsqltype="cf_sql_varchar">
            , ...
        WHERE ID = <cfqueryparam value="#session.ID#" cfsqltype="cf_sql_integer">
    
        IF @@ROWCOUNT=0
          INSERT INTO BillingAddress ( ID, BillingAddress1, OtherBillingFields, ....)
          VALUES (
               <cfqueryparam value="#session.ID#" cfsqltype="cf_sql_integer">
               , <cfqueryparam value="#cleanBillingAddress1#" cfsqltype="cf_sql_varchar">
               , cfqueryparam value="#cleanOtherBillingFields#" cfsqltype="cf_sql_varchar">
               , ...
          ) ;
    </cfquery>
    

    I tend to err on the side of paranoia when it comes to form or url or other untrustworthy inputs. My cleanString function would do various sanitations on the string before it goes into the database.

    I also used a SQL pattern that UPDATEs the database, and if no rows were inserted (@@ROWCOUNT=0 >> ID wasn't found), then it will INSERT instead.