This is a continuation of the series "Creating an EC site with Rails 5 ⑧" (https://qiita.com/GreenFingers_tk/items/0d2caea916a82e234af2), which creates an EC site where you can shop at a fictitious bakery. This time we will make a cart function. The appearance of the EC site is finally ready.
https://github.com/Sn16799/bakeryFUMIZUKI
The models related to the cart function are mainly Customer and Product. There is no such thing as the main body of the cart in the cart function, and when you select a product and press the "Add to cart" button, one data item is registered for each product. The CartItem model acts as an intermediate table that stores only the Customer and Product IDs and the number of products.
Controller
app/controllers/cart_items_controller.rb
class CartItemsController < ApplicationController
before_action :authenticate_customer!
before_action :set_cart_item, only: [:show, :update, :destroy, :edit]
before_action :set_customer
def create
@cart_item = current_customer.cart_items.build(cart_item_params)
@current_item = CartItem.find_by(product_id: @cart_item.product_id,customer_id: @cart_item.customer_id)
#If there is no same product in the cart, add a new one, if there is, add it to the existing data
if @current_item.nil?
if @cart_item.save
flash[:success] = 'The item has been added to the cart!'
redirect_to cart_items_path
else
@carts_items = @customer.cart_items.all
render 'index'
flash[:danger] = 'The item could not be added to the cart.'
end
else
@current_item.quantity += params[:quantity].to_i
@current_item.update(cart_item_params)
redirect_to cart_items_path
end
end
def destroy
@cart_item.destroy
redirect_to cart_items_path
flash[:info] = 'I canceled the item in the cart.'
end
def index
@cart_items = @customer.cart_items.all
end
def update
if @cart_item.update(cart_item_params)
redirect_to cart_items_path
flash[:success] = 'The items in the cart have been updated!'
end
end
def destroy_all #Erase all items in the cart
@customer.cart_items.destroy_all
redirect_to cart_items_path
flash[:info] = 'I emptied my cart.'
end
private
def set_customer
@customer = current_customer
end
def set_cart_item
@cart_item = CartItem.find(params[:id])
end
def cart_item_params
params.require(:cart_item).permit(:product_id, :customer_id, :quantity)
end
end
If you just write save in the create action, when you try to buy the same product, the same product will be registered as different data, such as ** "Pain de mie 1, Pain de mie 2, Pain de mie 1, ..." **. This happens due to the structure of the table, but since it is convenient to be able to display the additional items added to the cart later, the if statement separates the processing.
Also, when I wanted to be able to cancel the items in the cart individually and empty the contents of the cart all at once, it is convenient as destroy_all. I found a method.
View
html:app/views/cart_items/index.html.erb
<div class="col-lg-10 offset-lg-1 space">
<div class="container-fluid">
<!--title+Erase all method-->
<div class="row">
<div class="col-lg-4">
<h2>
<span style="display: inline-block;">shopping</span>
<span style="display: inline-block;">cart</span>
</h2>
</div>
<div class="col-lg-4">
<%= link_to 'Empty the cart', destroy_all_cart_items_path, method: :delete, class: 'btn btn-danger' %>
</div>
</div>
<!--List of products in the cart-->
<div class="d-none d-lg-block">
<div class="row space">
<div class="col-lg-5"><h4>Product name</h4></div>
<div class="col-lg-2"><h4>Unit price (tax included)</h4></div>
<div class="col-lg-2"><h4>quantity</h4></div>
<div class="col-lg-2"><h4>subtotal</h4></div>
</div>
</div>
<% sum_all = 0 %>
<% @cart_items.each do |cart_item| %>
<div class="row space-sm">
<div class="col-lg-3">
<%= link_to product_path(cart_item.product) do %>
<%= attachment_image_tag(cart_item.product, :image, :fill, 100, 100, fallback: "no_img.jpg ") %>
<% end %>
</div>
<div class="col-lg-2">
<%= link_to product_path(cart_item.product) do %>
<%= cart_item.product.name %>
<% end %>
</div>
<div class="col-lg-2">
<%= price_include_tax(cart_item.product.price) %>
</div>
<div class="col-lg-2">
<%= form_with model: cart_item, local: true do |f| %>
<%= f.number_field :quantity, value: cart_item.quantity, min:1, max:99 %>
<%= f.submit "Change", class: "btn btn-primary" %>
<% end %>
</div>
<div class="col-lg-2">
<%= sum_product = price_include_tax(cart_item.product.price).to_i * cart_item.quantity %>Circle
<% sum_all += sum_product %>
</div>
<div class="col-lg-1">
<%= link_to "delete", cart_item_path(cart_item), method: :delete, class: "btn btn-danger"%>
</div>
</div>
<% end %>
<!--total fee+Information input-->
<div class="row space">
<div class="col-lg-2 offset-lg-7 space-sm">
<%= link_to "Continue shopping", customer_top_path, class: "btn btn-danger "%>
</div>
<div class="col-lg-3 space-sm">
<div class="row">
<h4>total fee:<%= sum_all %>Circle</h4>
</div>
</div>
</div>
<div class="row space">
<div class="col-lg-3 offset-lg-9">
<%= link_to "Proceed to information input", new_order_path, class: "btn btn-danger btn-lg" %>
</div>
</div>
</div>
</div>
The subtotal for each product and the total for all products are displayed by incorporating a calculation formula in the each statement on the view. In general, it seems that it is not desirable to perform detailed calculations and branch processing in view, so is there any other good method?
app/helpers/application_helper.rb
def price_include_tax(price)
price = price * 1.08
"#{price.floor}Circle"
end
This is the helper used to display the tax-included price of the product in the above HTML. The part after the decimal point is truncated by floor. For more information on decimal point processing, see here.
app/assets/stylesheets/application.scss
.space-sm {
padding-top: 20px;
}
The implementation of features has become a bit more complicated, and it's finally getting interesting. Even if you look inside the site, you can feel like shopping by adding products to the cart. After that, if you create around Order (order information), the customer site is completed!
However, in this series, the admin site is also created by myself, so the amount of code is about half. However, the function of the Order model was extremely difficult to experience when I made it last time, so if I could do that, I would be able to do something about it.
Now, can I explain the most difficult Order model in an easy-to-understand manner? Continue to next time!
Pikawaka [Ruby] Round, round up, and round down by specifying the number of digits after the decimal point!
Recommended Posts