Khi tạo một trang web cần chức năng voting, bạn có hai lựa chọn chính:
Hãy so sánh các lựa chọn này.
Users
-----
- id (Primary Key)
- username
- password_digest
- created_at
Polls
-----
- id (Primary Key)
- title
- description
- created_by (Foreign Key -> Users.id)
- created_at
- ends_at
Options
-------
- id (Primary Key)
- poll_id (Foreign Key -> Polls.id)
- content
Votes
-----
- id (Primary Key)
- user_id (Foreign Key -> Users.id)
- option_id (Foreign Key -> Options.id)
- created_at
Những điểm chính về cơ sở dữ liệu này:
Cơ sở dữ liệu này cho phép:
Bạn có thể mở rộng cơ sở dữ liệu này để bao gồm nhiều tính năng hơn như:
Khi thiết kế hệ thống của riêng bạn, tập trung vào các yếu tố chính này:
# app/models/user.rb
class User < ApplicationRecord
has_many :votes
end
# app/models/poll.rb
class Poll < ApplicationRecord
has_many :options
has_many :votes, through: :options
end
# app/models/option.rb
class Option < ApplicationRecord
belongs_to :poll
has_many :votes
end
# app/models/vote.rb
class Vote < ApplicationRecord
belongs_to :user
belongs_to :option
end
Thiết lập routes trong config/routes.rb:
Rails.application.routes.draw do
resources :users, only: [:new, :create]
resources :polls do
resources :votes, only: [:create]
end
end
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
helper_method :current_user
def current_user
@current_user ||= User.find(session[:user_id]) if session[:user_id]
end
end
# app/controllers/users_controller.rb
class UsersController < ApplicationController
def new
@user = User.new
end
def create
@user = User.new(user_params)
if @user.save
session[:user_id] = @user.id
redirect_to polls_path
else
render :new
end
end
private
def user_params
params.require(:user).permit(:username, :password)
end
end
# app/controllers/votes_controller.rb
class VotesController < ApplicationController
def create
@poll = Poll.find(params[:poll_id])
@option = @poll.options.find(params[:option_id])
if !current_user.votes.exists?(option: @option)
@vote = current_user.votes.create(option: @option)
redirect_to @poll, notice: 'Vote recorded!'
else
redirect_to @poll, alert: 'You have already voted.'
end
end
end
<!-- app/views/polls/show.html.erb -->
<h1><%= @poll.title %></h1>
<% @poll.options.each do |option| %>
<p>
<%= option.content %>
<%= button_to 'Vote', poll_votes_path(@poll, option_id: option.id), method: :post %>
</p>
<% end %>
<h2>Results:</h2>
<% @poll.options.each do |option| %>
<p><%= option.content %>: <%= option.votes.count %> votes</p>
<% end %>
Đây là một triển khai cơ bản. Bạn cần thêm các tính năng như tạo cuộc bỏ phiếu, xử lý các trường hợp biên (edge case), và cải thiện giao diện người dùng. Hãy nhớ thực hiện các biện pháp bảo mật thích hợp, chẳng hạn như xác thực đầu vào và bảo vệ chống lại các cuộc tấn công CSRF.