我正在尝试编写一个自定义标记,它将以特殊方式迭代cfquery对象。我找到了这个页面:http://www.zrinity.com/developers/mx/undocumentation/query.cfm概述了如何使用底层的java方法来导航结果集,但它似乎不适用于CF9。
我可以很好地致电.next()
,.previous()
,.first()
和.last()
,每个方法都会更新query.currentRow
,但会引用query.columnName
始终返回第一行的值,而不是currentRow。
示例:
<cfquery name="testQuery" datasource="source">
SELECT FooName FROM NumberedFoos
</cfquery>
<cfloop from="1" to="3" index="i">
<cfoutput>#testQuery.currentRow# => #testQuery.fooName#</cfoutput><br />
<cfset testQuery.next()>
</cfloop>
产地:
1 => Foo 1
2 => Foo 1
3 => Foo 1
我知道我可以使用像testQuery.fooName[testQuery.currentRow]
这样的东西,但这对于我为自定义标记制作的人来说是非常不受欢迎的。上述链接中描述的功能是否已从CF9中删除?如果有,还有其他选择吗?
修改
为了扩展原因,客户需要一个自定义标记,允许它们“断言”查询的某些内容。客户端对CF的理解水平很低,但是编写SQL非常可靠。他们期望的最终结果类似于:
<cfquery name="purchaseTotals">
SELECT PurchaseId, Total FROM Purchases
</cfquery>
<CF_ASSERT query="purchaseTotals">
purchaseTotals.Total gte 0
</CF_ASSERT>
所需的输出将是一个html表,每一行都是查询中未通过断言的行。所以对我来说,CF_ASSERT标签需要能够更新当前行。
编辑2:
主要挑战是允许在标记正文中使用html,同时仍然从相应的行替换查询值:
<CF_ASSERT query="purchaseTotals">
<CF_CONDITION expression="purchaseTotals.Total gte 0">
<!---error message when expression is false--->
<cfoutput>
Purchase #purchaseTotals.purchaseId# has a negative total!
</cfoutput>
</CF_CONDITION>
<CF_CONDITION expression="purchaseTotals.Total eq ''">
#PurchaseTotals.purchaseId# has a null total, this may be caused by:
<ul>
<li>Edge Case 1</li>
<li>Edge Case 2</li>
</ul>
</CF_CONDITION>
<CF_ASSERT>
这里的输出类似于:
Purchase 120 has a negative total! Purchase 157 has a negative total! Purchase 157 has a null total, this may be caused by:
答案 0 :(得分:2)
上述链接中描述的功能是否已从CF9中删除?
自2006年撰写文章以来,内部的东西已经发生了变化。但我怀疑你所描述的完全功能可能不存在于任何mx版本中。您的代码与关联示例之间的主要区别在于<cfoutput query="..">
(不仅仅是普通<cfoutput>
)的使用。在评估变量时,query
属性显然提供了一些额外的上下文。删除它(如在您的示例中),结果是“第一行的值,而不是currentRow。”。即使在MX6下,这对后续版本来说也不是好兆头。确切的功能可能没有删除。它从来没有开始工作。
如果有,还有替代方案吗?
正如我之前所说,最干净的方法是使用数组概念,即#query.column[row]#
。鉴于您似乎拒绝了该选项,您基本上只剩下evaluate()
。您需要在父标记内循环查询。然后使用evaluate
处理子标签表达式和内容。它不是特别优雅或简单的IMO。但我认为这可能是好的,因为它没有数组符号,或某种仪式牺牲。
<强> ASSERT.cfm 强>
<cfparam name="attributes.query" type="string">
<cfif thisTag.ExecutionMode is 'start'>
<!--- validate attributes.query is a query object --->
<cfif not ( structKeyExists(caller, attributes.query) AND IsQuery(caller[attributes.query]) )>
<cfthrow message="Attributes.query [#attributes.query#] is undefined or not a query object">
</cfif>
</cfif>
<cfif thisTag.ExecutionMode is 'end'>
<cfset variables[attributes.query] = caller[attributes.query]>
<cfloop query="variables.#attributes.query#">
<cfloop array="#thisTag.assocAttribs#" index="subTag">
<cfset variables.matchFound = evaluate(subTag.expression)>
<cfif variables.matchFound>
<cfoutput>[#currentRow#] #evaluate(DE(subTag.Content))#</cfoutput><hr>
</cfif>
</cfloop>
</cfloop>
</cfif>
<强> CONDITION.cfm 强>
注意: NOT 在标记内容中使用<cfoutput>
标记。
<cfparam name="attributes.expression" type="string">
<cfif thisTag.ExecutionMode is "start">
<cfassociate baseTag="CF_ASSERT">
</cfif>
<cfif thisTag.ExecutionMode is "end">
<cfset attributes.content = thisTag.GeneratedContent>
<cfset thisTag.GeneratedContent = "">
</cfif>
客户对CF的理解水平很低,但很漂亮 扎实编写SQL
说了这么多,是不是以这种方式实现的,因为它是最好的方法,或者因为它与编写SQL最相似,即舒适?
答案 1 :(得分:1)
内部平台效应的经典例子。
我建议你不要这样做,因为你正在尝试创建一个系统,该系统模仿基础或运行系统的内置功能,最终成为运行/实现的系统的一个执行不佳的版本。
听起来很混乱,我知道 - 但这是一个众所周知的反模式要避免。
有关详细信息,请参阅此处:http://en.wikipedia.org/wiki/Inner-platform_effect
P.S。你正在寻找什么(虽然我不同意实现)是在cfloop之外的查询的迭代,只需使用数组语法:
#queryName.fieldName[rowNumber]#
使用它你可以根据需要迭代查询,当然不需要底层的java。请注意,我们没有使用queryName.currentRow。对于previous()next()功能,您只需向上或向下更改rowNumber。