Just wondering, with CFWheels ORM, how I go about incrementing a number in a column?
In straight up SQL I would do something like:
UPDATE table
SET field = field + 1
WHERE ...
I'd like to avoid "fetching" the value from the column and incrementing it manually. I'll be doing a lot of this in my app, so it's an overhead I could do without (plus I'd also like to keep my code simple and elegant).
Could something like this work?
<cfset post = model("post").findByKey(99) />
<cfset post.update( myColumn = myColumn + 1 ) />
Appreciate some input.
You can abstract this in the base Model.cfc if you need to repeat it a lot:
<cfcomponent extends="Wheels">
<cffunction name="incrementColumn" returntype="boolean">
<cfargument name="key" type="string" required="true">
<cfargument name="property" type="string" required="true">
<cfset local.record = this.findByKey(arguments.key)>
<cfset local.record[arguments.property]++>
<cfreturn local.record.update()>
</cffunction>
</cfcomponent>
Then you can call it from your controller like so:
<cfif post = model("post").incrementColumn(params.key, "myColumn")>
Success
<cfelse>
Error
</cfif>
the orm in cfwheels doesn't support this. for stuff like that straight sql is the way to go. the best thing to do is to create a custom method on you model to encapsulate the logic.
Related
I've started using ORM in Coldfusion 9, but I'm running into an issue in which I've got a CFC that is set to persistant=true so that when I run myCFC.init() the default values of the properties are assigned - but I don't want to use this CFC with ORM.
The problem is that Coldfusion throws the error "Table myCFC defined for cfc myCFC does not exist."
Is there a way I can get my application to ignore certain CFCs? Or only pay attention to specific CFCs, other than persistant=true
Alternatively, can I get my default property values to take effect without making the component persistent
Alternatively, can I get my default property values to take effect without making the component persistent?
Yes, just set them in your init() method.
<cfcomponent name="person" persistent="false" output="false">
<cfproperty name="gender"><!--- Non-persistent CFC: you can't set a default here --->
<cffunction name="init" output="false>
<cfset variables.gender = "m"><!--- Set the default here --->
</cffunction>
</cfcomponent>
You would also need to do this in your persistent CFCs for any complex or dynamic value defaults (e.g. an array or the current date), since you can only set simple default values (e.g. a literal string or integer) in property declarations.
<cfcomponent name="person" persistent="true" table="persons" output="false">
<cfproperty name="gender" default="m"><!---Persistent CFC, so this simple default will be set --->
<cfproperty name="dateCreated"><!---You can't set a default dynamic date value --->
<cffunction name="init" output="false>
<cfset variables.dateCreated= Now()><!--- Set the current datetime here --->
</cffunction>
</cfcomponent>
Any code you place between your opening and the first will be executed. I'm assuming your using CFproperty tags to set you defaults. Instead, use this structure:
<cfcomponent name="aCFC">
<!---
|| Psuedo Constructor code: this code runs when the object is created.
||--->
<cfset defaultVar_1 = "default value">
...etc
<cffunction name="firstFunction">
...
</cffunction>
</cfcomponent>
Via this question I've been told to start using cfqueryparam for my data, to prevent SQL injection attacks.
How do I use it for my forms? Right now I've been going over Ben Forta's book, Vol 1 and been passing data to my form, then to a form processor that calls a CFC. The CFC takes them in as a cfargument then injects that into the database with any type="x" validation.
Io use the cfqueryparam, I use that on the query itself and not even declare cfargument?
You can still use a CFC, but remember that string data passed as a function argument will still need <cfqueryparam>. Here is an example:
<cffunction name="saveData" access="public" returntype="void" output="false">
<cfargument name="formVar" type="string" required="true" />
<cfquery name="LOCAL.qSave" datasource="myDSN">
insert into myTable (col1)
values (<cfqueryparam cfsqltype="cf_sql_varchar" value="#ARGUMENTS.formVar#" />)
</cfquery>
</cffunction>
The important habit to get into is to always use <cfqueryparam>, even in CFCs.
Here is some more info on those edge-cases where you might find it hard to use <cfqueryparam>.
Hope that helps!
I have a ColdFusion page that displays data from a SQL database into a table. In the table header I have links set up to sort the data within its column. The problem is, I can only sort the data in one way: ascending or descending, depending on how I hard code it to act.
This is how I have my links set up (with the query set up to accept sorting):
Number
What is an easy way to make it switch between ASC and DESC so if it is already sorted by ASC, then sort by DESC and vise versa?
You could do something like the following:
<cfparam name="sortOrder" type="string" default="ASC">
Number
this is a little helper method i created that handles the swapping the sorting. to use just pass the name of the column to the helper within your link, the other argument are for customizing the helper:
Number
<cffunction name="swapSortOrder" returntype="string">
<cfargument name="column" type="string" required="true">
<cfargument name="sortByParams" type="string" required="false" default="sortby">
<cfargument name="sortOrderParams" type="string" required="false" default="sortorder">
<cfargument name="ascending" type="string" required="false" default="asc">
<cfargument name="descending" type="string" required="false" default="desc">
<cfif
!StructKeyExists(url, arguments.sortByParams)
OR !StructKeyExists(url, arguments.sortOrderParams)
OR arguments.column NEQ url[arguments.sortByParams]
OR url[arguments.sortOrderParams] eq arguments.descending
>
<cfreturn arguments.ascending>
</cfif>
<cfreturn arguments.descending>
</cffunction>
Now i will tell you this, the way you're doing it currently with the column and order in one variable is EXTREMELY dangerous. i would strongly suggest you follow my link example and split the column and order into two variables.
why? it makes adding security very easy. on the page that the link throws to (in this case mypage.cfm) you need to filter the sortby and sortorder variables to prevent sql injection attacks.
what i normally do is create a list of known columns that people can sort by and then filter against it:
<cfset _columns="numb,title,name,phone,email">
<cfif !ListFindNoCase(_columns, url.sortby)>
<cfset url.sortby = "title">
</cfif>
same goes for the sortorder:
<cfset _order = "asc,desc">
<cfif !ListFindNoCase(_order, url.sortorder)>
<cfset url.sortorder = "asc">
</cfif>
Hi I am using ColdFusion to call the last.fm api, using a cfc bundle sourced from here.
I am concerned about going over the request limit, which is 5 requests per originating IP address per second, averaged over a 5 minute period.
The cfc bundle has a central component which calls all the other components, which are split up into sections like "artist", "track" etc...This central component "lastFmApi.cfc." is initiated in my application, and persisted for the lifespan of the application
// Application.cfc example
<cffunction name="onApplicationStart">
<cfset var apiKey = '[your api key here]' />
<cfset var apiSecret = '[your api secret here]' />
<cfset application.lastFm = CreateObject('component', 'org.FrankFusion.lastFm.lastFmApi').init(apiKey, apiSecret) />
</cffunction>
Now if I want to call the api through a handler/controller, for example my artist handler...I can do this
<cffunction name="artistPage" cache="5 mins">
<cfset qAlbums = application.lastFm.user.getArtist(url.artistName) />
</cffunction>
I am a bit confused towards caching, but am caching each call to the api in this handler for 5 mins, but does this make any difference, because each time someone hits a new artist page wont this still count as a fresh hit against the api?
Wondering how best to tackle this
Thanks
Since it's simple data model I'd not complicate things with custom cache engines. I'd put simple struct/query : searchTerm/result,timestamp somewhere. There you could do this:
<cffunction name="artistPage" cache="5 mins">
<cfargument name="artistName" type="string" required="true"/>
<cfif structKeyExists(application.artistCache, arguments.artistName) and structfindkey(application.artistCache, arguments.artistName)>
<cfif (gettickcount() - application.artistCache[arguments.artistName].timestamp ) lte 5000 >
<cfset result = application.artistCache[arguments.artistName].result >
<cfelse>
<cfset qAlbums = application.lastFm.user.getArtist(arguments.artistName) />
<cfset tempStruct = structnew()>
<cfset structNew.result = qAlbums >
<cfset structNew.timestamp = getTickCount() >
<cfset structInsert(application.artistCache, arguments.artistName, tempstruct, true) >
<cfset result = qAlbums >
</cfif>
<cfreturn result >
</cffunction>
EDIT: yes, you should put somewhere also method which would remove struct keys where timestamp difference is gt then your cache validity period.
Facade pattern is recommended in order to make this changeable in future.
Sorry for typos :)
I would try the custom cache.
It can be a structure where keys are artist names or other unique identifiers of your entries.
If you are using CF9 or Railo 3.1.2+ you can use built-in caching (functions CachePut, CacheGet etc), it can handle for you the timeouts and stuff.
Otherwise you can store the cache into Application scope, but will need to include timestamp with each entry and check it on cache events (get/put/remove) or even on each request.
A form field value like 45,234 is to be inserted into the DB as 45234. When I try to do insert for 45,234, all it gets into the DB is 0.Note:- I am able to insert 45.234as 45.234 in the SQL DB.
The form field is named costOfShares, which is to be inserted into the table ShareVales (SQL Server 2005). I am using CF8.
SQL
table:-ShareVales; field:-costOfShares; DataType:-float
ColdFusion (form page)
<li>
<cfinput size="8" maxlength="20" name="costOfShares"
id="costOfShares" value="#share.GetcostOfShares()#">
</li>
Share.cfc:-
<cfcomponent>
<cfscript> Variables.CostOfShare </cfscript>
<cffunction name="GetCostOfShare" returntype="numeric" access="public" output="false">
<cfscript> variables.CostOfShare; </cfscript>
</cffunction>
<cffunction name="SetCostOfShare" retuntype="void" access="public" output="false">
<cfargument name="costOfShare" type="string" required="true">
<cfscript> variables. costOfShare = arguments. costOfShare; </cfscript>
</cffunction>
</cfcomponent>
ColdFusion (query page)
<cffunction>
<cfargumnet>
<cfquery …>
INSERT INTO ShareVales(shareUnitId, costOfShares)
VALUES (
<cfqueryparam cfsqltype="cf_sql_integer"
value="#arguments.share.GetshareUnitId()#">,
<cfqueryparam cfsqltype="cf_sql_float"
value="#arguments.share.GetcostOfShares()#">);
</cfquery>
</cffunction>
When I use the following directly in the Query Editor:
INSERT into share(setCostOfShare)
values(Cast(replace('5,322,444',',','') as float))
it succeeds. The error is while using cfquery/cfqueryparam in the ColdFusion template.
So, how do insert the 45234 when the form field costOfShares contains a value with commas?
If they type a comma, strip it out first before trying to insert.
LSParseNumber("45,234", "French (standard)")
Or if "French (standard)" is already your system standard system default locale, just use:
LSParseNumber("45,234")
Or...
<cfset setLocale("French (standard)")>
<cfset value = LSParseNumber("45,234")>
They'll return 45.234 (if you want 45234 instead, use US locale, see doc!)
I'd suggest you to store costOfShares as a valid ColdFusion numeric value (aka, use '.' instead of
','), and only use LSNumberFormat() or LSLSCurrencyFormat() to display the value on the view. That way you don't even have to LSParse the number, unless the number is created by the user, and he uses ','.
presumably the datatype is a float or something. comma's are not valid characters in a float (unless for European locales). You probably want instead to get the value out of the DB with a comma (but not necessarily store it with one). That would probably mean using the COVERT function during a SELECT:
http://technet.microsoft.com/en-us/library/ms174450.aspx
Not sure if this will help or not, but just put the replace inside of cfqueryparam. That way it hands SQL Server the "already cleaned up version".
CAST(<cfqueryparam cfsqltype="cf_sql_float"
value="#REPLACE(arguments.share.GetcostOfShares()',',','')#"> AS FLOAT)
Or, have CF run a replace on the variable and clean it up before you even try the query.
Or, have CF run a replace on the variable and clean it up before you even pass it in as an argument.