ColdFusion — динамическое создание имен столбцов с помощью CFLOOP

У меня есть таблица, в которой записано имя загруженных документов, до 14 на запись. Столбцы называются так:

TABLE tblDocuments
COLUMNS documentID (int, not null, pk)
        document1 (varchar(250), null)
        document2 (varchar(250), null)
        /* and this continues through */
        document14 (varchar(250), null)

Поэтому я запрашиваю любые документы для конкретной записи:

<cfquery name="qryGetDocs" datasource="#dsn#">
     SELECT document1, ...document14
     FROM   tblDocuments
     WHERE  documentID = <cfqueryparam name="SESSION.documentID" cfsqltype="cf_sql_integer">
</cfquery>

Форма выглядит примерно так:

<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 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>

Я хочу выполнить цикл от 1 до 14, чтобы у меня был только один оператор <input> и <cfif>, например:

<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>

Однако я не могу получить правильный синтаксис, независимо от того, что я пробовал. Может кто-нибудь, пожалуйста, помогите мне с этим? Спасибо!


person BlondeMoment    schedule 12.10.2010    source источник
comment
Это громоздкая структура. Отдельные документы лучше хранить в виде строк, а не столбцов. Тогда их может быть столько, сколько нужно. Хранение их в виде 14 разных столбцов будет затруднено для поиска и обработки.   -  person Leigh    schedule 13.10.2010
comment
Я могу только поддержать комментарий Ли. Ваша структура таблицы неверна.   -  person Tomalak    schedule 13.10.2010


Ответы (2)


(На исходный вопрос уже был дан ответ. Но просто для иллюстрации...)

Более гибкая структура — хранить документы в виде строк. Таким образом, основная таблица может быть:

TABLE:    tblDocuments
COLUMNS:  DocumentID  (unique record id)
          UserID
          DocumentName

Используя эту структуру, вы можете получить все существующие документы для одного пользователя с помощью простого запроса.

<cfquery name="qryGetDocs" datasource="#dsn#">
     SELECT documentID, documentName
     FROM   tblDocuments
     WHERE  userID = <cfqueryparam name="#SomeUserIDVariable#" cfsqltype="cf_sql_integer">
</cfquery>

.. и отображать их с помощью простого цикла вывода. (Обратите внимание, я добавил «ID документа» в качестве скрытого поля для идентификации существующих документов..)

<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>

Если запрос содержит менее 14 файлов (или любое другое максимальное значение), вы можете использовать query.recordCount, чтобы определить, сколько дополнительных входных файлов нужно отобразить.

<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>
person Leigh    schedule 12.10.2010

Доступ к запросам (как и к структурам) можно получить с помощью строкового индекса и квадратных скобок, но только если вы также включите нужный номер строки (!). Это работает как двумерный массив.

<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>

Обратите внимание на HTMLEditFormat(), чтобы защитить себя от атак межсайтового скриптинга. Это важно! Никогда не выводите данные в HTML без надлежащего экранирования. (Я признаю, что имена файлов являются маловероятным вектором атаки, потому что они обычно не могут содержать заостренные скобки, но а) нельзя быть слишком осторожным, б) это хорошая привычка, и в) никто не знает, что такое безопасность дыры всплывут, когда код будет рефакторинг в какой-то момент в будущем. Не экранирование HTML-данных — непростительная небрежность.)

Более идиоматичной и гораздо более читаемой версией будет:

<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>
person Tomalak    schedule 12.10.2010
comment
Только не забудьте добавить номер строки, т.е. #queryName[colName][rowNumber]# - person Leigh; 13.10.2010
comment
Большое спасибо за ответы! - person BlondeMoment; 13.10.2010
comment
@Blonde: Пожалуйста. Не забудьте принять, если это работает для вас! :) - person Tomalak; 13.10.2010
comment
Я не знал о защите HTMLEditFormat, так что это бонус. И я собираюсь изменить структуру моей таблицы на основе ваших комментариев. Итак, у меня должно быть что-то вроде tblDocuments со столбцами documentID, documentName, documentNumber? - person BlondeMoment; 13.10.2010
comment
@BlondeMoment - Какие 14 документов относятся к ..? Например, это 14 документов, относящихся к конкретному Пользователю, идентификатор записи в другой таблице, ...? - person Leigh; 13.10.2010
comment
Да, они связаны с пользователем, и я вижу, что забыл добавить это в столбцы минуту назад. - person BlondeMoment; 13.10.2010
comment
@BlondeMoment - Спасибо .. но я не хотел красть ответ. Кода было слишком много, чтобы поместиться в комментарии. - person Leigh; 13.10.2010
comment
@Tomalak - Хорошо, хорошо. (Знаете, либо эти поля для комментариев действительно маленькие, либо я очень многословен ;) - person Leigh; 13.10.2010