So, I'm trying to learn and utilize components to make my code better...
I understand what getters and setters are...however, I'm not sure where to put them in respect to how my component works. My component is a faculty which has a unique id and department. I want all my information in a struct form because each faculty has a lot of information. My init method would initialize the id and the department of the specific instance and then proceed to call a query that will populate the rest of the information into a struct. I'm just not sure how to do the getters and setters for the id and department...do I just init a "blank" instance and then use a getter/ setter to actually do the input?
Also another thought/ question regarding components:
Should my component only have methods that deal with single entities (individuals) or can I also have methods in my component that deals with the whole (like a search function for all individuals).
...or should I separate the two?
Thanks!
Here's a CF8 way of setting up the faculty CFC. Notice that I didn't use the 'instance' scope 'cause when it's time to upgrade to CF9, u can remove the getters/setters and add accessor=true to the cfcomponent and you're done. However, you may find adding an artificial 'instance' scope useful when you need to get the data out of the CFC as a struct for DAO to persist your object.
<cfcomponent>
<!--- does nothing in CF8 other then for documentation purposes --->
<cfproperty name="id">
<cfproperty name="department">
<!--- if you want to type your param and return type for functions in CF8, use CFML --->
<cfscript>
function init (id, department)
{
setId(id);
setDepartment(department);
return this;
}
function getId() {
return variables.id;
}
function setId(id) {
variables.id = arguments.id;
}
// do the same for dept
// make use of Snippet in CFEclipse / CF Builder to gen for you
</cfscript>
</cfcomponent>
can I also have methods in my
component that deals with the whole
(like a search function for all
individuals). ...or should I separate
the two?
Usually in the CF world (inspired by the Java world), we'd separate them into FooService with no state, and cache your FooService as singleton in Application scope. Then implement the Create Read Update Delete (CRUD) methods that talk to the DB in FooDAO (data access object) layer. Your FooService would then call the CRUD methods in FooDAO to read (and populate) a Foo object for you.
Related
I have a “best-practices” question in regards to the correct way to instance CFCs that all need to talk to each other in a given project.
Let’s say for example you have a web application that has a bunch of different modules in it:
Online Calendar
Online Store
Blog
File Manager (uploading/downloading/processing files)
User Accounts
Each of these modules is nicely organized so that the functions that pertain to each module are contained within separate CFC files:
Calendar.cfc
Store.cfc
Blog.cfc
Files.cfc
Users.cfc
Each CFC contains functions appropriate for that particular module. For example, the Users.cfc contains functions pertaining to logging users on/off, updating account info etc…
Sometimes a CFC might need to reference a function in another CFC, for example, if the store (Store.cfc) needs to get information from a customer (Users.cfc). However, I'm not sure of the correct way to accomplish this. There are a couple ways that I've been playing with to allow my CFC's to reference each other:
Method 1: Within a CFC, instance the other CFC’s that you’re going to need:
<!--- Store.cfc --->
<cfcomponent>
<!--- instance all the CFC’s we will need here --->
<cfset usersCFC = CreateObject("component","users") />
<cfset filesCFC = CreateObject("component","files") />
<cffunction name="storeAction">
<cfset var customerInfo = usersCFC.getUser(1) />
This approach seems to work most of the time unless some of the instanced CFC’s also instance the CFC’s that instance them. For example: If Users.cfc instances Files.cfc and Files.cfc also instances Users.cfc. I’ve run into problems with occasional dreaded NULL NULL errors with this probably because of some type of infinite recursion issue.
Method 2: Instance any needed CFCs inside a CFC’s function scope (this seems to prevent the recursion issues):
<!--- Store.cfc --->
<cfcomponent>
<cffunction name="storeAction">
<!--- create a struct to keep all this function’s variables --->
<cfset var local = structNew() />
<!--- instance all the CFC’s we will need here --->
<cfset local.usersCFC = CreateObject("component","users") />
<cfset local.filesCFC = CreateObject("component","files") />
<cfset var customerInfo = local.usersCFC.getUser(1) />
My concern with this approach is that it may not be as efficient in terms of memory and processing efficiency because you wind up instancing the same CFC’s multiple times for each function that needs it. However it does solve the problem from method 1 of infinite recursion by isolating the CFCs to their respective function scopes.
One thing I thought of based on things I've seen online and articles on object oriented programming is to take advantage of a “Base.cfc” which uses the “extends” property of the cfcompontent tag to instance all of the CFC's in the application. However, I've never tested this type of setup before and I'm not sure if this is the ideal way to allow all my CFCs to talk to each other especially since I believe using extends overwrites functions if any of them share a common function name (e.g. "init()").
<!--- Base.cfc --->
<cfcomponent extends="calendar store blog users files">
What is the correct "best-practices" method for solving this type of problem?
If each of your CFC instances are intended to be singletons (i.e. you only need one instance of it in your application), then you definitely want to looking into Dependancy Injection. There are three main Dependancy Injection frameworks for CF; ColdSpring, WireBox and DI/1.
I'd suggest you look at DI/1 or WireBox as ColdSpring hasn't been updated for a while.
The wiki page for DI/1 is here:
https://github.com/framework-one/di1/wiki/Getting-Started-with-Inject-One
Wirebox wiki page is here:
http://wiki.coldbox.org/wiki/WireBox.cfm
Essentially what these frameworks do is to create (instantiate) your CFCs (beans) and then handles the dependancies they have on each other. So when you need to get your instantiated CFC it's already wired up and ready to go.
Dependancy Injection is also sometimes called IoC (inversion of control) and is a common design pattern used in many languages.
Hope that helps and good luck!
If your cfcs not related to each other the base.cfc concept does not fit. The inheritance is for classes have common things that can inherit from each other. For example if you have User.cfc and you want to added new cfc called customer.cfc I would inherit from User and override some functionality or add some without touching the actual user.cfc.
So, back to your question, since the CFC are not related or have common between each other and to avoid cross referencing, I will create serviceFactory holds instances of cfcs like this
component name="ServiceFactory"
{
function init(){
return this;
}
public User function getUserService(){
return new User();
}
public Calendar function getCalendar(){
return new Calendar();
}
}
and referencing it by
serviceFactory= new ServiceFactory();
userService = serviceFactory.getUserService();
Keep in mind this approach works only if you have sort of another CFC to manage your logic
you can do the same for all other services. If your functions are static you can save your services in application scope and instantiate it only one time (like singleton).
The third option you have is DI(dependency Injection) framework
I am trying to access Coldbox getSetting within a model.
In the docs, and on Coldbox Google Group posts, it clearly states
The model is a completely isolated layer of your application and has no
access to anything ColdBox unless you inject dependencies into them.
We suggest you look at our WireBox dependency injection so you can see
how to inject the models with what they need.
They point to this doc: http://wiki.coldbox.org/wiki/WireBox.cfm#The_WireBox_Injector
But other than the somewhat confusing doc and Google Group posts repeating that quote above, there is no real good example on how to do it.
I have attempted property injection at the top of my model:
<cfcomponent displayname="myComponent" output="false">
<cfproperty name="mySetting" inject="coldbox:setting:mySetting" />
<cffunction name="myFunction" output="false" hint="index">
<cfset value = getProperty('mySetting') />
...
This returns Error Messages: Variable GETPROPERTY is undefined.
I also attempted an argument injection in the function of my model, but I knew that wouldn't work.
<cffunction name="myFunction" output="false" hint="index">
<cfargument name="mySetting" inject="coldbox:setting:mySetting">
Can anyone show me how to pass getSetting to a model via wirebox injection, or really any method?
How do you inject a dependency in the model of Coldbox?
Your injection looks fine. The issue is that you are trying to use "getProperty()" which is not defined in your component. It comes from the framework supertype and only exists in frameworks objects like handlers, etc. Property injection places the reference to the injected object directly into the "variables" scope by default, so you just access it as variables.mySetting.
You can also control the scope that the property is injected into with the scope attribute:
http://wiki.coldbox.org/wiki/WireBox.cfm#Property_Annotation
Argument injection DOES work, but only for constructors (init) since they are called automatically by the DI engine.
Also, this ref card may be a bit simpler to read through than the full WireBox docs, but it obviously doesn't cover as much information:
https://github.com/ColdBox/cbox-refcards/raw/master/WireBox/WireBox-Refcard.pdf
Disclaimer: I am part of Team ColdBox.
In /config/WireBox.cfc, you set up aliases for your model objects:
map('KungFooDAO').to('model.path.to.KungFooDAO');
You can inject a ColdBox setting as a property right here:
map('KungFooDAO').to('model.path.to.KungFooDAO')
.property(name='myDsn', dsl='coldbox:datasource:myDsn');
Now, inside of KungFooDAO.cfc, you can reference variables.myDsn, which has the value of the ColdBox setting.
Alternately, you can leave off the .property() call in the WireBox config and add a CFPROPERTY inside of KungFooDAO.cfc like this:
<cfproperty name="myDsn" inject="coldbox:datasource:myDsn" />
These examples are taken from live code (DSN names changed to protect the innocent).
What does your WireBox entry for your model CFC look like?
I have recently started using a cfc component to store user information in the Session variable. I used to store them as individual variables in Session, such as Session.UserId, Session.Firstname, or Session.Lastname. This new component nicely wraps that all up for me into a component with get methods for each value I need (i.e. Session.User.getUserId(), Session.User.GetName(), etc).
But when I do a cfdump of the session variable for debugging, it displays the object structure's metadata. I'd like to see the values returned by the component's get methods. Any way of using a cfdump to output the function values with the object metadata?
<cfdump> isn't magic, all it does is output the value you give it. As your CFC instance doesn't expose any public properties reflecting these values, <cfdump> hasn't got any way of knowing what they are.
If you need to extract all the private properties reflecting your former session scope in one hit, then you need to create a method to do so, and then <cfdump> that. EG:
public struct function getVariables(){
return variables;
}
If you are abstracting the values you want further, eg into variables.sessionStuff (variables.sessionStuff.userId, variables.sessionStuff.name etc), then write a method getSessionStuff() which returns variables.sessionStuff.
Any way of using a cfdump to output the function values with the object metadata?
Yes, use accessors.
<cfcomponent accessors="true">
<cfproperty name="userID">
<cfproperty name="firstName">
<cfproperty name="lastName">
</cfcomponent>
Try to <cfdump> this object, you'll see the property values.
Not sure if Adobe continued support for this, but in ColdFusion 8 any variable within the THIS scope is automatically considered public (and thus visible in cfdump) while using VARIABLES scope explicitly is considered private (and thus invisible in cfdump). This way you can also avoid having stack overflows when you keep component references in your component (cfdump used to solve them recursively ending in an infinite loop). But then again: this was probably fixed in ColdFusion 9 and above.
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.
I am working with a webservice that provides basic CRUD functionality. The Retrieve is easy enough to work with, but I'm having trouble working with the Create (I've not yet messed with the Update or Delete functions).
The update function takes a single argument. This is a zObject in the WSDL. However, this is a generic object extended by what I actually need to pass. If I want to create an account, for example, I pass an Account object which extends the zObject definition.
I cannot for the life of my figure out how to get CF to allow me to do this.
ColdFusion implements the Apache Axis engine for its web service functionality.
Unfortunately CF does not take full advantage of the SOAP object model and allow
CF developers to "new" the different objects that make up a service (or subclass them).
Thankfully there is something we can do about that. When you first access a WSDL,
Axis generates a set of stub objects. These are regular java classes that contain
getters and setters for the base properties of the object. We need to use these
stubs to build our object.
In order to use these stubs however, we need to add them to the ColdFusion
class path:
Step 1) Access the WSDL in any way with coldfusion.
Step 2) Look in the CF app directory for the stubs. They are in a "subs"
directory, organized by WSDL.like:
c:\ColdFusion8\stubs\WS\WS-21028249\com\foo\bar\
Step 3) Copy everything from "com" on down into a new directory that exists in
the CF class path. or we can make one like:
c:\ColdFusion8\MyStubs\com\foo\bar\
Step 4) If you created a new directory add it to the class path.
A, open CF administrator
B. click on Server settings >> Java and JVM
C. add the path to "ColdFusion Class Path". and click submit
D. Restart CF services.
Step 5) Use them like any other java object with <CFObject /> or CreateObject()
MyObj = CreateObject("java","com.foo.bar.MyObject");
Remember that you can CFDump the object to see the available methods.
<cfdump var="#MyObj#" />
Your account object SHOULD be in the stubs. If you need to create it for some reason you would need to do that in a new Java class file
Generally when working with this much Java, cfscript is the way to go.
finally, the code would look like this:
<cfscript>
// create the web service
ArgStruct = StructNew();
ArgStruct.refreshWSDL = True;
ArgStruct.username = 'TestUserAccount';
ArgStruct.password = 'MyP#ssw0r3';
ws = createObject("webservice", "http://localhost/services.asmx?WSDL",ArgStruct);
account = CreateObject("java","com.foo.bar.Account");
account.SetBaz("hello world");
ws.Update(account);
</cfscript>
I agree with the critique of ColdFusion, however, the posted solution also does not respond well to wsdl changes.
Thankfully CF does all access to all the underlying Java methods on objects. This includes 'reflection'. While CreateObject does not know about the stub objects, the classloader that created the webservice does.
ws = createObject("webservice", "http://localhost/services.asmx?WSDL",ArgStruct);
account = ws.getClass().getClassLoader().loadClass('com.foo.bar.Account').newInstance();