I am currently building a shopping cart, which is using a struct within an array to hold cart information. If the product is already added to the cart and the add to cart button is pressed again I need to update the struct quantity and not add another item to the cart. As the add to cart button is pressed, I first check to see if the array is empty, if not, I loop through the array looking for the product id submitted by the form, if found I simply update the quantity field associated to product id, I then set a variable addNew=no. I have used a cfelse if the product is not found set a variable addNew=yes. I realise what my problem is, if the cart has more than one product, the loop continues and obviously at some point it not find the product id and set the variable addNew=new, which will then as well as update the quantity, will also add the product into a new struct, ending up with product a qty 2 and product a qty 1.
This is the first time using arrays and struct in this way and am just finding my way through, so if my code is not efficient I apologise. Any pointers greatly appreciated;
<cfif arrayLen(session.mycart) GT 0>
<cfloop index="i" from="1" to="#arrayLen(session.mycart)#">
<!---check for existance of the id submitted--->
<cfif session.mycart[i].itemID eq form.itemID>
<!---if the id is matched update the quantity--->
<cfset session.mycart[i].quantity = form.quantity+session.mycart[i].quantity>
<cfset myTotal = form.itemCost*session.mycart[i].quantity>
<cfset session.mycart[i].totalPrice = myTotal>
<!---this will tell the add to cart function not add a new item--->
<cfset addNew = "no">
<cfelse>
<!---as this is a new item tell the add to cart function to add it--->
<cfset addNew ="yes">
</cfif>
</cfloop>
<cfelse>
<!---as the array is empty tel the add to cart function insert the product--->
<cfset addNew ="yes">
</cfif>
There's two things I can think of.
Firstly, you can simplify your code by getting rid of the outer length-check on the array: the loop will cover that for you.
Secondly, when you find and update your quantity (the TRUE part of the IF in the loop), you - at that point - can stop looking for the item. There's no point in continuing through the rest of the array looking for something you've already found. So use CFBREAK at that point to exit the loop.
Another thing to remember, is that one can use CFPARAM to set a variable only if it's not already been set. So if you set addNew to true, and subsequent calls to CFPARAM that variable will leave it as-is. However in this case the CFBREAK approach is better.
You should set the flag to true in the beginning, and then as you loop, if the product is found simply set the flag to false.
<!--- first set the flag to add item to cart --->
<cfset addNew = true>
<cfloop index="i" from="1" to="#arrayLen(session.mycart)#">
<!---check for existance of the id submitted--->
<cfif session.mycart[i].itemID eq form.itemID>
<!---if the id is matched update the quantity--->
<cfset session.mycart[i].quantity = form.quantity+session.mycart[i].quantity>
<cfset myTotal = form.itemCost*session.mycart[i].quantity>
<cfset session.mycart[i].totalPrice = myTotal>
<!---if the item is already in the cart, tell the add to cart function not add a new item--->
<cfset addNew = false>
</cfif>
</cfloop>
Sounds like what you need is a boolean value for each item in the cart, not just one. So you could even add that into the existing structure. Also even though ColdFusion will allow you to use the strings "yes" and "no" as boolean values, I think you'd be better to use true and false instead.
<cfif session.mycart[i].itemID eq form.itemID>
<!---if the id is matched update the quantity--->
<cfset session.mycart[i].quantity = form.quantity+session.mycart[i].quantity>
<cfset myTotal = form.itemCost*session.mycart[i].quantity>
<cfset session.mycart[i].totalPrice = myTotal>
<!---this will tell the add to cart function not add a new item--->
<cfset session.mycart[i].addNew = FALSE>
<cfelse>
<!---as this is a new item tell the add to cart function to add it--->
<cfset session.mycart[i].addNew =TRUE>
</cfif>
Related
Ok here's a thing, I have a form which when submitted can only update a table when a particular checkbox form variable values are identical, if one is different then it should proceed to update the table. So basically the user is selecting different rows and hitting the submit button.
So for example a good submission would be
form.claimid = 12,12,12,12,12
a bad submission would be
form.claimid = 1,5,77,33,4,
I'm not sure how to check in a list if all the values in the form.claimid list are identical or not?
I would appreciate any ideas on this.
You can also use CF's native ListRemoveDuplicates() if the version is sufficient (CF10, Railo/Lucee 4)
https://wikidocs.adobe.com/wiki/display/coldfusionen/ListRemoveDuplicates
<cfif ListLen(ListRemoveDuplicates(mylist)) eq 1>
I'll leave this as an alternate means for older CFs.
I'm not sure how to check in a list if all the values in the form.claimid list are identical or not?
There are a couple ways, most of which involve looping, but I'm going to show you a regular expression that can do this. We're going to get the first value using ListFirst, and use a ReReplace on that value to see if every other value matches.
I use a loop here but only as a demonstration.
<cfoutput>
<cfset mylist = "11,22,33,44|44,44,33,44|557,557,557">
<cfloop list="#mylist#" index="m" delimiters="|">
<cfset matchele = listfirst(m)>
<cfset eradicate = rereplace(m,"(?:(?:^|,)#matchele#(?=,|$))+","","ALL")>
"#m#", "#matchele#", "#eradicate#"<br />
</cfloop>
</cfoutput>
If you're stuck on CF 9 or lower, you could loop over the list and put the elements into a struct:
<cfset list_coll = structNew() />
<cfloop list="#mylist#" index="myitem">
<cfset list_coll[myitem] = "dummy" />
</cfloop>
<cfif structCount(list_coll) EQ 1>
<!--- This is good --->
<cfelse>
<!--- This is bad --->
</cfif>
The reason this works is that structs can't have duplicate keys.
I have a query called "rsUserRights" which returns User Rights like such:
UserID | Entity | Right
-----------------------
1 Note Create
1 Note Edit
1 Note Delete
This means that UserID '1' can create, edit, or delete a Note object.
All I want to do this store these permissions in a SESSION array so that the web applications know all the time what rights the logged in User has. So when a User is looking at a Note object in my applciation, the correct option to Create, Edit, or Delete will be available depending on what rights this User has.
The application needs to know what object the user has a right to, and then what is that right. Some users may not have rights expect read-only. How do I store this Entity-Right key-value pair in a ColdFusion structure?
All I have managed to do so far is this:
<cfset SESSION.Auth.UserRights = StructNew()>
<cfloop query="rsUserRights">
<cfset SESSION.Auth.UserRights.#rsUserRights.Entity#>
<cfset SESSION.Auth.UserRights.#rsUserRights.Entity#.#rsUserRights.Right#>
</cfloop>
Would the above work? Then using structkeyexits to find value-pairs? The problem I can see is that I could end up with a shed load of SESSION variables because the user may have hundreds of rights to hundreds of entities/objects. It will therefore create hundreds of SESSION variables and crash my server?
FIRST ATTEMPT SOLUTION
<cfset SESSION.Auth.UserRights = StructNew()>
<cfloop query="rsUserRights">
<cfset SESSION.Auth.UserRights[rsUserRights.Entity][rsUserRights.Right] = StructNew()>
</cfloop>
Then in my CFM pages I test for the existence of SESSION.Auth.UserRights.Note AND SESSION.UserRights.Note.Create for example
Is this ok??
That absolutely can work. Personally I would create a structure that contains the rights already defaulted to false and then on login update to true for those that match.
The reason being that you can then just check the perms instead of having to also check existence, etc. note that you should be doing a cflock but I'll leave that out of this example.
<cfset SESSION.Auth.UserRights = {}>
<cfloop index="AuthRight" list="Note,User,Documents,Application,SomethingElse">
<cfset SESSION.Auth.UserRights[AuthRight]={Create=false,Edit=false,Delete=false}>
</cfloop>
<cfloop query="rsUserRights">
<cfset SESSION.Auth.UserRights[rsUserRights.Entity][rsUserRights.Right]=true>
</cfloop>
If you are concerned about how much memory this will take, then rather than loading lots of falses into your structure, only load when true.
<cfset SESSION.Auth.UserRights = {}>
<cfloop query="rsUserRights">
<cfset SESSION.Auth.UserRights[Entity] ={}>
<cfif Right EQ 1>
<cfif SESSION.Auth.UserRights[Entity][Right] = true>
</cfif>
</cfloop>
Then all of your tests are for existence.
You could take the approach that unix web server / CHMOD permissions have. They use a single integer to denote what actions are possible for a given user.
http://www.zzee.com/solutions/unix-permissions.shtml
You could adapt your numbering to match the particular possible actions your app has - for example you might have 1 = add only, 2 = add & edit only, 3 = full add, edit & delete. Then for a user you'd just have a single digit entry for each type of object in your app. Your code might look like:
<cfset SESSION.Auth.UserRights = structNew()>
<cfset SESSION.Auth.UserRights.Note = 3>
<cfset SESSION.Auth.UserRights.User = 1>
<cfset SESSION.Auth.UserRights.Image = 2>
Checking if a user has the right permissions then involves some simple logic - depending on what actions you've got and how you've organised your numbering. Something like this:
<cfif SESSION.Auth.UserRights.Image GT 2>…
<cfif SESSION.Auth.UserRights.Note EQ 1>
If you're REALLY worried about space (and I wouldn't be) then you could just store those integers in a single list / array, and check for the integer in a particular position - but that might be tricky to keep track of as your app evolves.
<cfset SESSION.Auth.UserRights = "3,1,2,3">
<cfdump var="#listToArray(SESSION.Auth.UserRights)#">
<cfoutput>#listToArray(SESSION.Auth.UserRights)[1]#</cfoutput>
I am new to ColdFusion. Anyone know why this code is not working. When I leave the form null it is not showing 100 in the database.
<cfif isdefined("FORM.Percentage")>
<cfset Form.Percentage = #Form.Percentage#>
<cfelse>
<cfset Form.Percentage = 100>
</cfif>
<cfquery name="percent" datasource ="abc">
Insert into Employees
(Percentage)
Values
(#Form.Percentage#)
</cfquery>
If you have a textbox it is submitted to the form even if it's left blank, so you want to check if the field was left blank. If it was then you can set the default.
You'll also want to do some server side validation that the value is a number and use cfqueryparam for inserting your value into the database.
<cfif NOT len(trim(FORM.Percentage))>
<cfset Form.Percentage = 100>
<cfif>
<cfquery result="percent" datasource="abc">
Insert into Employees (Percentage)
Values (
<cfqueryparam cf_sql_type="cf_sql_integer" value="#Form.Percentage#">
)
</cfquery>
When using cfquery with an INSERT the name attribute doesn't provide anything. Using result would allow you to view some data about the query if needed, but generally it shouldn't be used.
You could also have dumped form to the screen by using <cfdump var="#form#"> to see what it was returning. If you want to check that the key exists for a radio button or checkbox you can use structKeyExists(form,'myCheckbox') rather than using isDefined().
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>
I am trying to write a function for a survey where it pulls questions from a database. The catch is that there are both active and unactive questions. I need older questions to show up when someone views the results from an old survey.
Here is the code I am trying within a CFC:
<cffunction name="getAllQuestions" access="public" returntype="query">
<cfargument name="survey" default=0>
<cfif len(#survey#) gt 0>
<cfquery name="getsdate" datasource="blah.database">
select * from past_survey
where survey_id = #survey#
</cfquery>
<cfreturn getsdate>
</cfif>
<cfquery name="getquestions" datasource="blah.database">
select * from pool_questions
<cfif len(#survey#) eq 0>
where active_flag='Y'
<cfelse>
where <cfqueryparam value="#dateformat
(getsdate.survey_date, "yyyy/mm/dd")#"> BETWEEN start_date AND
end_date
</cfif>
order by qtn_nb
</cfquery>
<cfreturn getquestions>
</cffunction>
#survey# is the survey id which is generated by the form. What I am trying to do is that if survey has a value to run query getsdate. Then the second query would run no matter if survey has a value or not. If there is not value it should pull all active questions. If there is a value then it should check if the survey date is between the start date and end date for past questions.
Any advice on how to make this work would be greatly appreciated. Thank you in advance!
<cffunction name="getAllQuestions" access="public" returntype="struct">
<cfargument name="survey" required="true" default="0" type="numeric">
<cfset var qryReturn = ""> <!---Always var scope your variables to prevent them from leaking to other functions --->
<cfset var structReturn = structNew()>
<cfset structReturn.pastSurvey = "">
<cfset structReturn.surveyQuestions = "">
<cfif survey GT 0>
<cfquery name="qryReturn" datasource="blah.database">
SELECT *
FROM past_survey
<!--- Always cfqueryparam to prevent SQL injection attacks & also always reference the scope to prevent confusion --->
WHERE survey_id = <cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.survey#">
</cfquery>
<cfset structReturn.pastSurvey = qryReturn>
<cfelse>
<cfquery name="qryReturn" datasource="blah.database">
SELECT *
FROM pool_questions
<cfif arguments.survey EQ 0>
WHERE active_flag = 'Y'
<cfelse>
WHERE <cfqueryparam value="#dateformat
(getsdate.survey_date, "yyyy/mm/dd")#"> BETWEEN start_date AND
end_date
</cfif>
ORDER BY qtn_nb
</cfquery>
<cfset structReturn.surveyQuestions = qryReturn>
</cfif>
<cfreturn structReturn>
</cffunction>
You probably should be doing this in two separate functions, but I will attempt to answer your question.
My code will return a struct of queries (you can change to an array if you prefer) that returns a past survey and the survey questions
Note: In your example code, you have a few bad practices.
You are checking the length of the survey value rather than checking the value itself.
If you want to ensure that survey always has a value regardless of if it is passed or not, set requried=true and give it a default value.
Use cfqueryparam to prevent sql injection attacks
Any variables created in the function need to be var scoped to prevent them from leaking to other cffunctions in the same cfcomponent. I always do this at the top. Yes, even the name you give a cfquery needs to be var scoped.
Since you are doing a return after your first query, if the survey value is greater than 0 it will never get to the second query where it has the date check.
I see the following problems you need to address.
First, your survey argument has a default value of 0 and you are doing conditional logic on the length of it. The length of "0" is 1, so that condition will always return true.
Next, you state that you want the 2nd query to run whether the first one runs or not, but you refer to a value from the 1st query in the 2nd one. That means if the 1st query does not run, the 2nd one will crash due to an undefined variable.
Next, dateformat returns a string. Applying it the way you do in the 2nd query is at best unnecessary, and at worse, and indication that you are storing the start and end dates in pool_questions as strings. If you are attempting to strip out the time portion of the datefield in the first query, ColdFusion has a cast() function for that.
Also, scope your variables. ie - instead of <cfif len(survey), do this <cfif len(arguments.survey).
Also, var your local variables. In this case, it's the names of your two queries.
That should get you started.