ColdFusion - Creating column names dynamically with CFLOOP
I have a table that records the name of uploaded documents, up to 14 per record. The columns are named thus:
TABLE tblDocuments
COLUMNS documentID (int, not null, pk)
document1 (varchar(250), null)
document2 (varchar(250), null)
/* and this continues through */
document14 (varchar(250), null)
So I query for any documents for a particular record:
<cfquery name="qryGetDocs" datasource="#dsn#">
SELECT document1, ...document14
FROM tblDocuments
WHERE documentID = <cfqueryparam name="SESSION.documentID" cfsqltype="cf_sql_integer">
</cfquery>
The form looks something like this:
<form name="frmUploadDocs" method="post" action="documentsPage.cfm">
<input type="file" name="document1" size="50" >
<cfif qryGetDocs.document1 IS NOT ''>
(current file name: <a href="#vars.file_path#/#qryGetDocs.document1#">#qryGetDocs.document1#</a>)</cfif>
<input type="file" name="document2" size="50" >
<cfif qryGetDocs.document2 IS NOT ''>
(current 开发者_C百科file name: <a href="#vars.file_path#/#qryGetDocs.document2#">#qryGetDocs.document2#</a>)</cfif>
<!--- list all documents --->
<input type="file" name="document14" size="50" >
<cfif qryGetDocs.document14 IS NOT ''>
(current file name: <a href="#vars.file_path#/#qryGetDocs.document14#">#qryGetDocs.document14#</a>)</cfif>
<input type="submit" name="submit" value="Upload Documents">
</form>
I want to loop from 1 to 14, so that I only have one <input>
and <cfif>
statement, like so:
<cfloop from="1" to="14" index="i">
<input type="fiile" name="document#i#" size="30">
<cfif qryGetDocs.document#i# IS NOT ''>
(current file name: <a href="#vars.file_path#/#qryGetDocs.document[#i#]#">#qryGetDocs.document[#i#]#</a>)
</cfif>
</cfloop>
However, I cannot get the syntax correct no matter what I've tried. Can someone please help me with this? Thank you!
(The original question was already answered. But just to illustrate ...)
A more flexible structure is to store the documents as rows. So basic table might be:
TABLE: tblDocuments
COLUMNS: DocumentID (unique record id)
UserID
DocumentName
Using this structure, you could retrieve all existing documents for a single user with a simple query
<cfquery name="qryGetDocs" datasource="#dsn#">
SELECT documentID, documentName
FROM tblDocuments
WHERE userID = <cfqueryparam name="#SomeUserIDVariable#" cfsqltype="cf_sql_integer">
</cfquery>
.. and display them with a simple output loop. (Note, I added "documentID" as hidden field to identify existing documents ..)
<cfoutput query="qryGetDocs">
...
<input type="file" name="document#CurrentRow#" size="50" >
<input type="hidden" name="documentID#CurrentRow#" value="#documentID#" >
(current file name: <a href="#vars.file_path#/#documentName#">#documentName#</a>)
</cfoutput>
If the query contains less than 14 files (or whatever your maximum is..), you can use the query.recordCount to determine how many additional file inputs to display.
<cfset nextInputNumber = qryGetDocs.recordCount + 1>
<cfoutput>
<cfloop from="#nextInputNumber#" to="#MaximumNumberOfDocs#" index="counter">
<input type="file" name="document#counter#" size="50" >
<input type="hidden" name="documentID#counter#" value="0" >
</cfloop>
</cfoutput>
Queries can be accessed (like structs) with a string index and square brackets, but only if you also include the desired row number (!). This works like a two-dimenisonal array.
<cfloop from="1" to="14" index="i">
<input type="file" name="document#i#" size="30">
<cfif qryGetDocs["document#i#"][qryGetDocs.CurrentRow] IS NOT ''>
(current file name: <a href="HTMLEditFormat("#vars.file_path#/#qryGetDocs["document#i#"][qryGetDocs.CurrentRow]#")#">#HTMLEditFormat(qryGetDocs["document#i#"][qryGetDocs.CurrentRow])#</a>)
</cfif>
</cfloop>
Note the HTMLEditFormat()
to protect yourself against cross site scripting attacks. This is important! Never ever output data to HTML without properly escaping it. (I admit that filenames are an improbable attack vector because they usually cannot contain pointy brackets, but a) you can't be too cautious, b) it's a good habit to get into, and c) no one knows what security holes will pop up when the code gets re-factored at some point in the future. Not HTML-escaping data is inexcusable sloppiness.)
A more idiomatic and much more readable version would be:
<cfloop from="1" to="14" index="i">
<cfset RowNum = qryGetDocs.CurrentRow>
<cfset ColName = "document#i#">
<cfset DocName = qryGetDocs[ColName][RowNum]>
<cfset DocPath = "#vars.file_path#/#DocName#">
<input type="file" name="#ColName#" size="30">
<cfif FileExists(ExpandPath(DocPath))>
(current file name: <a href="#HTMLEditFormat(DocPath)#">#HTMLEditFormat(DocName)#</a>)
</cfif>
</cfloop>
精彩评论