I am doing cart for cafe, everything works fine except increasing quantity. When I add a food to cart...it just dublicating the same food...instead of increase the quantity. I want to make the solution that will check...If the food already in cart...instead of adding it will just increase the quantity. Below is my controller that adds food to cart. Any help will be appreciated
def add_to_cart
add_to_session(params[:cafe], params[:food])
redirect_to about_cafe_path(Cafe.find(params[:cafe]))
end
def show_cart
#cart = session[:cart]
end
def clear_cart
session[:cart] = {}
redirect_to show_cart_path
end
def remove
destroy_food(params[:cafe], params[:food])
redirect_to :back
end
private
def add_to_session(cafe_id, food_id)
if session[:cart][food_id].present?
session[:cart][cafe_id][food_id].quantity += 1
else
session[:cart][cafe_id].push food_id
end
end
def destroy_food(cafe_id, food_id)
session[:cart][cafe_id].delete food_id
end
you should check if session[:cart][cafe_id][food_id].present? instead of if session[:cart][food_id].present?
Related
I'm trying to call some ActiveRecord methods in OrdersController#new action.
I've tested the code in Rails Console and it works the way I intended.
But in the controller action it does not produce the same
order has_many cart_items
cart has_many cart_items
cart_items belong_to order cart_items
belong_to cart
cart_items belong_to product
product has_many cart_items
def new
#order.new
#order.cart_items = current_cart.cart_items
#order.save
current_cart.cart_items.destroy_all
end
Now current_cart is an application_controller method that checks if the current user has a shopping cart. If it does it pulls that cart from the database and if the user does not then it creates a new cart for the user. What I am trying to do here is when the user finalizes their order I'm trying to transfer the cart_items from current_cart to orders then clear the shopping cart.
When I do this in rails console, it gives me what I want. Order with cart_items that were in current_cart, and after I run destroy_all on the cart I have an empty active record association array.
When I test this in my controller both Order and Cart return an empty active association array.
What is going on here?
#application controller method of finding current_users cart
def current_cart
# if user is logged in
if current_user
#user = current_user
# checking user to see if account is confirmed and verified
if #user.confirmed_at != nil
# checking if user already has cart in cart database
if Cart.find_by(users_id: #user.id) != nil
# find a row in the database where users_id: equal to #user.id
# where clause does not work here
cart = Cart.find_by(users_id: #user.id)
session[:cart_id] = cart.id
cart.save
#establish Cart session cart for user
Cart.find(session[:cart_id])
else
# create a new Cart Object for user.assign current_user's id to cart object
cart = Cart.new
cart.users_id = #user.id
# save it to get cart id assign session[:cart_id] == cart.id
cart.save
session[:cart_id] = cart.id
end
end
end
end
class CartItemsController < ApplicationController
before_action :set_cart_item, only: [:show, :edit, :update, :destroy]
# scope for most_recent and subtotal
# find out if rails sorts on update column cuz this is annoying.
def create
# grabbing cart from application controller current_cart method
#cart = current_cart
# session[:cart_id] = #cart.id
# individual product items get added to cart item and added to cart and saved
#cart_item = #cart.cart_items.build(cart_item_params)
#cart.save
end
def update
#cart = current_cart
# finding cart_items by cart_id
#cart_item = #cart.cart_items.find(params[:id])
# #cart_items.order(:id)
#cart_item.update_attributes(cart_item_params)
#cart_items = #cart.cart_items.order(:id)
# redirect 'cart_show_path'
#cart.save
end
def destroy
#cart = current_cart
#cart_item = #cart.cart_items.find(params[:id])
#cart_item.destroy
#cart_items = #cart.cart_items
#cart.save
end
private
def set_cart_item
#cart_item = CartItem.find(params[:id])
end
def cart_item_params
params.require(:cart_item).permit(:cart_id, :product_id, :unit_price, :quantity, :total_price)
end
end
When you say you are transferring the cart_items from current_cart you are passing on the objects, you are not creating new cart_items (which means the database ids are same) and when you do current_cart.cart_items.destroy_all it is deleting them from the database. See ActiveRecord::Relation#destroy_all
For you use case, its enough if you just do
def new
#order.new
#order.cart_items = current_cart.cart_items
#order.save
current_cart.cart_items = []
end
Alright, figured it out.
I was using #order.save which was not processing the error message!!!!!!!!
After #order.save! it gave me the validation error in another model.
I commented that out and it worked.
I iterated current.cart_items and assigned the cart_id to nil and order_id to #order.id essentially clearing the cart and "transferring" the items over.
I couldn't figure out a way using destroy_all though. I think this is impossible like #Sri said.
Thanks alot!
So the end code was this:
#order = Order.create!(users_id: current_cart.users_id)
current_cart.cart_items.each do |item|
item.order_id = #order.id
item.save!
end
if #order.save
current_cart.cart_items.each do |item|
item.cart_id = nil
item.save!
end
So I've built a system of products and a shopping cart in my rails app. The goal I have is to associate saved products from a cart with user model.
I'm struggling to find a solution on how could I save ids of the items from a cart from each current_user to the column from user's model?
So in my cart view page there is a list of all added products in a cart and I want to add a save button which will save those products by their ids.
As an example, if current_user ads three products in the cart with ids 1,2,3 and clicks on "Save" button in a cart, I want to be able to save those three ids by integers to the "addedproducts" column of the current_user.
This is part of my cart_controller:
def additems
id = params[:id]
if session[:cart] then
cart = session[:cart]
else
session[:cart] = {}
cart = session[:cart]
end
if cart[id] then
cart[id] = cart[id] + 1
else
cart[id] = 1
end
redirect_to :action => :index
end
and a part of my product controller(generated through scaffold):
class ItemsController < ApplicationController
before_action :set_item, only: [:show, :edit, :update, :destroy]
respond_to :html, :json, :js
def index
#products = Product.where(availability: true)
end
def show
end
def new
#product = Product.new
end
def edit
end
def create
#product = Item.new(item_params)
#product.save
respond_with(#product)
end
Is this possible to achieve?
(I'm using standard Devise setup if it's of any help)
How you want to save multiple items to a single column "addedproducts" ? I think the best solution is adding a foreign key to the cart. Making the cart belongs_to a user. And the user having many carts. Just the same way you have probably created a Cart model and an Item model too. With the proper relationship between the two... (If you do that you will have to add a new migration to the cart model with a reference to User.)
I am following the book Agile Web Development with Rails - A Pragmatic Guide. On Iteration C1, creating a cart, I am getting the following error:
NoMethodError in StoreController#display_cart
undefined method `items' for #<Hash:0xb5cf041c>
Extracted source (around line #15):
13 def display_cart
14 #cart = find_cart
15 #items = #cart.items
16 end
17
18 private
Here are the source files:
routes.rb
Rails.application.routes.draw do
get 'store' => 'store#index'
get 'add_item_to_cart' => 'store#add_to_cart'
get 'display_cart' => 'store#display_cart'
resources :products
store_controller.rb
class StoreController < ApplicationController
def index
#products = Product.salable_items
end
def add_to_cart
product = Product.find(params[:id])
#cart = find_cart
#cart.add_product(product)
redirect_to display_cart_path
end
def display_cart
#cart = find_cart
#items = #cart.items
end
private
def find_cart
session[:cart] ||= Cart.new
end
end
cart.rb
class Cart
attr_reader :items
attr_reader :total_price
def initialize
#items = []
#total_price = 0.0
end
def items
#items
end
def add_product(product)
#items << LineItem.for_product(product)
#total_price += product.price
end
end
line_item.rb
class LineItem < ActiveRecord::Base
belongs_to :product
def self.for_product(product)
item = self.new
item.quantity = 1
item.product = product
item.unit_price = product.price
item
end
end
I reach the action display_cart from the add_to_cart action of StoreController. Even though I have a def items in cart.rb, why am I getting a NoMethodError?
Assuming you have stored cart id in the session you can do following changes in your method
def find_cart
Cart.find_by_id(session[:cart_id]) ||= Cart.new
end
Try this to return your Cart instance:
def find_cart
Cart.find(session[:cart]
rescue ActiveRecord::RecordNotFound
cart = Cart.new
session[:cart] = cart.id
end
This is almost the same code as in your book. Semantically you could call that function current_cart.
By storing only the carts id and using the ActiveRecord find() method, rescuing with calling Cart.new , you will always return a cart instance.
Edit
if you do not implement Cart as a Model, you will (logically) never be able to instantiate an object of it.
This is a good article about handling sessions in Ruby on Rails. As you will see, the session itself is a Hash by 'nature', further down you will find some alternative solutions for storing sessions in your rails app.
The problem is in
def find_cart
session[:cart] ||= Cart.new
end
your session[:cart] is a Hash, and thus does not get Cart.new, and when you call #cart.items it gets called on Hash not on a Cart object
In your display_cart method: you're doing #cart = find_cart which may return a hash instead of an object as in the find_cart methhod you're doing session[:cart] ||= Cart.new which may return a Hash.
The Capturing an Order chapter in Agile Wed Development with Rails uses the following code:
# orders_controller.rb
def create
#order = Order.new(params[:order])
#order.add_line_items_from_cart(current_cart)
if #order.save
Cart.destroy(session[:cart_id])
session[:cart_id] = nil
redirect_to store_url
else
#cart = current_cart
render 'new'
end
end
# order.rb
def add_line_items_from_cart(cart)
cart.line_items.each do |item|
item.cart_id = nil
line_items << item
end
end
How does the cart retain its line items when there's a validation error? The add_line_items_from_cart runs before we know whether the order is valid or not. It associates the line items with the order, then sets the item.cart_id to nil:
item.cart_id = nil
self.line_items << item # self is an instance of `Order`.
When I submit an empty form then view the cart, all the line items are still there. How is this possible? What have I missed?
The cart is only destroyed when the order is saved. So I guess it will never lose the items. What happens is when you create the order it probably destroy the current cart and the it creates a new one.
I'm trying to integrate eCommerce functionality into my rails app, and am having trouble creating a new order. I start with a cart, which has_many orders, which has_many transactions. The first field in my order database is cart_id. I need to be able to access information in the cart (such as total_price) from the view/order/new.html.erb.
Where would be the best place to build this relation, and how? I can find the cart through the session id, but I don't know how to build the relationship. I was thinking in the order model, in the new action, somthing like so?
def new
#order = Order.new
current_cart.#order.build
Defined in my application controller is the function current_cart
def current_cart
Cart.find(session[:cart_id])
rescue ActiveRecord::RecordNotFound
cart = Cart.create
session[:cart_id] = cart.id
cart
end
UPDATE
Here is my new and create function, and where I need the value
def new
#order = Order.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #order }
end
end
# POST /orders
# POST /orders.json
def create
#order = Order.new(params[:order])
# THIS IS WHERE I HAVE TRIED TO BUILD THE RELATIONSHIP
# I have tried current_cart.orders.build, #order.cart = current_cart, and
# #order = current_cart.build_order(params[:order])
#order.ip_address = request.remote_ip
if #order.save
if #order.purchase
render :action => "success"
else
render :action => "failure"
end
else
render :action => 'new'
end
end
and this is where I need to access the cart in the model
def price_in_cents
(cart.total_price*100).round
end
And I always get an exception caught for an undefined function, either the build function, or the total price function
In the Order model you have the cart_id, so define the relation there:
belongs_to :cart
You can also define the relation in the Cart model, additionally:
has_many :orders
After that you can simply add new orders to your current basket:
#order = Order.new
#order.cart = current_cart
EDIT:
Maybe there is an other Problem with the current_cart method.
Try:
#order.cart_id = session[:cart_id]
I made a video about this: http://www.ror-e.com/info/videos/6
I actually separate the cart from the order. So basically The cart has_many cart_item and the order has_many order_items. I'd love to help out more. Feel free to contact me directly. I'd love to discuss the pro's & con's of different approaches.
In your create action:
#order = current_cart.build_order(order_params)
and add strong params:
private
def order_params
params.require(:order).permit(:first_name, :last_name, :card_type, :card_expires_on)
end