如何使用cfc对象的引用从另一个函数调用函数?

5

对于翻译问题的措辞表示抱歉,我找不到更好的描述方式。但我的问题如下:

我有三个CFC,分别是settings.cfc、prices.cfc和helpers.cfc。这些CFC都扩展了第四个CFC controller.cfc。helper.cfc如下所示:

<cfcomponent extends="Controller">
    <cffunction name="formatCurrency">
        <cfset formattedCurrency = 1 />    
        <cfreturn formattedCurrency>        
    </cffunction>
    <cffunction name="processTemplateVariables">
       <cfargument name="templateText" default="defaultText" >
       <cfset formatCurrency() />
       <cfreturn formattedCurrency >        
    </cffunction>
</cfcomponent>

settings.cfc有一个setApplicationVariables方法,我们用它来设置应用级别的变量。在这个cfc中,我创建了一个helpers.cfc的对象,然后把这个对象放到了应用程序范围内。settings.cfc如下所示:

<cfcomponent extends="Controller">
   <cffunction name="setApplicationVariables">    
      <cfset application.helpers = createObject("component","controllers.Helpers") />  
   </cffunction>
</cfcomponent>

settings.cfc会在应用程序启动时被调用,然后创建一个helpers.cfc的对象并将其放入应用程序范围内。

我们按照以下方式在controller.cfc中创建了对ProcessTemplateVariables方法的引用:

<cfcomponent extends="Wheels">
   <cfset getFormattedCurrency = application.helpers.processTemplateVariables >
</cfcomponent>

在prices.cfc中,我们使用以下引用来调用function processTemplateVariables,它会被执行。但是它没有调用从processTemplateVariables中调用的函数formatCurrency,并且会抛出错误"variable formatCurrency is undefined"。

但如果我使用application.helpers.processTemplateVariables(templateText="someText"),它就可以工作了。
当我使用下面的cfinvoke时,它也可以正常工作:
<cfinvoke method="processTemplateVariables" component="controllers.helpers" templateText="someText" returnvariable="content">

prices.cfc如下所示:

<cfcomponent extends="Controller">
    <cffunction name="index">
        <!--- does not work, throws 'the formatCurrency() variable is undefined' --->
        <cfdump var="#getFormattedCurrency("someText")#"><cfabort>
        <!--- works --->    
        <cfinvoke method="processTemplateVariables" component="controllers.helpers" templateText="someText" returnvariable="content">
        <!--- works --->
        <cfset application.helpers.processTemplateVariables("someText") />   
    </cffunction>
</cfcomponent>

我不确定为什么使用引用不起作用。很抱歉之前的混乱,但是你的评论让我深入挖掘,我发现问题出在了引用上。有没有办法通过引用来达到这个效果,那就太棒了。


1
在您的 cfobject 调用中,您正在传递一个参数。如果您在 createObject 中传入该参数,错误是否仍然发生? - Matt Busche
1
除了Matt的评论之外:猜测一下,你有没有在将组件实例存储在应用程序范围之后添加/更改formatCurrency()方法?如果是这样的话,当您创建实例时调用不存在的函数可能会导致您看到的“未定义”错误。如果是这种情况,使用cfinvoke(按“名称”)起作用,因为它每次都会创建一个新实例,因此它将使用最新的代码。 - Leigh
1
Matt提出了一个很好的观点。我理解上面的内容是一个缩写示例。然而,存在一些不一致之处,例如cfinvoke提供了一个在示例函数签名中未定义的参数。你能否发布实际代码和函数签名,在发生错误时使用? - Leigh
@MattBusche,在让问题更易于理解的过程中,我忘记在使用应用程序范围内的对象时放置参数,感谢您注意到了这一点。是的,如果我从应用程序范围传递参数给对象,错误仍然会发生。我已经相应地更新了问题。 - Tushar Bhaware
@TusharBhaware - 好的,听起来你已经检查了显而易见的问题。我稍后会为你的新复现案例进行检查。 - Leigh
显示剩余10条评论
2个回答

2

更新:

这篇博客(作者Adam Cameron)有更好的描述。简而言之:

...它从CFC中提取方法,因此该方法将在调用代码的上下文中运行,而不是在CFC实例中运行。根据方法中的代码,这可能或可能不重要。

在您特定的情况下,这很重要。该函数依赖于 formatCurrency ,但在调用页面的“上下文”中不存在,这就是为什么会出现“未定义”错误的原因。


(来自评论)

是的,我非常确定你不能这样做。每个函数都编译成单独的类:具体而言是静态内部类。(如果您在没有括号的情况下转储函数名称,即#application.helpers.formatCurrency#),也可以查看内部类名称)。换句话说,它与任何特定实例以及其他功能断开连接。

当您创建组件的实例时,所有函数都存储在其variables作用域中。因此,当您从实例中调用“processTemplateVariables”时,它可以通过组件的variables作用域访问其他函数。当您的代码创建对该函数的引用时,实际上得到的是与父实例完全断开连接的 ie application.helpers。因此,它将无法访问任何其他函数。这就是为什么会出现“未定义”错误的原因。


1

Leigh的回答中可以很好地理解限制。

为了满足您的需求:使用函数的短名称别名,您仍然可以使用原始代码,并通过添加所有依赖函数作为引用的小技巧来使它们在controller.cfc范围内可用,类似于getFormattedCurrency

编辑1:

<cfcomponent extends="Wheels">
   <cfset getFormattedCurrency = application.helpers.processTemplateVariables />
   <cfset formatCurrency = application.helpers.formatCurrency />
</cfcomponent>

现在,让我们来谈谈有趣的部分,从另一个cfc访问函数并仍然保留所有依赖关系是完全可能的。当我回想起Bennadel这里发表的一篇惊人的帖子时,我也感到惊讶。这真是太神奇了。但是要警告你不鼓励使用这种方法。所以我按照你的设置继续前进。到目前为止,一切都像魅力一样运作良好,没有遇到任何问题(但我相信可能会出现一些复杂情况)。 问题的关键在于原始用法的函数依赖于formatCurrency,而根据Leigh在他的答案中所说,该函数在调用页面的"上下文"中不存在。 那么,如果您可以从另一个组件中复制作用域对象甚至函数到您的controller.cfc中,听起来既奇怪又惊人,但是使用<cfinclude>标记在controller.cfc中是可能的(注意:不建议使用),这是基于Bennadel用于他的示例的想法。
您的controller.cfc应该长这个样子:
<cfcomponent extends="Wheels">
   <!--- Placed inside the controller's scope itself, outside every other function --->
   <cfinclude template="helpers.cfc" />
   ....<rest of your code>....
   ...........
</cfcomponent>

请注意,现在甚至不需要为函数创建简写别名。所有组件和视图都可以直接使用您打算使用的函数名称。 无需指出,如果扩展Controller.cfc的任何其他组件具有与导入的组件库中的任何函数名称相同的函数,则可能会发生命名冲突。但是,这些可以通过将函数更多地专业化为多个组件或仅将前缀作为函数名称的编码标准的一部分来解决,以避免任何这样的未来情况。

有趣的方法。但我们已经决定采用不同的方法。抱歉回复晚了。我已经很长时间没有上 SO 了。 - Tushar Bhaware

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接