Search code examples
ruby-on-railsruby-on-rails-4devisee-commerce

shopping cart, possible to use Devise's session functionality?


I am writing an ecommerce website and I need to implement a shopping cart feature. I want the customers to be able to add products to their cart without signing up beforehand, so I figured I would accomplish this through sessions.

Can this be done in the Devise gem or will I have to implement my own session model for this to work?


Solution

  • You'll need to handle your own session data - doesn't mean you'll need a session model though.

    We've implemented what you're looking for. It works using a session model, but uses the same base functionality I'd recommend for you:

    #app/models/cart_session.rb
    class CartSession
    
        #Initalize Cart Session
        def initialize(session)
            @session = session
            @session[:cart] ||= {}
        end
    
        #Cart Count
        def cart_count
            if (@session[:cart][:products] && @session[:cart][:products] != {})
                @session[:cart][:products].count
            else
                0
            end
        end
    
        #Cart Contents
        def cart_contents
            products = @session[:cart][:products]
    
            if (products && products != {})
    
                #Determine Quantities
                quantities = Hash[products.uniq.map {|i| [i, products.count(i)]}]
    
                #Get products from DB
                products_array = Product.find(products.uniq)
    
                #Create Qty Array
                products_new = {}
                products_array.each{
                    |a| products_new[a] = {"qty" => quantities[a.id.to_s]}
                }
    
                #Output appended
                return products_new
    
            end
    
        end
    
        #Qty & Price Count
        def subtotal
            products = cart_contents
    
            #Get subtotal of the cart items
            subtotal = 0
            unless products.blank?
                products.each do |a|
                    subtotal += (a[0]["price"].to_f * a[1]["qty"].to_f)
                end
            end
    
            return subtotal
    
        end
    
        #Build Hash For ActiveMerchant
        def build_order
    
            #Take cart objects & add them to items hash
            products = cart_contents
    
            @order = []
            products.each do |product|
                @order << {name: product[0].name, quantity: product[1]["qty"], amount: (product[0].price * 100).to_i }
            end
    
            return @order
        end
    
        #Build JSON Requests
        def build_json
            session = @session[:cart][:products]
            json = {:subtotal => self.subtotal.to_f.round(2), :qty => self.cart_count, :items => Hash[session.uniq.map {|i| [i, session.count(i)]}]}
            return json
        end
    
      
    end
    

    Sessions

    According to the Rails documentation:

    Most applications need to keep track of certain state of a particular user. This could be the contents of a shopping basket or the user id of the currently logged in user. Without the idea of sessions, the user would have to identify, and probably authenticate, on every request. Rails will create a new session automatically if a new user accesses the application. It will load an existing session if the user has already used the application.

    Sessions, to my knowledge, are small cookie files storing data for each user. These are ambiguous (are created for every user - regardless of whether they are signed in or not), which means I'd use them for your cart

    We use Sessions to create cart data by storing raw id's of the cart products in the session


    Devise

    A note on Devise - what you're asking about has nothing to do with the Devise gem. Devise is an authentication system, meaning it handles if a user has access to your app; it doesn't handle shopping cart data

    Whilst Devise stores data in sessions, you'll need to define your own session data for your cart. We do this using a cart controller with the model code above:

    #config/routes.rb
    get 'cart' => 'cart#index', :as => 'cart_index'
    post 'cart/add/:id' => 'cart#add', :as => 'cart_add'
    delete 'cart/remove(/:id(/:all))' => 'cart#delete', :as => 'cart_delete'
    
    #app/controllers/cart_controller.rb
    class CartController < ApplicationController
        include ApplicationHelper
    
        #Index
        def index
            @items = cart_session.cart_contents
            @shipping = Shipping.all
        end
    
        #Add
        def add
            session[:cart] ||={}
            products = session[:cart][:products]
    
            #If exists, add new, else create new variable
            if (products && products != {})
                session[:cart][:products] << params[:id]
            else
                session[:cart][:products] = Array(params[:id])
            end
    
            #Handle the request
            respond_to do |format|
                format.json { render json: cart_session.build_json }
                format.html { redirect_to cart_index_path }
            end
        end
    
        #Delete
        def delete
            session[:cart] ||={}
            products = session[:cart][:products]
            id = params[:id]
            all = params[:all]
    
            #Is ID present?
            unless id.blank?
                unless all.blank?
                    products.delete(params['id'])
                else
                    products.delete_at(products.index(id) || products.length)
                end
            else
                products.delete
            end
    
            #Handle the request
            respond_to do |format|
                format.json { render json: cart_session.build_json }
                format.html { redirect_to cart_index_path }
            end
        end
    
    end