We currently use CF8 and don't have access to any ORM functionality. However, I'm hoping someone out there can give me any tips on how to create all my crud actions more quickly than we are doing them right now.
Right now we create a cfc with the functions we need for each new component, hardcoded all the db field names into each function which feels like it takes forever to do.
Unfortunately the bosses wont allow us to use anything like CFWheels and we are forever creating each function manually.
I'm not looking for the scripts to self create based on what's in the DB, just a quicker way of creating the necessary crud actions for any script we write.
Below is a basic function we write for all out apps.. So I'm hoping someone out there can give me some pointers on creating all these things much more quickly.
<cfcomponent extends="master.cfc">
<cffunction name="users" access="public" returntype="query">
<cfargument name="dsn" type="string" required="yes">
<cfargument name="id" type="numeric" required="yes">
<cfquery name="get_users" datasource="#arguments.dsn#">
SELECT ID,firstname,lastname,email
FROM users
WHERE ID = <cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.id#">
</cfquery>
<cfreturn get_users >
</cffunction>
</cfcomponent>
I prefer DataMgr for CRUD. It is compatible with many CF versions and database engines. It automatically applies cfqueryparam, too.
Example query using DatMgr:
<cfset get_users = Application.DataMgr.getRecords("users", {id: arguments.id})>
This doesn't replace your CFC, but it might save you same typing. I've found it especially helpful for insert and update actions.
Have you tried CFBuilder?
Use Adobe CFC Generator (in ColdFusion Builder 2.0) -> Create CFC
http://help.adobe.com/en_US/ColdFusionBuilder/2.0/Using/WS0ef8c004658c1089-1b4fc34c122964e1318-8000.html
I have not used Woodi, but I used Illudium PU-36 Code Generator # http://cfcgenerator.riaforge.org/ back in the CF7/8 days and it worked well.
At first glance, what I feel is you need a code generator. Try Woodi.
Also,
Say if you a table called 'user', create two components.
user.cfc
w_user.cfc
user extends w_user. This way, always put your code generator code in 'w_user' and any customization in code in the 'user'. So, your MODEL object will be just the 'user.cfc'.
Ex: Getting User records
let the function be named as 'get_users()'. This can be generated with Woodi. So, the code should be in w_user cfc.
While you may have a case where you need some filter on it. Lets call it get_userWithFilter(). This will be in the 'user'. Now, You can be referring to the 'get_users()' function from inside this function.
If you have no filters needed, then you can directly call the get_users() function from 'user.cfc' (available by inheritance).
Related
I'm calling a web service through ColdFusion which returns an object, and I want to verify if one of the methods of this object exists as it won't always exist.
I found this source which seemed promising however based on my tests I can see the results are always negative and the method is never found when it's clearly there.
<cfif structKeyExists("#Result.getNotifications().getValidationResult(0)#","getField")>
Result is my underlying object, and my end goal is to verify if the method getField() exists.
Is there a clean way to do this as opposed to a try/catch?
Update:
Unfortunately, I am not sure IsInstanceOf() works with web services, due to the fact that CF uses a Proxy object to "wrap" the underlying web service class. If not, another simple option is to check class name. That avoids the ambiguity of checking for method name only (which could potentially exist in many different classes). Plus I suspect it may be more light-weight than IsInstanceOf() anyway.
<cfif compare(yourObject.getClass().name, "org.tempuri.ValidationResultField") eq 0>
Found ValidationResultField. do something
</cfif>
It looks like the dump contains several different types of objects/classes: ArrayOfValidationResult, ValidationResultField, etecetera. It sounds like what you are really trying to determine is which of those classes you are working with, so you know exactly what fields and methods will be available, per the web service definitions. Given that, I think IsInstanceOf() would be a more appropriate test, than checking for method names. More accurate as well. Nothing prevents two different classes from having the same method name. So even if method X or Y exists, there is still a possibility it may be a different class than expected.
<cfif IsInstanceOf(yourObject, "org.tempuri.ValidationResultField")>
do something
</cfif>
As far as I know, the mentioned structKeyExists approach only works if CF wraps the class internally, e.g. all instances of cfcomponent.
The only option left is to actually reflect the class:
<cftry>
<cfset Result.getNotifications().getValidationResult(0).getClass().getMethod("getField", javaCast("null", ""))>
<!--- method does exist --->
<cfcatch type="coldfusion.runtime.CfJspPage$UnsupportedBaseTypeException">
<!--- method does not exist --->
</cfcatch>
</cftry>
If the method doesn't exist, it throws UnsupportedBaseTypeException, which seems to be a follow-up of NoSuchMethodException.
Honestly, you might as well just invoke the method and catch it. Reflection comes with an additional overhead and you have to catch it anyway.
Like Miguel-F, I think this is something for getMetadata(). The following should return an array containing the respective functions of the object:
<cfset funcs = getmetadata(nameOfObj).functions>
The names of the functions are then funcs[1].name, funcs[2].name, and so on.
In general, you may obtain the metadata of all the functions of a webservice, given the URL of the WSDL, with something like
<cfhttp method="get" url="http://www.webservicex.net/globalweather.asmx?WSDL" result="res">
<cfset wsXml=xmlparse(res.filecontent)>
<cfset wsOperations = xmlsearch(wsXml,"//wsdl:operation")>
<cfdump var="#wsOperations#">
Another method you could look at (perhaps undocumented) is to get the method names from the class names in the stubs directory.
The code to run is:
<cfscript>
wsargs = structnew();
wsargs.savejava="yes";
</cfscript>
<cfset convert=createobject("webservice","url_of_wsdl",wsargs)>
Then figure out how to fish out the names from the stubs directory, {CF_INSTALL}/stubs. In my case, CF_INSTALL is C:/ColdFusion2016/cfusion/
I'm writing a utility that will parse CF code and identify functions that need to be mocked when writing tests. In order to make the "should be mocked" list not include native CF functions I need to be able to identify them.
I'd rather not maintain a list of native functions to check against. The only solution I've come up with so far is to use getMetaData and include only things found by that method. This is very little code and will certainly work, but I'm wondering if there's a simpler method with lower overhead for making the determination.
Example:
<cffunction name="foo">
<cfset LTrim(" spaces!") />
<cfset myFunction(42) />
</cffunction>
Here I'd parse and see LTrim and myFunction and want to know, for each one, if it's a native CF or a UDF in the same component.
You can use getFunctionList() it returns a struct where each key is the name of a builtin function supported by the engine. This has been there since CF4.5 and works on Railo/Lucee as well.
Sorry about the question phrase. I couldn't find better way to describe it. But my problem is as follows:
I have 3 cfc's namely settings.cfc, prices.cfc and helpers.cfc. These cfc's extend 4th cfc controller.cfc.
The helper.cfc is as follows:
<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>
The settings.cfc has a setApplicationVariables method which we use to set the application level variables. In this cfc, i have created an object of helpers.cfc and put that object into the application scope. The settings.cfc is as follows:
<cfcomponent extends="Controller">
<cffunction name="setApplicationVariables">
<cfset application.helpers = createObject("component","controllers.Helpers") />
</cffunction>
</cfcomponent>
The settings.cfc gets invoked on application start which in turn creates a object of helpers.cfc and put it into the application scope.
We create a reference to the method ProcessTemplateVariables in the controller.cfc as follows:
<cfcomponent extends="Wheels">
<cfset getFormattedCurrency = application.helpers.processTemplateVariables >
</cfcomponent>
In the prices.cfc, we use this reference to call the function processTemplateVariables, which it does. But it does not call the function formatCurrency that is called inside from the processTemplateVariables and it throws error "variable formatCurrency is undefined".
But if i use the application.helpers.processTemplateVariables(templateText="someText"), it works.
It also works, when i use cfinvoke as bellow:
<cfinvoke method="processTemplateVariables" component="controllers.helpers" templateText="someText" returnvariable="content">
The prices.cfc is as follows:
<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>
I am not sure why using reference is not working.
Sorry about the earlier confusion but your comments made me dig deeper and i could found out that it was reference that was culprit. Is there any way to make this work with reference, that would be cool?
Update:
This blog entry (by Adam Cameron) has a better description. To summarize:
.. it pulls the method out of the CFC, so it will be running in the
context of the calling code, not the CFC instance. Depending on the
code in the method, this might or might not matter.
In your specific case it does matter. The function has a dependency on formatCurrency, which does not exist in the "context" of the calling page, and that is why you get an "undefined" error.
(From comments)
Yeah, I am pretty sure you cannot do that. Each function is compiled into an individual class: specifically a static inner class. (You can see the inner class names if you dump out the function name without parenthesis ie #application.helpers.formatCurrency#) In other words, it is disconnected from any specific instance - and by extension - the other functions.
When you create an instance of the component, all of the functions are stored its variables scope. So when you invoke "processTemplateVariables" - from within the instance - it has access to the other functions via the component's variables scope. When your code creates a reference to that function, what you are actually getting is completely disconnected from the parent instance ie application.helpers. So it won't have access to any of the other functions. Hence why you get an "undefined" error.
The limitation is understood well from the answer by Leigh.
To focus on your requirement here: using a short name alias for the functions, You can still use your original code, with a little trick of adding all the dependent functions as a reference, so that they are available inside the controller.cfc scope similar to the getFormattedCurrency.
Edit 1:
<cfcomponent extends="Wheels">
<cfset getFormattedCurrency = application.helpers.processTemplateVariables />
<cfset formatCurrency = application.helpers.formatCurrency />
</cfcomponent>
Now, for the fun part, it's quite possible to access a function from another cfc and still preserve all the dependencies. I was surprised as well when I recalled an amazing post by Bennadel here. This is simply amazing. But to warn you this practice is discouraged. So I took up with your setup and went ahead anyway. And it all worked like a charm with no issues encountered so far(but I'm quite sure there could arise some complications).
The point of problem with original usage was that the function has a dependency on formatCurrency, which does not exist in the "context" of the calling page as educated by Leigh in his answer.
So what if you can just copy scoped objects or even the functions from another component to you controller.cfc, sounds weird and amazing at the same time but it's possible using the <cfinclude> tag inside the controller.cfc (note: not encouraged) based upon the idea what Bennadel used for his example.
Edit2:
Your controller.cfc should look something like this:
<cfcomponent extends="Wheels">
<!--- Placed inside the controller's scope itself, outside every other function --->
<cfinclude template="helpers.cfc" />
....<rest of your code>....
...........
</cfcomponent>
Note that you don't even need to create a short-hand alias for the functions now. All the components and views can directly use the function name as you intended.
No need to point out that there would be chances of Naming collision if any other component extending the Controller.cfc has a function with the same name as any of the function inside the component library that is being imported. But those can be solved by following more specialization for the functions into multiple components OR as simple as using a prefix as a part of the coding standard to the function names to avoid any such future scenarios.
As my application has grown, I've noticed that I am reusing a lot of database queries across multiple webpages.
At the moment I have done it using a .CFM file which has many <cfstoredproc> tags that gets included on every page that needs database data. All I am doing is wrapping these stored procedure executions in a <cfif> tag which tests what the name of the calling page is and then executes the appropriate <cfstoredproc> block of code.
I am no expert in anything, but this doesn't feel right to me. I just don't know how to manage all my database queries correctly so that they can be shared across any CFM page in the entire website. For example, one page might need the "GetUsers" stored procedure and another page might need "GetOrders".
I'm just about to embark on creating a CFC which holds every separate <cfstoredproc> or <cfquery> in its own method/function. E.g.:
<cfcomponent name="DBQueries" hint="Everything for DB retrieval">
<cffunction name="GetUsers" returntype="query">
<cfstoredproc procedure="GetUsers">
<cfprocresult name="rsUsers">
</cfstoredproc>
<cfreturn rsUsers>
</cffunction>
.....
<cffunction name="DBQuery100">
<cfstoredproc procedure="GetSomething" returntype="query">
<cfprocresult name="rsSomething">
</cfstoredproc>
<cfreturn rsSomething>
</cffunction>
</cfcomponent>
Then on a main .CFM page I will invoke the component and method required to return the data. Is this a good way to achieve DB query management?
The fact that it's database related is not as relevant as the fact that you have code repetition. You are on the right track in your effort to make the code more re-useable.
If you put your queries into a cfc, you might consider taking this one step further. Instead of invoking it all the time, use the onApplicationStart method of your Application.cfc to create an application variable that's available to all users on all pages.
Another approach is to put all these database tags into a .cfm file and to put a cfinclude in the onRequestStart method of your Application.cfc.
Both methods work. And, as is almost always the case when you compare two things, each has advantages over the other.
Consider the following two db tables
User
UserID PrimaryKey
firstname
lastname
Security
SecurityID PrimaryKey
UserID ForeignKey
Permission
All database tables have Create, Read, Update, Delete operations (CRUD)
CRUD operations can exist in several places
Inside of <cfquery> tags
Inside of Stored procedures
Others
The thing is all the CRUD operations belong together in their own way. Consider making a User object (user.cfc).
<cfcomponent>
<cffunction name="create"></cffunction>
<cffunction name="read"></cffunction>
<cffunction name="update"></cffunction>
<cffunction name="delete"></cffunction>
</cfcomponent>
Security is a part of user management, so is the object a one to one match to the db table? In some environments like ORM the answer is yes, in others not.
If you consider security to be a part of user managment, your user.cfc might look like this
<cfcomponent>
<cffunction name="create"></cffunction>
<cffunction name="read" hint="Read will also read security info"></cffunction>
<cffunction name="update" hint="Perhaps this can update security too"></cffunction>
<cffunction name="delete" hint="Delete will also delete security info"></cffunction>
<cffunction name="create_security"></cffunction>
<cffunction name="read_secrity" hint="This may not even be needed"></cffunction>
<cffunction name="update_security"></cffunction>
<cffunction name="delete_security" hint="This may not even be needed"></cffunction>
</cfcomponent>
At the end of the day you may find that you need far fewer objects (*.cfcs) than tables.
OK, now you have you user.cfc what do you do with it? It can be attached to you the rest of your app in various different ways
application.User = new user();
session.User = new user();
request.User = new user();
Each one of these is very from the next. Before we go down the road of which is appropriate, we have to consider member data, and how long we want it around.
<cfcomponent>
<cfset this.userid = ""><!--- This always points to the user I want to interact with --->
<cffunction name="create"></cffunction>
<cffunction name="read"></cffunction>
<cffunction name="update"></cffunction>
<cffunction name="delete"></cffunction>
</cfcomponent>
It is likely that your CRUD operations are going to interact with the same UserID for all their operations. You may find that after you update a record, you will often read it. Rather than always stating which UserID you are interacting with, you may just want to set it up once, and have all the functions just use the same one.
OK, now let's get back over to where you will be using them
application.User
Only one User object will exist in the the whole system. It will be created when the request comes in onthe site. This object will be shared for every request. If you attach your user object here, that suggests that all requests will be looking at the same user.
session.User
One User object will exist for a given end-user in the outside world. It will separated from all other end-users. This suggests that each end user will be looking at their own user AND that even as they click around the site, they will still be looking at the same user
request.User
One User object will exist per request. It will only exist for a particular request, and then be discarded. This suggests that looking at at particular User is meaningful on this request, but the next may be quite different, or maybe not even about users.
~~~~~~~~~~~~~~~
At the end of the day, you will need to decide how to bundle your DB interactions, and how long you will keep those bundled action together
I would have a model per table.
in there you have every query that ever does anything to that table
Lets say the Users table
Users.cfc
would have all the methods which return queries
getUsers - return alll users
getUserById - could be a paramater on the first function also.
Then when you need to work out where something in orders is being updated there is only one place to look.
I get the results like this
<cfset users = new model.Users().getUsers() />
or I use script
users = new model.Users().getUsers();
And if your really brave, try doing all the queries in script also.
One last thing to consider, if the data isn't changing, cache the query.
Things like OrderType or similar, you will get a lot of performance benefit rather than repeating the query over and over.
I have written a gateway to get a result set from my database. How do i store every row in a separate dao so that i can manipulate every record further? Or can i access the result set directly to get the records?
This is my Gateway (btw, should i write the conditional logic within the cfquery into a seperate cfc that extends this one?)
<cfcomponent name="MaterialDao" hint="data access object" output="false">
<cffunction name="init" hint="constructor" access="public" output="false" returntype="MaterialDao">
<cfargument name="dsn" type="String" required="true" hint="datasource" />
<cfset variables.instance.dsn = arguments.dsn />
<cfreturn this />
</cffunction>
<cffunction name="readMaterial" hint="read" access="public" output="false" returntype="Query">
<cfargument name="district" type="String" />
<cfset var qReadMaterial = "" />
<cfquery name="qReadMaterial" datasource="#variables.instance.dsn#">
<cfif StructKeyExists(arguments,"district")>
SELECT A.NR, A.BEZ, D.BES, D.STA
<cfelse>
SELECT A.NR, A.BEZ
</cfif>
FROM DEK AS D INNER JOIN ART AS A
ON D.NR = A.NR
WHERE 0=0
<cfif StructKeyExists(arguments,"district")>
AND D.BEZ = #arguments.district#
</cfif>
ORDER BY A.BEZ
</cfquery>
<cfreturn qReadMaterial />
</cffunction>
</cfcomponent>
I have already read a lot of articles and it seems that there are different opinions about this matter (DAO vs. Gateway, DAO & Gateway etc.). What is the best practice, what do the pros do?
The pros use just one pattern for the database access layer. The use of both a DAO and Gateway is a misnomer that I'm not really sure where it got started, but seems to only exist in the ColdFusion crowd. The DAO and Gateway patterns can pretty much serve the same function, but I think the DAO fits the bill more when talking about database interaction.
DAOs should include functionality for CRUD methods plus returning sets of records. Since CRUD and basic sets of records is highly repetitive, I use a code generator to create the code for this interaction and then customize what I need. This is a good place for conditional logic for selecting the records you need.
As Aaron mentioned, returning an array of objects for a set of records in your database is unfeasible in ColdFusion due the the performance overhead of creating objects. I typically just use the basic query returned from the DAO in my views. However, if the thing I'm modeling needs some behavior in a view, then I will put the query in an object using something similar to what Peter Bell does.
Peter Bell had a great presentation some months ago on his release of the Iterating Business Object CFC which allows you to take multiple records and iterate over one record at a time using this simple framework: http://ibo.riaforge.org/. Until CF is a little faster at generating objects, recycling a single instance of an object and repopulating the properties is likely your best best. Perhaps this can help you load one record at a time into your DAO.
Conditional logic can go in the Gateway or in a Manager CFC. Typically, I would include logic that is simple like the logic outlined in your post directly in the CFC.
A bit of advice, you may wish to make the arguments.distinct NOT required and do a simple check with if (structKeyExists(arguments, "distinct") ) { do something }.
Regards,
-Aaron Greenlee
At our company we thought long and hard about this stuff for a few months, trying the Adobe CF DAO creator via RDS and some other older ones (anyone remember CFPowerTools?).
We decided in the end to write our own DAO code generator and I thought I'd share our thoughts here. The reason we decided was because we needed to add locking hints to out SQL, we wanted to make it more efficient, more secure and cleaner.
The setup we decided on was to create a pre-defined base DAO object (called DAO.cfc) which all generated 'table' DAOs extended. All it had was a few utility methods, but the key thing thing is we can add any other functions in there that we need all our generated DAOs to have access to.
So we auto-generate code by selecting a table from the database (using the CF admin API) and create the [TableName].cfc DAO with the usual init, setters and getters, so the basic CRUD stuff.
In addition to this, we also generate a [TableName]GatewayBase.cfc and a [TableName]Gateway.cfc. [TableName]Gateway.cfc extends [TableName]GatewayBase.cfc.
So for a sample DAO run on a table called 'Customers', the files created are:
Customers.cfc /* extends DAO.cfc [not created, already exists] */
CustomersGateway.cfc
CustomersGatewayBase.cfc /* extends CustomersGateway */
So, the idea is that the gateway provides a way to deal with many 'Customer' records - the DAO is used when dealing with one and only one. All methods in the gateway will generally return a CF query object. CF is too inefficient to create massive arrays of DAO objects and in our mind the query object in CF is really flexible, so we're happy to use it.
When coding, the sub-class CustomerGateway.cfc is the only one instantiated and used. However, the base class it extends has some very useful generic functions that come for free, such as getFieldListByProperty() which based on passed parameters will return certain fields (i.e. table columns) by a certain property (i.e. a column value), so for example:
myGateway.getFieldListByProperty(property="status", value="1", fieldList="customerName,customerID", orderBy="createdOn") />
That call will return the 'customerName' and 'customerID' values for all customers with a status of 1, ordered by the date they were created on. The code is also hardened against SQL injection and validated so sensible exceptions are thrown.
This function will provide 99% (we hope!) of the multi-record querying you do on a table. If you need a more sophisiticated query then the CustomerGateway.cfc is there for you to add functions to.
Finally, we allow you add functions to CustomerGateway CFC only because if you change the customers table (say add a column), you will need to recreate the table, and tha will overwrite the Customers.cfc and the CustomersGatewayBase.cfc. However, your custom code (if any) is safe in the sub-class.
Anyway, this might be slightly off topic, but sure I thought someone might find our experience useful.