CFWheels - How to highight which posts a user has commented on? - coldfusion

I have a basic query to get all 'posts' in my database table 'posts'. Users who are logged in can comment on these posts. However, I want to be able to highlight (when viewing all posts) which ones the user has commented on before.
My query at the moment looks like this:
<cfset posts = model("post").findAll(
select = "
posts.postID,
postTitle,
postPoints,
postAuthority,
postCreated,
postDescription,
postCommentCount,
postUpVoteCount,
postDownVoteCount,
users.userID,
userName,
categories.categoryID,
categoryTitle,
categoryToken",
include = "user,category",
order = "postPoints DESC",
page = params.page,
perPage = params.pageQuantity
) />
I get all posts and join the user who created the post along with the category info etc.
How can I modify this, so that if the user is logged in, they'll be able to see which posts they've already commented on? The amount of comments the user has made isn't that important, just if the user has commented at all.
I'd prefer not to run a query on every post to check this. Ideally, it'd be good to join the count in with this query.
In addition to this, I have 3 queries in the posts model:
<cfset property(name="postUpVoteCount", sql="SELECT COUNT(*) FROM votes WHERE votes.postID = posts.postID AND votes.voteType = 1") />
<cfset property(name="postDownVoteCount", sql="SELECT COUNT(*) FROM votes WHERE votes.postID = posts.postID AND votes.voteType = 0") />
<cfset property(name="postCommentCount", sql="SELECT COUNT(*) FROM comments WHERE comments.postID = posts.postID AND comments.commentRemoved = 0") />
Could I utilise that in my query?
Any ideas?
Thanks,
Mikey.

Related

how can I check if a record already in database before submitting form in ColdFusion applications

<cfquery datasource = "myDb" name = "compare">
select *
from users
where cnic = #form.cnic#
</cfquery>
<cfif compare.cnic eq form.cnic>
<p> *CNIC already Exists </p>
</cfif>
I think you're misstating the problem. It should be more like,
"How can I show a form to add a record I know is not in the database?"
Please clarify if that is not the case.
Based on your code, I assume there's been a form submission from another page already. You're running a query to see if there is a record in the users table where cnic = #form.cnic#. If there was no previous form submission, then form.cnic wouldn't exist.
<cfquery datasource="myDb" name="compare">
select *
from users
where
cnic = #form.cnic#
</cfquery>
So when this page loads, you've done your "check if a record is already in the database" with the query named compare. Now all you need to do is check if there are 0 records in the query.
<cfif compare.recordcount EQ 0>
<!--- Display form here. ---->
</cfif>
If the query returns any records, then there is at least one record in the database, so no need to show the form or allow it to be submitted.
You can use bellow code in Jquery ajax calling
<cfquery datasource = "myDb" name = "compare">
select *
from users
where cnic = #form.cnic#
</cfquery>
<cfif compare.recordcount GT 0>
<p> *CNIC already Exists </p>
</cfif>
Depending on your database, too, there are other options. MySQL has some features for INSERT ON DUPLICATE KEY UPDATE (https://dev.mysql.com/doc/refman/8.0/en/insert-on-duplicate.html)
In MSSQL you can do:
IF NOT EXISTS (SELECT 1 FROM [users] u WITH (NOLOCK) WHERE cnic = <cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="#form.cnic#")
INSERT INTO [users].....
(My cfsqltype syntax might not be 100% correct, but always use . Always.
Those might give you some other ways to handle your scenario.

URL Redirection basis of category name

I am working on coldfusion application and using IIS URL rewriting. The URL rewriting rewrite my URL http://www.test.com/viewcategory/1 to http://www.test.com/viewcategory.cfm?category=1. It is working good and open the webpage with category details on the basis of categoryid.
Again i was rewrite my URL http://www.test.com/viewcategory/categoryname to http://www.test.com/viewcategory.cfm?category=categoryname and i want to open category detail page on the basis of categoryname but dont want to change my code i.e get details on the basis of categoryid. Is there any way in coldfusion to get internally the id of category on the basis of categoryname and display the page with the above http://www.test.com/viewcategory.cfm?category=categoryname.
Thanks,
Arun
<cfquery name="getCat" datasource="#request.dsn#">
select * from Categories
where CategoryName=<cfqueryparam value="#url.category#" cfsqltype="CF_SQL_VARCHAR">
</cfquery>
As one of the comments points out, you need to ensure category names are unique, or you could do like some sites do and include the numbers like viewcategory/Apples/1, viewcategory/Bananas/2 etc then you can rewrite the url to pass the ID, or to work with your current scheme, you could use urls like Bananas_1, Apples_2 and then:
<cfset url.cID = val(listlast(url.category,"_"))>
<cfquery name="getCat" datasource="#request.dsn#">
select * from Categories
where CategoryID=<cfqueryparam value="#url.cid#" cfsqltype="CF_SQL_INTEGER">
</cfquery>
Edit: Further, you could do this to prevent errant urls having mismatched category names and numeric IDs (Let's say a user missed the last digit when copying a url), though that's not an issue that most websites care about.
<cfset url.cID = val(listlast(url.category,"_"))>
<cfset url.category = listDeleteAt(url.category,listlen(url.category,"_"),"_")>
<!--- I chose not to use listfirst above in case your category name includes underscores --->
<cfquery name="getCat" datasource="#request.dsn#">
select * from Categories
where CategoryID=<cfqueryparam value="#url.cid#" cfsqltype="CF_SQL_INTEGER">
and CategoryName=<cfqueryparam value="#url.category#" cfsqltype="CF_SQL_VARCHAR">
</cfquery>
Edit: With minor changes for proper field names, and table names, these queries will work, but as a comment on my answer points out, you should list out the columns you want to retrieve rather than using *. Using * by default is a bad habit get into.

how to store query data in variables in ColdFusion for use later?

I am trying to retrieve and store ID's for each item retrieved from my table, so I can use these ID's later. I tried nesting the queries, but this didn't, work. Here is my first query:
<CFQUERY datasource="MyDSN" name="MAIN2"> SELECT * from order_items where orderID= #orderID#</CFQUERY>
Now, if I output this query it displays, 1 and 117 which are the two ID's I need.
My next query is:
<CFQUERY datasource="MyDSN" name="MAIN3">select c.catalogueID,
c.product_name,
c.product_price,
c.description,
p.productID
from products p
join product_catalogue c on c.catalogueid = p.catalogueid
where p.productid = "#productID#"</CFQUERY>
But it is telling me that productID is not defined, it is obviously empty. I am just getting started using ColdFusion, so I am not sure the best way to store the values I need so I use them again. I also need to loop the second query to run for each ID 1 and 117, so twice.
Any suggestions on how to accomplish this would be greatly appreciated.
Thanks
My basic rule is that if I find myself using queries to create other queries or looping over a query to execute other queries; it is time to consider combining the queries.
I'm not sure what field you are using in the MAIN2 query to feed the MAIN3 query. So, I put in "productID" in the query below. You may have to change it to fit your field name.
<CFQUERY datasource="MyDSN" name="MAIN3">select c.catalogueID,
c.product_name,
c.product_price,
c.description,
p.productID
from products p
join product_catalogue c on c.catalogueid = p.catalogueid
where p.productid IN (SELECT DISTINCT productID from order_items where orderID= <cfqueryparam value="#orderID#" cfsqltype="CF_SQL_INTEGER">)
</CFQUERY>
You could also change this query to utilize a "join" to connect [order_items] to the query.
Lastly, you should use the <cfqueryparam> tag for the where clauses; this helps protect your query from sql injection attacks.
Whenever I'm caching data for use later, I tend to ask myself how I'll be using that data, and whether it belongs in another data type rather than query.
For instance, if I'm wanting a bunch of data that I'm likely to access via ID, I can create a structure where the key is the ID, and the data is another structure of a dataset. Then I'll save this structure in application scope and only refresh it when it needs to be. This is zippy fast and so much easier to grab with
rather than querying for it every time. This is especially useful when the query that creates the original data set is kind of a resource hog with lots of joins, sub-queries, magical cross-db stored procedures, but the datasets returns are actually fairly small.
So creating your products structure would look something like this:
<CFQUERY datasource="MyDSN" name="MAIN3">
SELECT
c.catalogueID,
c.product_name,
c.product_price,
c.description,
p.productID
FROM products p
JOIN product_catalogue c
ON c.catalogueid = p.catalogueid
WHERE p.productid = <cfqueryparam value="#ProductID#" cfsqltype="cf_sql_integer">
</CFQUERY>
<cfset products = structNew() />
<cfset item = structNew() />
<cfloop query="MAIN3">
<cfif NOT structKeyExists(products, productID)>
<cfset item = structNew() />
<cfset item.catalogueID = catalogueID />
<cfset item.product_name = product_name />
<cfset item.product_price = product_price />
<cfset item.description = description />
<cfset products[productID] = structCopy(item) />
</cfif>
</cfloop>
<cfset application.products = structCopy(products) />

CFQuery 'WHERE' from results of another query?

I'll explain the 'real life' application of this so it's easier to understand.
I'm working on an eCommerce app that has a category structure. It starts at the top level and gradually moves down through subcategories. For example Home > Electronics > TVs > Plasma
I'm using a single page for this, showing the 'home' page if no category is defined, the subcategories if there are any, and finally the products if there are no subcategories.
This all works fine, however when I get to the 2nd part - displaying subcategories, the page is a little empty. Therefore, I'd like to display a selection of products that span all of the subcategories applicable.
This is where I'm struggling - in most cases, there will be a few subcategories. However, I'm not sure how to structure the 'where' query using the results of the previous query (code snippets below for reference).
I don't believe QofQ would be worth exploring, and I've made a vain attempt at doing something with substrings, without success.
Any pointers much appreciated!
<cfquery name="getcategories">
SELECT p.ID AS CategoryID, p.Cat_Name as CategoryName, p.Cat_Shortname, c.ID AS SubCategoryID, c.Cat_Name as SubCategoryName, c.Cat_Shortname AS SubCatShortname
FROM product_categories p LEFT JOIN product_categories c ON p.ID = c.SubcategoryOf
WHERE p.SubcategoryOf = 0
</cfquery>
<cfif IsDefined('url.cat')>
<!--- Look for additional subcategories --->
<cfquery name="getsubcategories">
SELECT *
FROM product_categories
WHERE Subcategoryof='#url.cat#'
</cfquery>
<cfquery name="getproducts">
SELECT *
FROM products
WHERE categoryid='#url.cat#'
ORDER BY RAND()
</cfquery>
</cfif>
Assuming your products table contains a subcategoryID of some kind you can use the following to get a list of sub category IDs from the query getsubcategories:
<cfset subCategoryIDs = valueList(getsubcategories.subCategoryID) >
This will give you a list of all subCategoryIDs. You can the feed this into the getproducts query like so:
<cfquery name="getproducts">
SELECT *
FROM products
WHERE subCategoryID in (<cfqueryparam cfsqltype="cf_sql_integer" value="#subCategoryIDs#" list="true">)
ORDER BY RAND()
</cfquery>
You should always cfqueryparam your query parameters.
If i understand your database structure, this query should return all products in all subcategories.
<cfquery name="getallproducts">
SELECT *
FROM products p LEFT JOIN product_categories pc ON p.categoryID = pc.ID
WHERE pc.Subcategoryof= <cfqueryparam cfsqltype="cf_sql_integer" value="#url.cat#">
</cfquery>
note: you really do want to use cfqueryparam here.

How can I insert an expiration date into a table and having messages expire without breaking functionality?

I’m using Coldfusion 9,0,0,251028 on Windows 7 64-bit, with a Microsoft Access 97 database.
The code is for an application that takes a submitted message from a form and posts it to a different page (breakingnews.cfm). The form values are inserted into the "news" table on a database, including the date the message was submitted (mes_dat) and the date it should expire (exp_dat).
On the database, the is_current and display columns have a default value of 0.
The form action is new_process.cfm, which does two things:
1) Does <cfset expdate = createdate(end_year, end_month, end_day)>. End_year, end_month, and end_day are dropdown selectors that set the expiration date. A problem I have had is expdate not being able to be inserted by post_breaking.cfm, but only when it is put online, on my local testing server there seems to be no problem.
2) Includes a template named "post_breaking.cfm", which runs these queries:
<cfquery name="get_init_info" datasource="#db#">
select id
from news
where is_current = 1
</cfquery>
<cfquery name="update_info_1" datasource="#db#">
update news
set is_current = 0, scrollshow = 0
</cfquery>
<cfif get_init_info.recordcount NEQ 0>
<cfquery name="update_info_2" datasource="#db#">
update news
set display = 1
where id = #get_init_info.id#
</cfquery>
</cfif>
<cfquery name="put_in_info" datasource="#db#">
insert into news
(is_current, display, mes_dat,mes_tim,mes_sub,mes_text,scrollshow, exp_dat)
values
(1,0, #createodbcdate(now())#, #createodbctime(now())#, '#subject#', '#message#',1, #expdate#)
</cfquery>
The message is then displayed on breakingnews.cfm if the column is_current on the news table is 1. This is code I have inherited, so I'm not sure how it does it, but the code only allows 5 messages at a time to have is_current = 1.
The part I'm trying to make happen is having a query run when breakingnews.cfm loads that checks if exp_dat is between now() and mes_dat to set is_current to 1, while still maintaining only 5 items having an is_current of 1.
When breakingnews.cfm is accessed it runs these queries:
<cfquery name="get_info" datasource="#db#">
select *
from news
where
<cfif not isdefined("id")>
is_current = 1
<cfelse>
id = #id#
</cfif>
order by mes_dat desc, mes_tim desc
</cfquery>
<cfquery name="add_exp" datasource="#db#">
UPDATE news
SET is_current = 1
WHERE now() BETWEEN mes_dat AND exp_dat
</cfquery>
<cfquery name="remove_exp" datasource="#db#">
UPDATE news
SET is_current = 0
WHERE now() NOT BETWEEN mes_dat AND exp_dat
</cfquery>
This will cause the messages that have an exp_dat between now() and mes_dat to be displayed and otherwise not display on breakingnews.cfm.
However, when a new message is submitted, upon accessing breakingnews.cfm the first time, only the newly submitted message is displayed.
If the page is refreshed, the messages that were filtered correctly will be displayed under the new message, but there will now be six items displaying on the page when there should only be five.
How can I get the correct amount of messages, and have them display the first time without needing a refresh?
I've gotten advice to replace the get_init_info query with
<cfquery name="get_init_info" datasource="#db#">
select id
from news
where exp_dat > now()
</cfquery>
but that caused all other rows in is_current and scrollshow to be changed to 0 after creating a new message (though the new message's scrollshow and is_current are 1).
My fist thought is to change how the system gets the news to display, but since you sppear not to have control of that, here's my suggestion. Please bear in mind that I've never used Access, so I'm using MS-SQ syntax.
Get the ids of the 5 items you want
select top 5 id
from bews
where exp_date > #createodbcdate(now)#
and display = 1
Now, set display = 0 on all items.
Finally, re-display the ids you found earlier.
update news
set display = 1
where id in #valuelist(query, "id")
If you have questions or corrections, feel free to comment.