Polymorphic Associations in Rails - Part 02
Ở phần trước mình đã viết bài tìm hiểu về đa hình trong Rails, trình bày tóm tắt và đưa ra ví dụ về mối quan hệ này. Phần tiếp theo này mình viết thêm chi tiết hơn về một ví dụ sử dụng mối quan hệ này. Bài toán đặt ra như sau: Bạn có một website bán hoa (gọi là shop hoa Tết) chẳng hạn, khi ấy ...
Ở phần trước mình đã viết bài tìm hiểu về đa hình trong Rails, trình bày tóm tắt và đưa ra ví dụ về mối quan hệ này. Phần tiếp theo này mình viết thêm chi tiết hơn về một ví dụ sử dụng mối quan hệ này.
Bài toán đặt ra như sau: Bạn có một website bán hoa (gọi là shop hoa Tết) chẳng hạn, khi ấy người dùng vào website của bạn và ghé thăm các mẫu hoa trưng bày trên website của bạn và bạn muốn cho họ có thể ghim lại các mẫu hoa mà họ thích vào danh sách yêu thích của họ.
Bài viết này mình sẽ xây dựng chức năng này trên Rails app.
Create new app (ex: r_shop)
rails new r_shop
Mình dùng gem devise cho User login, logout, v..v.. cho hệ thống này.
Thêm gem 'devise' vào Gemfile
# Gemfile gem 'devise'
bundle install
Chạy lệnh sau để install devise cho app của mình.
rails generate devise:install
Tạo model User
rails generate devise User
Migration database
rake db:migrate
Phần này mình sẽ tạo một model để lưu trữ các mẫu hoa, mình gọi tên là Post, mình ví dụ mỗi mẫu hoa có tiêu đề (title) và phần mô tả (content). Tùy theo mô hình của bạn mà thiết kế khác.
Create Post.rb
rails g scaffold post title:string content:text
Tiếp theo, mình gọi danh sách yêu thích của người dùng tên là Wishlist. Tạo model Wishlist.rb
rails g scaffold wishlist user_id:integer wishlistable_id:integer wishlistable_type:string
Cài đặt quan hệ cho models
#wishlist.rb class Wishlist < ActiveRecord::Base belongs_to :wishlistable, polymorphic: true end
#post.rb class Post < ActiveRecord::Base has_many :wishlists, as: :wishlistable end
#user.rb class User < ActiveRecord::Base has_many :posts end
Phần này mình sẽ tạo giả một số bản ghi trong cơ sở dữ liệu để tiện cho việc demo.
Sử dụng gem faker
# Gemfile gem 'faker', '1.4.2'
run command to install
bundle install
Mình sẽ tạo ra 1 user và 50 posts trong cơ sở dữ liệu.
# seeds.rb User.create (email: "admin@test.com", password: "12345678", password_confirmation: "12345678", ) 50.times do title = Faker::Lorem.sentence(2) content = Faker::Lorem.sentence(4) Post.create title: title, content: content, user_id: 1 end
Import vào database
rake db:seed
Đầu tiên mình viết 1 hàm để kiểm tra xem mẫu hoa đó đã được đưa vào danh sách yêu thích của người dùng hay chưa.
# post.rb def wish_by? user wishlists.exists? user_id: user end
Tiếp theo, tạo chức năng trên giao diện web. Tại trang xem mẫu hoa (show post) mình tạo nút bấm để đưa mẫu này vào danh sách yêu thích hoặc bỏ ra khỏi danh sách yêu thích nếu đã có trong danh sách yêu thích của người dùng.
#app/views/posts/show.html.erb <% if user_signed_in? %> <div id="wish_box"> <%= render "shared/actions", obj: @post %> </div> <% end %>
Tạo file render actions bên trên.
#shared/_actions.html.erb <% if obj.wish_by? current_user %> <% wishlist = Wishlist.find_by_wishlistable_id(obj.id) %> <%= form_for wishlist, html: {method: :delete}, remote: true do |f| %> <button type="submit" class="btn add-to-wish"> <i class="fa fa-times"></i> <%= t("wishlist.remove") %> </button> <% end %> <% else %> <%= form_for obj.wishlists.build(user_id: current_user.id), remote: true do |f| %> <%= f.hidden_field :user_id %> <%= f.hidden_field :wishlistable_id, value: obj.id %> <%= f.hidden_field :wishlistable_type, value: obj.class.name %> <button type="submit" class="btn add-to-wish"> <i class='fa fa-heart-o'></i> <%= t("wishlist.add") %> </button> <% end %> <% end %>
Chỉnh sửa phần controller wishlist_controller.rb
# wishlist_controller.rb def create @object = params[:wishlist][:wishlistable_type] @obj = @object.constantize.find(params[:wishlist][:wishlistable_id]) @wishlist = Wishlist.new(wishlist_params) @wishlist.user_id = current_user.id respond_to do |format| if @wishlist.save format.html { redirect_to @wishlist, notice: 'Wishlist was successfully created.' } format.js else format.html { render :new } format.json { render json: @wishlist.errors, status: :unprocessable_entity } end end end def destroy @obj = Wishlist.find(params[:id]).wishlistable @wishlist.destroy respond_to do |format| format.html { redirect_to wishlists_url, notice: 'Wishlist was successfully destroyed.' } format.js end end
Như vậy là đã xong, mình đã xây dựng được chức năng wishlistable sử dụng ajax.