I'm exporting a query to an Excel file using cfspeadsheet. It's working and creating the Excel sheet. However, the problem is that one of the columns, ie card_number, contains a 15 digit number, which is displayed like this: 4.5421E+15. Is there a way I can display the full number instead: 4254218068670980 ?
<!--- create manual query for demo --->
<cfset qData = queryNew("")>
<cfset queryAddColumn(qData, "NumericCol", "BigInt",["4254218068670980"])>
<cfset queryAddColumn(qData, "StringCol", "Varchar",["4254218068670980"])>
<cfset queryAddColumn(qData, "DecimalCol", "Decimal",["4254218068670980"])>
<!--- export to file --->
<cfspreadsheet action="write"
filename="c:/path/to/myFile.xls"
query="qData"
overwrite="true">
You need to define and use a format for the cell to show complete number. Below is a sample code snippet for your code:
<cfscript>
theFile=GetDirectoryFromPath(GetCurrentTemplatePath()) & "new_data.xls";
//Create a new Excel spreadsheet object.
theSheet = SpreadsheetNew("Expenses");
//Set the value a cell.
SpreadsheetSetCellValue(theSheet,"4254218068670980",1,4);
//Set value into another cell.
SpreadsheetSetCellValue(theSheet,"4254218068670980",2,4);
// Define a format class for for number.
longNum=StructNew();
longNum.dataformat = "0";
//Now use this class to format cell
SpreadsheetFormatCell(theSheet,longNum,2,4);
</cfscript>
There are many supported formats available; for a complete list you may check here.
Also, just like SpreadsheetFormatCell you may want to use SpreadsheetFormatColumn or other related functions.
(Too long for comments...)
FWIW, CFSpreadsheet is designed for very simple exports, without a lot of bells and whistles. If you need special formatting, you must use spreadsheet functions instead.
The closest equivalent to your current code is probably the SpreadsheetAddRows(sheet, query) function. It populates a worksheet with the data in the supplied query object. As Viv's answer mentions, you can then format the columns as desired. For example, if you want the value to be treated as text, use {dataformat = "#"}:
<cfscript>
SpreadsheetAddRows(theSheet, qData);
SpreadsheetFormatColumns(theSheet, {dataformat = "#"}, "1-3");
SpreadSheetWrite(theSheet, "c:/path/to/myFile.xls", true);
</cfscript>
As an aside, the examples in the documentation are not always the best or cleanest. Consider them a starting point, rather than using the code exactly "as is" ..
Related
I have a note pushing to people that is Long Text format.
So that display is:
<cfoutput>
#maindata#
</cfoutput>
And lets say that data is a bunch of "blah blah blah, and more text data, blah blah" which is outputting properly.
What I am trying to do, is add an additional variable note inside the "LongText"
So in the text data I have tried.
#set.newnote#
I have tried these 2 with no luck either:
#Variables[set.newnote]#
#evaluate(set.newnote)#
I'm not having luck. Is this possible or do I need to break out of the output to add an additional output after.
I assume you're asking this because you want to nest coldfusion string variables inside text stored in a database.
You could do this using a combination of evaluate() and de() like this:
<cfset mockDBText = "The ##x.a## jumps over the ##x.b##." />
<!--- the double ## above is just for escaping a single # - in your DB you would not need ## --->
<cfoutput>#mockDBText#</cfoutput>
<cfset x.a = "quick brown fox" />
<cfset x.b = "lazy dog" />
<cfoutput><br /> #evaluate(de(mockDBText))#</cfoutput>
...however beware the huge security risks of doing this with any text derived from user input - explained in more detail for example here: https://www.bennadel.com/blog/3861-evaluating-database-records-that-contain-coldfusion-interpolation-expressions-in-adobe-coldfusion-2018.htm
A safer way is to include your own tokens to delimit variables inside DB text, and then use a function to parse these and only output known safe variables.
<cfscript>
mainData = "blah blah blah";
moreData = "more more more";
writeOutput(mainData & moreData);
</cfscript>
or
<cfoutput>#mainData##moreData#</cfoutput>
or if you want to insert moreData into mainData:
<cfscript>
newString = Left(mainData, 1, 5) & moreData & Right(mainData, 6, Len(mainData)-5));
writeOutput(newString);
</cfscript>
And another note... never ever ever ever use Evaluate(nonSanitizedUserText) or you will open your application up to injection attacks.
I created sample code using the Microsoft SQL Server Northwind demo database. If you don't have access to this demo database here is a simple (MS-SQL) script to create the table and a row of data for this question.
CREATE TABLE [dbo].[Products](
[ProductID] [int] IDENTITY(1,1) NOT NULL,
[ProductName] [nvarchar](40) NOT NULL,
[SupplierID] [int] NULL,
[CategoryID] [int] NULL,
[QuantityPerUnit] [nvarchar](20) NULL,
[UnitPrice] [money] NULL CONSTRAINT [DF_Products_UnitPrice] DEFAULT (0),
[UnitsInStock] [smallint] NULL CONSTRAINT [DF_Products_UnitsInStock] DEFAULT (0),
[UnitsOnOrder] [smallint] NULL CONSTRAINT [DF_Products_UnitsOnOrder] DEFAULT (0),
[ReorderLevel] [smallint] NULL CONSTRAINT [DF_Products_ReorderLevel] DEFAULT (0),
[Discontinued] [bit] NOT NULL CONSTRAINT [DF_Products_Discontinued] DEFAULT (0),
CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED
(
[ProductID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
GO
SET IDENTITY_INSERT [dbo].[Products] ON
GO
INSERT [dbo].[Products] ([ProductID], [ProductName], [SupplierID], [CategoryID], [QuantityPerUnit], [UnitPrice], [UnitsInStock], [UnitsOnOrder], [ReorderLevel], [Discontinued]) VALUES (1, N'Chai', 1, 1, N'10 boxes x 20 bags', 18.0000, 39, 0, 10, 0)
GO
SET IDENTITY_INSERT [dbo].[Products] OFF
GO
Here is the ColdFusion code:
<cfset variables.useTempVar = false>
<cfquery datasource="Northwind2014" name="qryNWProducts">
SELECT TOP 1 * from Products;
</cfquery>
<cfdump var="#qryNWProducts#" label="qryNWProducts">
<cfset variables['stProduct'] = {}>
<cfloop index="vcColName" list="#qryNWProducts.columnlist#">
<cfif variables.useTempVar>
<cfset variables['temp'] = qryNWProducts[vcColName]>
<cfset variables['stProduct'][vcColName] = variables.temp>
<cfelse>
<cfset variables['stProduct'][vcColName] = qryNWProducts[vcColName]>
</cfif>
</cfloop>
<cfdump var="#variables['stProduct']#" label="variables['stProduct']">
<cfloop collection="#variables['stProduct']#" item="key"><cfoutput>
variables['stProduct']['#key#'] JVM datatype = #getMetadata(variables['stProduct'][key]).getName()#<br>
</cfoutput></cfloop>
<br>
This always works:<br>
<cfset variables['aPhrase'] = "I ordered " & variables.stProduct.ProductName & " for " & DollarFormat(variables.stProduct.UnitPrice) & ".">
<cfoutput>#variables['aPhrase']#<br></cfoutput>
<br>
With "variables.useTempVar = false", the next line will throw a "Complex object types cannot be converted to simple values. " error.<br>
<cfset variables['aPhrase'] = "I ordered " & variables['stProduct']['ProductName'] & " for " & DollarFormat(variables['stProduct']['UnitPrice']) & ".">
<cfoutput>#variables['aPhrase']#<br></cfoutput>
The code above has a boolean variable named "variables.useTempVar" at the top that can be flipped to see the error that I'm getting.
It looks like the direct assignment (when variables.useTempVar = false) from the query to the structure causes the structure values to be of JVM type "coldfusion.sql.QueryColumn".
Another note: if this line of code:
<cfset variables['stProduct'][vcColName] = variables.temp>
is changed to:
<cfset variables['stProduct'][vcColName] = variables['temp']>
The JVM datatype will be "coldfusion.sql.QueryColumn".
When the dot notation temp variable is used to assign the query field (when variables.useTempVar = true); the JVM datatypes are simple types that matches up pretty well with the database column types (java.lang.Integer, java.math.BigDecimal, java.lang.String, etc.).
I've also experiemented with statements like this and that provided some odd results:
<cfset variables['stProduct'][vcColName] = qryNWProducts[vcColName].toString()>
Here's the question. Is this the best way to transfer the simple values from a query to a structure? It seems odd to be forced to use a temp variable and dot notation to make this work.
Comment: I've always thought that dot notation and associative array notation were equivalent. This code example appears to contradict that opinion.
#Leigh is correct in that you need to supply the row number when using associative array notation with a query object. So you'd reference row 1 like: qryNWProducts[vcColName][1]
As for your question
Is this the best way to transfer the simple values from a query to a structure?
Are you sure you need a struct? Your question doesn't really specify the use case, so it is entirely possible that you would be better off using the query object as-is.
If you do need it to be a struct (and since you are using ColdFusion 11) might I suggest you take a look at serializeJSON/deSerializeJSON to convert this to a struct. The serializeJSON has a new attribute that will properly serialize a query object into an "AJAX friendly" JSON array of structs. You can then deSerialize the JSON into a CF array, like so:
NWProducts = deSerializeJSON( serializeJSON( qryNWProducts, 'struct' ) )[1];
Which would return a struct representation of the first row in that query object.
Although it's not obvious from the Adobe docs for serializeJSON, the second parameter can be one of: true|false|struct|row|column which will change how the resulting data is formatted.
Here's a runnable example of using the above technique showcasing each serializeQueryAs option.
It's also a better practice to start moving that kind of code into cfscript. queryExecute is quite easy to use and makes script based queries very easy to develop. See the How To Create a Query in cfscript tutorial at trycf.com for more on how to develop script based queries.
Final note, and this is a bit off topic but it is a generally accepted best practice to not use Hungarian Notation when naming variables.
#Abram's covered the mains answer, but just to pick up one tangential point you raise.
Dot notation and associative array notation are generally equivalent in CFML. However in the case of queries, there is a slight variation. Dot notation: query.columnName is treated as shorthand for query.columnName[currentRow] (where currentRow defaults to 1).
Associative array notation with queries does not have this "syntactic sugar", so query["columnName"] refers to the entire column, as the syntax actually indicates.
There are no functions I am aware of in CFML that take a query column as an argument, however the CFML engine will convert the column to an array if it's used in an array function. This is quite handy sometimes.
Using coldfusion I would like to write a string to a txt document but it needs to be in a specific format
The code I am using at the moment is:
<!---SET STRING LENGTH FOR EACH--->
<cfset initial = "#LJustify(initial, 35)#">
<cfset lname_final = "#LJustify(lname_final, 35)#">
<cfset accounttype_final = "#LJustify(accounttype_final, 35)#">
<cfset amount_final = "#LJustify(amount_final, 35)#">
<cfset date_final = "#LJustify(date_final, 35)#">
<!---SET TOTAL STRING--->
<cfset total_string = "#initial##lname_final##accounttype_final##amount_final##date_final#">
#accountholder#<br>
#accountnumber#<br>
#accounttype#<br>
#bankname#<br>
#branch#<br>
#amount#<br>
#date#<br>
#initial#<br>
#lname_final#<br />
#accounttype_final#<br>
#amount_final#<br />
#date_final#<br>
123456789012345678901234567890123456789012345678901234567890<br />
#total_string#<br>
When I run the code however it gives me the total_string as:
123456789012345678901234567890123456789012345678901234567890
G Sinclair CH 27500 01201212
I would like to achieve the following:
123456789012345678901234567890123456789012345678901234567890
GSinclair CH 2750001201212
So I need to space the values a certain amount from each other in a neat format within the txt document
So for example in the txt the rows should look like this:
123456789012345678901234567890123456789012345678901234567890
GSinclair CH 2750001201212
DGreen OTH 3456001201212
HRamsbottom SAV 0581620016181
GSmith CC 6326378734827
What is the best way to achieve this, should I loop an empty space depending on the length of each variable within the string or is there and easier way to achieve this?
Thanks in advance
Well you're mixing the concepts of "plain text" (total_string) and "HTML" (the <br>) there. And you seem to be viewing your output in a browser, not in a text editor, because all the padding is there, just how you wanted it. However one of the behaviours of HTML rendering is that sequences of whitespace are collapsed by default. You can prevent this from happening in a browser by wrapping your string in <pre> tags.
However if you're wanting the output to be plain text, you should be saving it as a text document and inspecting it with a text editor, not with a browser.
You can also use the CHR for the tab space (#chr(9)#) to force a text editor to insert a tab. This won't help the browser (they ignore the tab command in general), but it will format the output for you in a text editor.
Use
To make Text in the file as you need.
Like,
#accountholder##LF#
#accountnumber##TAB##TAB##accounttype#
I'm a bit stumped on this one..
I currently have a string.
Please enter your variable.firstname here
What i would like to do is find the variable.firstname in the string and convert it to be used as #variable.firstname#
Im using CF8, and ive looked at using findNoCase() but the variable.firstname portion can appear anywhere. I am also trying to use this in a Coldfusion Custom Tag as its to simply display the firstname of the user that could be dynamically populated.
I cant use any other functionality to change it IE = variable['firstname] because the variable could be the result of a dynamic variable i pass in and the query for the content will reside within the custom tag.
<cfset yourNewString = replace(yourOldString,'variable.firstname',
'##variable.firstname##', 'all')>
Note the double pound signs.
I cant use any other functionality to change it IE =
variable['firstname] because the variable could be the result of a
dynamic variable i pass in and the query for the content will reside
within the custom tag.
I'm not sure I understand exactly what you're saying here but if you're saying that variables.firstname is coming from another variable and the .firstname is the dynamic part you could still use array notation.
<cfset myName = "Travis">
<cfset yourName = "user125264">
<cfset myCustomVariable = "myName">
<cfoutput>Hi, My name is #variables[myCustomVariable]#. </cfoutput>
<cfset myCustomVariable = "yourName">
<cfoutput>Your name is #variables[MyCustomVariable]#.</cfoutput>
Output: Hi, My name is Travis. Your name is user125264.
If that isn't what you meant, I apologize.
If you're trying to replace variable.firstname with #variables.firstname# and then also get the value of that variable, you'll need to do the replace <cfset yourNewString = replace(yourOldString,'variable.firstname',
'##variables.firstname##', 'all')> and then wrap the resulting string in an evaluate() function (and an inner de() to prevent CF from evaluating everything): <cfset evaluatedString = evaluate(de(yourNewstring))>.
If there are more variables besides variable.firstname that need this kind of translation, you'll need to get into regex with reReplace() to catch them all in one statement. My regex is rusty, so you'll need to Google that bit on your own. ;o)
I have a page called "shopping_cart_qry.cfm", that does a series of SELECT queries from various tables. It extracts data and populates a single structure called shopping. This structure contains around 50 parameters, like:
shopping.company_id
shopping.brand_id
shopping.cost_Price
shopping.expiry_dt
shopping.user_id
shopping.item_name
shopping.item_cost
...
I only need 15 out of the 50 parameters (shopping.item_name , shopping.item_cost, etc) for a different task. So I am calling "shopping_cart_qry.cfm" as <cfinclude> in a new file named "item_info.cfm".
In this file when I do an <cfdump> of the structure, I see all 50 parameters, including the 15 parameters I need. But when I try to assign new names to the 15 parameters I need like this:
<cfset itemName = "shopping.item_name">
<cfset itemCost = "shopping.item_Cost">
<cfset itemDt = "shopping.item_Dt">
And then use <cfdump> to see I was able to do successfully, I am seeing the variable names (itemName, itemCost, etc..) but no values.
<cfdump var="#shopping.item_name#">
<cfdump var="#shopping.item_Cost#">
<cfdump var="#shopping.item_Dt#">
Should I use <script>?
You should remove the quotation marks, ex:
<cfset itemName = shopping.item_name>
See Adobe docs on cfset.