I’m implementing a small voting system on my website. I came up with three implementation methods that I would like your feedback on.
I want to give my users the ability to cast several types of votes on some user generated content. It’s micro Q&A about games, not unlike SO’s and their vote system, on a much smaller and specialized scale.
After toying with the following methods I can’t decide which is best.
Method 1: Uses URL params and forms
Method 2: Uses URL params and jQuery
Method 3: Uses ether one of the above, but retrieves its info from the database
The schema assumes both Q’s and A’s are a post object with a different postTypeId, and the following two tables:
voteTypes(id, voteTypeId, voteName)
votes(id, postId, parentId, userId, ownerUserId, voteTypeId)
- parentId represents the id of the
parent post. If the post being voted
on is an answer post, it is used to
ensure a question post (postTypeId=1)
can have only one accepted answer.- ownerUserId represents the userId of
the post’s (being vote on) owner. It
is compared against the userId, which
comes from the session, to ensure a
user can’t vote on his or her own
posts.- userId comes from the session and represents the person casting the
vote.
Method 1 Create a vote form in the view as the query loops over each post. Use hidden fields to capture the data:
<input type="hidden" value="@voteTypeId" etc...
The postId, parentId, and ownerUserId will come from the query being output to the view. The userId will come from the session.
Disadvantages:
1. Users can manipulate data. A user can accept an answer to a question he did not ask since ownerUserId is set at the view level.
2. Cumbersome: I would have to create as many forms as there are posts in the view. Each post will have 4 forms. A page with 10 posts can have 40 forms.
Advantages:
1. It’s simple.
Method 2 Use anchors with custom data tags and jQuery to construct the vote URL.
<a data-vote-type-id="@voteTypeId" data-post-id="@postId" etc...
The ownerUserId, postId, parentId, voteTypeId will come from the URL. The userId from the session.
Advantages
1. Light weight and no forms. One jQuery call to submit any vote, as such:
var data = 'voteTypeId='$(this).data("vote-type-id") etc. Submitted over ajax!
Disadvantages:
1. JS disabled = no vote.
2. Data can be manipulated since it’s being sent through the URL.
Method 3 Submit only the voteTypeId and the postId over URL, using either methods 1 or 2. Use the postId to query the db and verify the post object being voted on exists. This way I can also verify the ownerUserId and parentId of the post.
If the post is an object, create a newVote object.
The userId will come from the session. The postId and voteTypeId will come from the URL.
The parentId and ownerUserId will come from the post object I queried for.
Advantages:
1. Data shielded from user manipulation as the presence of the post can be verified, and so can be it’s ownerUserId and parentId.
Disadvantages:
1. Laborious. Asking the database to find the post and retrieve details already available at the view level seems unnecessary.
2. The data is denormalized in some instances, so after a successful vote (an up vote, for example) I have to increment the posts table via object callbacks (add +1 to the existing number of up votes, for example), which is yet another call to the database with information that was already available at the view, and then the controller levels.
Other things I have not considered:
1. Finding if a vote already exists and toggling it, which will require yet another query. 2. Validation combinations are a nightmare.
I’m looking for feedback or additional ideas. So please share!
Many thanks!
I’d go with Method 2, but with some changes.
Server side
Hooks for casting a vote, changing a vote etc. those return simply
trueorfalsedepending on the success.Input PostID, VoteTypeID, UserID etc. you can’t leave this out, if this is your bottleneck rethink the database model (if you can’t fit it into a relational DB use noSQL stuff).
If you leave validation out, I’m gonna personally down rank your site into oblivion, do server side validation.
ALWAYS.
Client side
The Client gets the information from the view, then it can issue the specific calls to the server, we assume a non-malicious user here, if that’s not the case he will simply fail at the back end.
As soon as someone casts/toggles we do the following things:
falsewe undo the action and give an error messageBenefits of this approach: