cfml, database and a multilingual website - coldfusion

I have a database that has four columns id,languageid,name,text
Depending on the users' default language, I create a query that has all the texts for the set language (where languageid=#user.defaultlanguageid#)
What's the easiest way of retrieving these when it comes to displaying the required string.
Seems like creating a subquery every time is a bit much work.
Is creating a function the best way to go?

You could just have a single query that populates a struct (perhaps an application-level struct) - something like this:
<cfif not IsDefined("application.langMap")>
<cfquery name="langNames" datasource="...">SELECT * from langTable</cfquery>
<cfset application.langMap = {}>
<cfloop query="langNames">
<cfif not StructKeyExists(application.langMap, languageid)>
<cfset application.langMap[languageid] = {}>
</cfif>
<cfset application.langMap[languageid][name] = text>
</cfloop>
</cfif>
And then as you need the particular string within the display:
#application.langMap[mylanguageid][name]#

Related

Coldfusion Complex Construct

I am trying to construct a coldfusion conditional statement that looks for incremental form ID checkboxes to have been selected. All checkboxes are defined as Component[component number]. I have established a loop that is looking for a URL variable that is different for every form that calls on the condition as seen below. The issue I am having is that I recieve an error when executing that tells me "Complex constructs are not supported with function parameterexists."
Clearly it has to do with the dynamic nature of the parameterexists statement, but I do not fully know what this means. Can anyone explain this and also offer a solution? I am fairly new to coldfusion and coding, so take it easy on me.
<cfloop from="1" to="#URL.loopcounter#" index="loopvar">
<cfif parameterexists(Form.Component#loopvar#)>
INSERT INTO Results (MP_Barcode, Reworked, Reworked_By)
VALUES ('#Form.MontaplastBarcode#', 'YES', '#URL.BadgeNumber#')
</cfloop>
<cfoutput>
<p class="success">YOUR REWORK HAS BEEN SUBMITTED SUCCESSFULLY.</p>
</cfoutput>
<cfelse>
<p class="error">NO REWORK WAS SUBMITTED. NO COMPONENTS SELECTED.</p>
</cfif>
Depending on the form that calls on this action, the URL loopcounter variable could range from 1 to 50.
To answer the question, there are several ColdFusion functions that won't allow you to create a dynamic name before the function evaluates it. parameterExists() was one of those. Both isDefined() and structKeyExists() will allow dynamic variables. So will the member function of structKeyExists() > structName.keyExists("theKey").
Again, if you are new to ColdFusion, I'd simply pretend you never saw parameterExists(). I believe it has been listed as "deprecated" since CF 4.5 or somewhere around there. That's almost 20 years ago. That function has actually become somewhat of a joke about how Adobe never really throws away their trash.
As I pointed out above, I'd get rid of it completely and go with structKeyExists(). I also don't know what your whole page is doing, but with the code you provided, I'd change it to something like this:
<cfloop from="1" to="#url.loopcounter#" index="loopvar">
<cfoutput>
<cfif structKeyExists(form,"Component#loopvar#")>
<!--- SANITIZE INPUTS --->
<cfset inMontplastBarcode = sanitizingFunction(FORM.MontaplastBarcode)>
<cfset inBadgeNumber = sanitizingFunction(URL.BadgeNumber)>
<!--- Now use sanitized inputs in query with queryparams --->
<cfquery name="InsertStuff" datasource="myds">
INSERT INTO Results (MP_Barcode, Reworked, Reworked_By)
VALUES (
<cfqueryparam value="#inMontaplastBarcode#" cfsqltype="cf_sql_varchar" maxlength="50">
, 'YES'
, <cfqueryparam value="#inBadgeNumber#" cfsqltype="cf_sql_varchar" maxlength="20">
)
</cfquery>
</cfif>
</cfoutput>
</cfloop>
In your database, Reworked should be a boolean datatype. It appears that it may be a 'Yes' or 'No' string. A true boolean will be a) smaller and b) easier to validate. In the cfqueryparams, if you are using a cf_sql_varchar datatype, make sure you set an appropriate max length. You'll need to look at the available CF datatypes and see how they match up to your database datatypes. (Also see https://cfdocs.org/cfqueryparam)
For your sanitizingFunction() that you'll use to sanitize your input variables, you'll want to write a function that will follow through the steps to clean up your variables to strip out unsafe characters or other things you don't want. That is an entirely different, extremely large topic all on its own.
In your form, name your checkboxes simpler. Like reworked01 through reworked50.
On the action page use cfparam to default them to zero (since html forms don't post unchecked boxes):
<cfloop from="1" to="#url.loopCounter#" index="i">
<cfparam name="form.reworked#numberFormat(i, 00)#" default="0">
</cfloop>
Then instead of fumbling with whether or not a variable exists, you can instead look for the value:
<cfloop from="1" to="#url.loopCounter#" index="i">
<cfif evaluate("form.reworked"&i) eq 1>
<!--- some logic here --->
<cfelse>
<!--- some other logic here --->
</cfif>
</cfloop>

ColdFusion - Reference variables in cfloop using query generated from UDF

I'm new to ColdFusion and have an interesting question regarding accessing variables inside a cfloop using a query that is generated from a query function.
I know I can create a variable, assign the result of the query function to the variable, and then loop over the variable containing the query result and access the data using the variable name given to the query attribute inside the loop as follows:
<cfscript>
q = createObject("component", "cfc.myDBquery");
result = q.myQuery();
</cfscript>
<cfloop query="result">
<cfoutput># result.MY_DATA #</cfoutput>
</cfloop>
However, consider this example:
<cfscript>
q = createObject("component", "cfc.myDBquery");
</cfscript>
<cfloop query="#q.myQuery()#">
<cfoutput># ???.MY_DATA #</cfoutput>
</cfloop>
Other than just outputting the data using the column name from the query (e.g. MY_DATA), how would I go about referencing this specific query when outputting data in the loop?
FWIW, Adobe shows this type of scenario in their documentation, however fails to show outputting data inside of the loop using this method:
https://helpx.adobe.com/coldfusion/cfml-reference/coldfusion-tags/tags-j-l/cfloop-looping-over-a-query.html
I know I'm being a bit neurotic for trying to eliminate one line from my code, I'm just curious if this is even possible while adhering to best practices.
This is a long formatted comment. Here:
<cfscript>
q = createObject("component", "cfc.myDBquery");
result = q.myQuery();
</cfscript>
Creating the object makes the myQuery() function available. It doesn't actually run it. You might be able to do this:
result = createObject("component", "cfc.myDBquery").myQuery();
Next, since you asked about best practices, don't do this:
<cfloop query="result">
<cfoutput># result.MY_DATA #</cfoutput>
</cfloop>
You are executing the cfoutput tag each time through the loop. Instead, do this:
<cfoutput>
<cfloop query="result">
#result.MY_DATA #
</cfloop>
</cfoutput>
or this
<cfoutput query="result">
#MY_DATA#
</cfoutput>
It behaves like a loop. Other comments about best practices are simply opinions. One of mine is that readable code is good code.
I believe there are 2 possibilities. First, ColdFusion doesn't require the scope when looping over a query so you could just reference the field name you need from the query like so:
<cfloop query="#q.myQuery()#">
<cfoutput>#MY_DATA#</cfoutput>
</cfloop>
Knowing non-scoped variables cause confusion and anger, I believe you can reference the name of the original query from your function call. For instance, if your 'myQuery()' function is something like:
<cffunction name="myQuery">
<cfquery datasource="myDS" name="myQry">
SELECT * FROM Names
</cfquery>
<cfreturn myQry>
</cffunction>
Then your can reference 'myQry' like so:
<cfloop query="#q.myQuery()#">
<cfoutput>#myQry.MY_DATA#</cfoutput>
</cfloop>

conditional query in coldfusion

I need to provide some status on items in my table which I do in the last column of my table.
First I go and query one table to see if I have a confirmation for the item .
<cfquery name="focnotice" datasource="******" result="FocResult">
SELECT ecspc
FROM tbl_CNR_H
WHERE icsc = '#myarray[i].ICSC#'
AND asr_no = '#myarray[i].ASR#'
</cfquery>
The ECSPC is a field in my Table, so logic is see if there is a record. If so, see if the ECSPC value is something other then "". If so, query another table to see if there is a matching record for this ECSPC.
<cfset ISUPStatus = "#focnotice.ecspc#">
<cfif ISUPStatus NEQ "">
<cfquery name="isupStatus" datasource="******" result="ISUPResult">
select *
from tbl_ISUP
where dpc = '#ISUPStatus#'
</cfquery>
<cfset isupcount = #ISUPResult.RecordCount#>
<cfif #isupcount# GT 0>
<cfset ISUPorder = "Yes">
<cfelse>
<cfset ISUPorder = "No">
</cfif>
<cfelse>
<cfset ISUPorder = "No">
</cfif>
I get the following error in my debug
Complex object types cannot be converted to simple values.
The expression has requested a variable or an intermediate expression
result as a simple value. However, the result cannot be converted to a
simple value. Simple values are strings, numbers, boolean values, and
date/time values. Queries, arrays, and COM objects are examples of
complex values. The most likely cause of the error is that you tried
to use a complex value as a simple one. For example, you tried to use
a query variable in a cfif tag.
What am I missing here ?
You are passing invalid parameter into the Query "myarray[i].ICSC",'#myarray[i].ASR#'. You need to specify what index of array you are using.
<cfquery name="focnotice" datasource="*******" result="FocResult">
Select ecspc
From tbl_CNR_H
Where icsc = <cfqueryparam cfsqltype="cf_sql_varchar" value="#myarray[1].ICSC#">
AND
asr_no = <cfqueryparam cfsqltype="cf_sql_varchar" value="#myarray[1].ASR#">
</cfquery>
I believe the error causing you the issue lies in:
<cfset isupcount = #ISUPResult.RecordCount#>
From a quick look of your code, try using instead:
<cfset isUpCount = isUpStatus.recordCount>
But in addition please look at the comments above, especially joins.

Set a variable dynamically into a structure via CFLOOP

I am trying to set a variable dynamically into a structure via CFLOOP. I have read Ben Nadal's blog post but cant seem to get the assignment correct. I would like to use dot notation and make the VIN a sub structure of values.
Here is my code:
<cfloop query="VINs">
<cfquery name="carsQue" datasource="carsData">
SELECT VIN, MODEL, MAKE, etc
FROM CarsDB
WHERE (VIN = #VIN#)
</cfquery>
<cfset carsStruct= StructNew()>
<cfset carsStruct.[VIN].MAKE = '#carsQue.MODEL#'>
<cfset carsStruct.[VIN].MODEL = '#carsQue.MAKE#'>
</cfloop>
Any guidance would be greatly appreciated,
Thanks
Running a query inside a loop is almost always a bad idea. In your case, a better option would be:
<cfif ListLen(valuelist(vins.vin)) gt 0>
<cfquery name=CarsQue datasource = "carsData">
select vin, model, make, etc
from carsDB
where vin in (<cfqueryparam cfsqltype="cf_sql_varchar"
value="#valuelist(vins.vin)#" list="true">)
</cfquery>
<cfset carsStruct = StructNew()>
<cfloop query="carsQue">
code for your struct
</cfloop>
<cfelse>
code for vins query returning no data
</cfif>
Better yet would be to get all the data with one query. You didn't provide enough information to determine if this was possible, but it often is.
Create a structure outside loop and and setting variable within loop can solve the problem. in a current scenario each time loop run its create a new structure.
you can do some thing like this
<cfset carsStruct= StructNew()>
<cfloop query="VINs">
<cfquery name="carsQue" datasource="carsData">
SELECT VIN, MODEL, MAKE, etc
FROM CarsDB
WHERE VIN = <cfqueryparam cf_sql_type="cf_sql_varchar" value="#VINs.VIN#">
</cfquery>
<cfset carsStruct[VINs.VIN].MAKE = carsQue.MODEL>
<cfset carsStruct[VINs.VIN].MODEL = carsQue.MAKE>
</cfloop>
Based on the limited information you've given you should be able to run one query and loop through that to add to your struct.
<cfset carsStruct= {}> //new struct
<cfif VINs.RecordCount> //your VINs query has records
<cfquery name="carsQueue" datasource="carsData">
SELECT VIN, MODEL, MAKE, etc
FROM CarsDB
// Quoted list of all your VINs. cfqueryparam prevents against SQL injection
WHERE VIN IN (<cfqueryparam cf_sql_type="cf_sql_varchar" value="#ValueList(VINs.VIN)#" list="true">
</cfquery>
<cfloop query="carsQueue">
<cfset carsStruct.[carsQueue.VIN].MAKE = carsQueue.MODEL>
<cfset carsStruct.[carsQueue.VIN].MODEL = carsQueue.MAKE>
</cfloop>
<cfelse>
// if VINs query return nothing a blank struct will be returned.
//You do NOT need this <cfelse> unless you are returning something when the query is blank
</cfif>

coldfusion struct syntax and query data

I’ve mostly only used coldfusion for queries before never needed structs or any object notation until now. The server I am working on doesn’t have debugging turned on just a “500- internal server error.” so I am unable to see why my code is not working and sadly I do not have the ability to turn debugging on.
By trial and error with commenting blocks out I’ve noticed the errors are occurring in my struct line, and adding the struct to my array. From what I’ve read of the CF documentation I do not see any syntax errors but any help would be much appreciated as to if I have any bad logic or what could be wrong.
<cfset dataArray = []>
<cfset i = 0>
<cfloop query="getMembers">
<cfquery name="getmaps" datasource=“a" dbtype="odbc">
SELECT member_id, mlong, mlat
FROM maps
WHERE member_id = '#getMembers.MemberID#'
</cfquery>
<cfif getmaps.recordcount eq 1>
<!--- temp structure to insert into array --->
<cfset dataTemp = {
memberID = getMemebers.memberID,
name = getMemebers.MemberName,
long = getmaps.mlong,
lat = getmaps.mlat
}>
<cfset dataArray[i] = dataTemp>
<cfset i++>
</cfif>
</cfloop>
I addition to Shawn's comment, I believe you'll have a problem with starting your array index at 0, rather than 1. Coldfusion begins array indices at 1.
edit Some more suggestions:
<cfset dataArray = []>
<cfloop query="getMembers">
<!--- Not usually a good idea to query each time through a loop - should be able to do a single query outside it --->
<cfquery name="getmaps" datasource=“a" dbtype="odbc">
SELECT member_id, mlong, mlat
FROM maps
WHERE member_id = <cfqueryparam value='#getMembers.MemberID#' cfsqltype="cf_sql_varchar"><!--- assuming varchar since you had quotes around it --->
</cfquery>
<cfif getmaps.recordcount eq 1>
<!--- temp structure to insert into array --->
<cfset dataTemp = {
memberID = getMembers.memberID,
name = getMembers.MemberName,
long = getmaps.mlong,
lat = getmaps.mlat
}>
<cfset ArrayAppend(dataArray,dataTemp)>
</cfif>
</cfloop>
You should consider combining the two queries into one query.
<cfquery name="qryMemberMaps" datasource="a" dbtype="ODBC">
SELECT
members.memberID, members.MemberName,
maps.mlong, maps.mlat
FROM
[members_database].dbo.members JOIN [maps_database].dbo.maps ON members.memberID = maps.member_id
</cfquery>
The current method could potentially generate thousands of queries when you only need one!
Anytime you find yourself looping over a query and calling other queries, it is a good idea to revise the original query and save hammering your database server.
For putting the data into an array of structs, Jake's answer works well.
If you are joining multiple DB's make sure that you create a non-clustered index on the column(s) that will act as the primary/foreign keys.