Rails_learner
Rails_learner

Reputation: 83

Rails Issue with User Cart

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

Answers (3)

Jason
Jason

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:

  1. 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.

  2. 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

Artem Ankudovich
Artem Ankudovich

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

ReggieB
ReggieB

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

Related Questions