Is it essential to use the getter functions when using internal ORM properties within ORM functions? For example in the example below I use #person_id# instead of this.getperson_id().
<cfcomponent persistent="true" table="people" schema="dbo" output="false">
<cfproperty name="person_id" column="person_id" type="numeric" ormtype="int"fieldtype="id"/>
<cffunction name= "hasGifts" returntype="boolean">
<cfset gift_count = ORMExecuteQuery("SELECT COUNT(g.gift_id) FROM gifts g INNER JOIN g.people p WHERE p.person_id = '#person_id#', True )>
<cfif gift_count eq 0>
<cfreturn false>
<cfelse>
<cfreturn true>
</cfif>
</cffunction>
This seems to work fine, but I don't find it documented. It seems like it should be allowed as there is no ambiguity and this is consistent with other languages.
AFAIK it's fine to reference the variables directly as long as they are not lazy loaded. Your ORMExecuteQuery() should use :named or ? position binded value though.
Related
I'm writing a ColdFusion function that is the following:
<cffunction name="checkStatusCode" output="false" access="private" returnType="void">
<cfif result.Responseheader.Status_Code eq "400">
<cfset isBadRequest = true>
</cfif>
</cffunction>
It is both void and contains no parameters; I understand how I would call it if it had parameters and returned something; I'd simply put the code in a <cfset> tag. I simply want to run the function. What tags do I need to put it in?
Either this:
<cfset checkStatusCode()>
or, if you are using cfscript,
checkStatusCode();
You could probably use <cfinvoke> if you tried hard enough.
In Coldfusion, why do we expose private CFC variables to the world, using setters & getters?
Now, I know this question may sound dumb, because every expert, in this field, advocates such an approach, but I would like to understand why? Surely, if one sets 'variables.name' in a CFC, it should remain private. Looking at this the other way around, why don't we just set 'this.name', and then we don't have the overhead of having to create a setter & getter method. Although, I realise that Coldfusion has improved this issue by providing us with implicit setters & getters, this still doesn't answer the fundamental question.
I keep being told that this breaks the golden rule of encapsulation. But doesn't exposing a private CFC variable via a public method, break this rule, anyway?
Then, other people have told me that it is about controlling access. But how is this controlling access, when public setter & getter methods, are exposed to all & sundry?
Accessing a private CFC variable value via a public method, seems like a lot of hard work to me, especially if you have hundreds of private variables. Why not just access a public CFC variable directly?
Inside user.cfc:
<cfset this.name = "foo" />
<cfset variables.name = this.name />
<cfset var name = this.name />
Outside user.cfc [another CFC or CF template]
<cfset user.name = "foo" />
<cfset variables.name = user.name />
Any help on this issue, would be much appreciated...
Thanks for your help Scott.
Rather than talking about encapsulation, I will attempt to answer my own question with a code example:
GOOD:
foo.cfc
<cfcomponent displayname="foo">
<cffunction name="init">
<cfreturn this />
</cffunction>
<cffunction name="getName">
<cfreturn variables.name />
</cffunction>
<cffunction name="setName">
<cfargument name="name">
<cfset variables.name = "">
<cfif ListFindNoCase("foo",arguments.name)>
<cfset variables.name = arguments.name>
</cfif>
</cffunction>
</cfcomponent>
BAD:
bar.cfc
<cfcomponent displayname="bar">
<cffunction name="init">
<cfreturn this />
</cffunction>
<cffunction name="getName">
<cfreturn this.name />
</cffunction>
<cffunction name="setName">
<cfargument name="name">
<cfset this.name = "">
<cfif ListFindNoCase("bar",arguments.name)>
<cfset this.name = arguments.name>
</cfif>
</cffunction>
</cfcomponent>
test.cfm
<cfoutput>
<cfset foo = CreateObject("component","foo").init()>
<cfset foo.setName("foo")>
<cfset foo = foo.getName()>
#foo#
//foo
<cfset foo.setName("bar")>
<cfset bar = foo.getName()>
#bar#
//[empty string]
<cfset foo.name = "foo">
//ERROR
<cfset bar = CreateObject("component","bar").init()>
<cfset bar.setName("bar")>
<cfset bar = bar.getName()>
#bar#
//bar
<cfset bar.name = "foo">
<cfset foo = bar.getName()>
#foo#
//foo
</cfoutput>
Setting a mutator [setting a private [variables scope] CFC variable in a public CFC method], allows one to control what business rules are applied to variable processing via a setter.
Using the public CFC scope [this] inside a setter, would allow outside access to change the value of the variable without calling the setter method. This could be potentially dangerous.
Firstly, as I remember, ColdFusion 8+ has support for implicit getters and setters, which reduces the amount of code.
Generally encapsulation is about controlling access to the underlying object data. This is to provide safeguard the way that data is operated on in concurrent and multi-threaded applications. In a ColdFusion apps most people never worry about this, even though CF7+ has provided CFThread and ColdFusion 4+ has support for CFLock, thus enabling the application developer to control the level of thread safety across an application or CFC library.
I am currently checking all my functions and components for unscoped variables. I am using a tool called varscoper4 to check all functions. Given the following code varscoper tells me that the variable "input" at line 4 in unscoped. Is this true and do i need to scope the argument variable if I alter it?
<cfoutput>#testit(1)#</cfoutput>
<cffunction name="testit">
<cfargument name="input">
<Cfset input = 3>
<cfreturn input>
</cffunction>
FYI if I do not alter the argument variable input in the function varscoper4 does not report any unscoped variables.
<cfoutput>#testit(1)#</cfoutput>
<cffunction name="testit">
<cfargument name="input">
<Cfset var output = 3 + input>
<cfreturn output>
</cffunction>
In your fist code block, input is "unscoped" but CF interprets it as arguments scope. CF will always try to find your unscoped variables by looking through a priority order. You can find more information on that here: http://www.learncfinaweek.com/week1/Scopes/
You can also view for yourself what that looks like by dumping the different scopes and seeing the output.
<cfoutput>#testit(1)#</cfoutput>
<cffunction name="testit">
<cfargument name="input">
<cfset input = 3>
<cfdump var="#variables#" label="variables">
<cfdump var="#arguments#" label="arguments">
<cfdump var="#local#" label="local">
<cfreturn input>
</cffunction>
I would strongly encourage you to take the output of your varscoper tool as a guide to where you should explicitly scope your variables. In that case, your first block of code would look like this. This is for clarity and certainty in your code.
<cfoutput>#testit(1)#</cfoutput>
<cffunction name="testit">
<cfargument name="input">
<cfset arguments.input = 3>
<cfdump var="#variables#" label="variables">
<cfdump var="#arguments#" label="arguments">
<cfdump var="#local#" label="local">
<cfreturn arguments.input>
</cffunction>
Personally, I don't like setting or changing arguments in my functions and methods. I'd rather keep them unadulterated as you have in your second block of code. But even there, I would explicitly scope the arguments so that you know where it came from -- even if not flagged by varscoper
<cfoutput>#testit(1)#</cfoutput>
<cffunction name="testit">
<cfargument name="input">
<cfset var output = 3 + arguments.input>
<cfdump var="#variables#" label="variables">
<cfdump var="#arguments#" label="arguments">
<cfdump var="#local#" label="local">
<cfreturn output>
</cffunction>
Last thing to be added here if it's not clear is that var scoping puts everything int he local scope. You could also do this and it would be functionally equivalent to the previous block of code:
<cfoutput>#testit(1)#</cfoutput>
<cffunction name="testit">
<cfargument name="input">
<cfset local.output = 3 + arguments.input>
<cfdump var="#variables#" label="variables">
<cfdump var="#arguments#" label="arguments">
<cfdump var="#local#" label="local">
<cfreturn local.output>
</cffunction>
You should use arguments.input:
<cfset arguments.input = 3>
Although even better would be to use your second example, with the arguments scope:
<cfset var output = 3 + arguments.input >
Don't modify arguments, leave them as they are when they arrive, incase you want to re-use the original value later on.
http://help.adobe.com/livedocs/coldfusion/8/htmldocs/help.html?content=buildingComponents_29.html
I'm trying to refactor all of my CFCs to avoid using SESSION and APPLICATION variables (not an easy task).
However, in this application, SESSION variables are used in every database call, since different logged in users may be accessing different databases and schemas:
<cfquery name="qEmployees" datasource="#SESSION.DataSourceName#">
SELECT *
FROM #SESSION.DatabaseSchema#.Employees
</cfquery>
I don't want to go through the trouble of passing these two SESSION variables to every method call that accesses the database. This is especially the case since I don't want to pass DSNs and Schema Names in remote AJAX calls.
What is best practice for doing this - for all Scopes that shouldn't be used in CFCs?
I think that since the datasource truly is variable I'd pass it into every function as an optional parameter and set the default value to a variables scoped dsn attribute. I'd set the variables scoped DSN in the CFC's constructor. That way you only have to pass in the DSN for the AJAX calls.
<cffunction name="doFoo" access="remote"...>
<cfargument name="dsn" type="String" required="false" default="#variables.datasource#" />
</cffunction>
I'd use the session scope of your app to store the users dsn name and use that var to pass to the AJAX call.
You should create an "init" method that will serve as a constructor for your CFC. You can then instantiate the CFCs and store them in a shared scope, most likely the application scope. From here, to use this CFC via AJAX, I typically will create a remote facade. Basically this is another CFC that will directly access the CFC instance in the application scope. It will implement the methods you need to access via Ajax, expose them using access="remote" giving your application access to the access="public" methods from the actual CFC. In this case it is generally accepted that the remote facade can access the application scope directly as part of the design pattern.
A simple example:
example.cfc:
<cfcomponent output="false">
<cffunction name="init" access="public" output="false" returntype="any">
<cfargument name="dsn" type="string" required="true" />
<cfset variables.dsn = arguments.dsn />
<cfreturn this />
</cffunction>
<cffunction name="doStuff" access="public" output="false" returntype="query">
<cfset var q = "" />
<cfquery name="q" datasource="#variables.dsn#">
select stuff from tblStuff
</cfquery>
<cfreturn q />
</cffunction>
</cfcomponent>
In your Application.cfc onApplicationStart() method:
<cfset application.example = createObject("component","example").init(dsn = "somedsn") />
remote.cfc:
<cfcomponent output="false">
<cffunction name="doStuff" access="remote" returntype="query">
<cfreturn application.example.doStuff() />
</cffunction>
</cfcomponent>
Can you set your datasource variables in the onRequest or onRequestStart functions in your Application.cfc
<cffunction name="onSessionStart">
<cfset session.dsn = _users_personal_dsn_ />
</cffunction>
<cffunction name="onRequestStart" >
<cfset dsn = "#session.dsn#" />
</cffunction>
<cfquery name="qEmployees" datasource="#dsn#">
SELECT *
FROM #SESSION.DatabaseSchema#.Employees
</cfquery>
etc.
not sure if that will work [not tested - actually feels a bit sloppy]
-sean
The scope you choose (for any variation of this question, not just for DSNs) should be based on whether the lifetime of the value is the same as the lifetime of the scope.
In our application, the DSN is just set once in the lifetime of the application, so we have an application.config struct that gets created (parsed from a file) in onApplicationStart, and within it is application.config.dsn
If your value really does change between sessions, but not over the life of a session, go ahead and use the session scope.
If your value could change for any given request, but not in the middle of a request, put it in the request scope.
That said, still heed ryan's advice and add optional arguments that only default to this value: being flexible is always the best.
My suggestion for this is to create a base class and then have your components that need database access extend that component. It doesn't have to be in the immediate parent hierarchy but somewhere down the line.
They goal is to do two things, keep the cfc abstracted from the main program and keep it easily configurable. This accomplishes both.
So your CFC that queries the database would look something like this :
<cfcomponent extends="DataAccessBase">
<cffunction name="myFunction" access="public" returntype="string">
<cfquery datasource="#getDSN()#" name="qStuff">select * from table</cfquery>
</cffunction>
The key above is the extends="DataAccessBase" portion. This adds the layer of abstraction where you can control the data access at one configurable point, but it's not tied to the application itself, leaving the component abstracted from where it's implemented.
Your DataAccessBase.cfc could look something like this:
<cfcomponent>
<cffunction name="loadSettings">
<cfparam name="request.settings" default="#structNew()#">
<cfparam name="request.settigns.loaded" default="false">
<cfif request.settings.loaded eq false>
<!--- load settings from resource bundle etc --->
<cfset request.settings.dsn = 'myDSN'>
<cfset request.settings.loaded = true>
</cfif>
</cffunction>
<cffunction name="getDsn" access="public" returntype="string">
<cfset loadSettings()>
<cfreturn request.settings.dsn>
</cffunction>
You can of course get more intricate with how you configure and store the settings etc, but that's out of scope of the question I think. :)
I don't see any reason to pass the DSN with every method call. Yes, it works, but it's not necessary. The components are developed with a built-in assumption of the datastructure so you know that it is not going to change from a addItem() call to a updateItem() call, thus its duplication of work which means additional points of failure. :P
Make sense?
For my lookup tables, the ones that are the same for every user in the application, I do an
Application.objectname = createobject(...).init(datasource)
in the init method, I read the table into the this scope like so:
cfquery name="this.queryname"
return this
Now, whenever I need to reference the query, I can refer to it like this:
cfselect query="Application.objectname.queryname" ...
Q: Is there anything wrong with that?
No, that would be fine. The server will keep the entire object instance in memory as part of the application scope, which will include all of its properties.
As a question of style, I would suggest making your query a private property (in the variables scope in a CFC) rather than a public one (in a CFC's this scope). Allowing an object property to be public implies that as the black box designer, you're okay with an unknown developer overwriting the value. If these are database lookup tables you're storing, I'm guessing you intend this data to be read-only. Consider the following:
<cfcomponent hint="Proxy for database lookup tables" output="false">
<cfproperty name="variables.lookupTable1" type="query" hint="[Private] lookupTable1 query object." />
<cfproperty name="variables.lookupTable2" type="query" hint="[Private] lookupTable2 query object." />
<!--- Implicit initialization --->
<cfscript>
variables.lookupTable1 = QueryNew('');
variables.lookupTable2 = QueryNew('');
</cfscript>
<!--- Active initialization --->
<cffunction name="init" returntype="void" access="public" hint="Initializes the query objects with data." output="false">
<cfargument name="dsn" type="string" required="true" hint="The datasource to use." />
<cfquery name="variables.lookupTable1" datasource="#arguments.dsn#">
SELECT * FROM [TblFoo]
</cfquery>
<cfquery name="variables.lookupTable2" datasource="#arguments.dsn#">
SELECT * FROM [TblBar]
</cfquery>
</cffunction>
<!--- Data Fetching Methods --->
<cffunction name="getFoo" returntype="query" access="public" hint="Returns the contents of TblFoo." output="false">
<cfreturn variables.lookupTable1 />
</cffunction>
<cffunction name="getBar" returntype="query" access="public" hint="Returns the contents of TblFoo." output="false">
<cfreturn variables.lookupTable2 />
</cffunction>
</cfcomponent>
Syntactically, no. However, I'm assuming that you've also included a "name" attribute to that cfselect tag, since it's required.
If that's the only place you use the query object, you might want to cache the output of the cfselect drop-down box instead. :)
If you're not setting application scope variables in onApplicationStart() or onServerStart(), then don't forget to use <cflock>