Reputation: 83
I've got a Rails cart for a customer which works but only on the second attempt of trying to 'add-to-cart'.
So every time I click 'add to cart', the cart is empty the first time. But on second attempt, the item is added to cart.
What am I doing wrong?
Here's the customer controller code:
class Customer::CartsController < ApplicationController
before_action :authenticate_user!
def show
@cart = if current_user
current_user.cart ||= Cart.find_by(session[:cart_id])
session[:cart_id] = nil if current_user.cart.purchased_at
end
if session[:cart_id].nil?
current_user.cart = Cart.create!(user_id: params[:id])
session[:cart_id] = current_user.cart.id
end
@cart = current_user.cart
end
end
Regular Carts controller
class CartsController < ApplicationController
skip_before_action :authorize, only: [:create, :update, :destroy]
before_action :set_cart, only: [:show, :edit, :update, :destroy]
rescue_from ActiveRecord::RecordNotFound, with: :invalid_cart
def index
@carts = Cart.all
end
def show
end
def new
@cart = Cart.new
end
def edit
end
def create
@cart = Cart.new(cart_params)
respond_to do |format|
if @cart.save
format.html { redirect_to @cart, notice: 'Cart was successfully created.'}
format.json { render :show, status: :created, location: @cart }
else
format.html { render :new }
format.json { render json: @cart.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if @cart.update(cart_params)
format.html { redirect_to @cart, notice: 'Cart was successfully updated.' }
format.json { render :show, status: :ok, location: @cart }
else
format.html { render :edit }
format.json { render json: @cart.errors, status: :unprocessable_entity }
end
end
end
def destroy
@cart.destroy if @cart.id == session[:cart_id]
session[:cart_id] = nil
end
respond_to do |format|
format.html { redirect_to root_path, notice: 'Your Cart is currently empty.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_cart
@cart = Cart.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def cart_params
params[:cart]
end
def invalid_cart
logger.error "Attempt to access invalid cart #{params[:id]}"
redirect_to root_path, notice: 'Invalid cart'
end
end
Line Items controller create method
def create
product = Product.find(params[:product_id])
@line_item = @cart.add_product(product.id, params[:size])
respond_to do |format|
if @line_item.save
format.html { redirect_to customer_cart_path }
format.json { render :show, status: :created, location: @line_item }
else
format.html { render :new }
format.json { render json: @line_item.errors, status: :unprocessable_entity }
end
end
end
Any help would be appreciated. Thanks in advance!
Upvotes: 0
Views: 541
Reputation: 195
It's hard to tell exactly what is going on here without seeing the full code. For instance, the question is primarily about a bug that occurs when trying to add a product to the cart, but the definition of the add_product
method is not shown.
I can offer a couple pieces of advice that should help. First, the show
method of the Customer::CartsController
was hard to follow. I've refactored it to remove the unnecessary nested if statement, and reduce the number of conditionals.
class Customer::CartsController < ApplicationController
before_action :authenticate_user!
def show
return unless current_user
current_user.cart ||= Cart.find(session[:cart_id])
# if the cart has already been purchased, reset to a new cart
if current_user.cart.purchased_at
current_user.cart = Cart.create!(user_id: params[:id])
session[:cart_id] = current_user.cart.id
end
@cart = current_user.cart
end
end
Second, when dealing with a bug like this, you should find yourself with a much more specific question (if not the answer to your problem). In order to arrive at such a specific question, you need to debug to find out exactly what the problem is. You can do this in a couple of ways:
The simplest option is to put some "probe" statements in your code. For example, if you need to confirm whether or not the purchased_at
value is set in the Customer::CartsController show action when you click to add to the cart, you can put in a statement like puts "purchased_at is set"
within the body of the if statement. The output will show up in the Rails server output, which you can watch in another window while interacting with your site in the web browser.
The preferred option is to use a debugger. I recommend pry
, which you can find and read about here: https://github.com/nixme/pry-debugger.
What you should be trying to do with either of these techniques is to find out where what happens during runtime does not match your expectations of what your code should have done, or what would facilitate the behavior you expect as a user. Ultimately, this will often result in isolating a specific area of code where things don't make sense, making the question much easier.
Upvotes: 1
Reputation: 456
Previously working on cart i had the same issue as far as i remember it came from current_cart method sitting in your application controller
I ended up changing it to this
def current_cart
@cart = Cart.find(session[:cart_id])
rescue ActiveRecord::RecordNotFound
@cart = Cart.create
session[:cart_id] = @cart.id
@cart
end
also prior to @cart.add_.... I have the call for the method in my lineitems controller create method
@cart = current_cart
Edit 1
this is my lineitems create method I think you are missing a creation of cart if there is none
@lineitem = Lineitem.new(lineitem_params)
@cart = current_cart
item = Item.find(lineitem_params[:item_id])
@lineitem = @cart.addItem(item.id,@lineitem.quantity)
Upvotes: 0
Reputation: 8222
I think this code is wrong in Customer::CartsController
show action
@cart = if current_user
current_user.cart ||= Cart.find_by(session[:cart_id])
session[:cart_id] = nil if current_user.cart.purchased_at
end
That will always make @cart == nil
I think it should be something like
@cart = if current_user
current_user.cart ||= Cart.find_by(session[:cart_id])
session[:cart_id] = nil if current_user.cart.purchased_at
current_user.cart
end
That is, the last part of the if
statement needs to return the object that will be assigned to @cart
Upvotes: 0