Search code examples
ruby-on-railsrubyruby-on-rails-4multiple-records

Rails create multiple tokens for same record (multiple coupons for one offer)


so here is what I am trying to do: when the user is creating a special offer, he writes down how many coupons there should be. Each of these coupons is stored in the database with a unique code.

Example:

He wants to sell 100 coupons for "Dinner offer". When he creates the "Dinner offer" and types in, that he wants 100 coupons, these 100 coupons should be generated. How am I supposed to do this?

Thanks.


Solution

  • It's hard to tell which parts of this you're asking for help on. Are you not sure how to generate unique coupon codes, not sure how to model it, not sure how to create multiple records at a time? I'll try to give a general answer of what I'd do if I were building this, and maybe that will be helpful to you.

    I'd create two models, Offer and Coupon:

    rails g model offer title:string user:references
    rails g model coupon offer:references code:uuid
    

    I don't know if you're using mysql or postgresql. I think uuid will only work on postgresql. If you're using mysql I guess make the code column a string instead. Or better yet, switch to postgresql. ;)

    class Offer < ActiveRecord::Base
      belongs_to :user
      has_many :coupons
    end
    
    class Coupon < ActiveRecord::Base
      belongs_to :coupon
    
      before_create -> { self.code = SecureRandom.uuid }
    end
    

    I would index the database on your code column and make it unique, and disallow code from being nil. Might as well make both columns disallow nil, I'm a big believer in doing that wherever possible in the database:

    class CreateCoupons < ActiveRecord::Migration
      def change
        create_table :coupons do |t|
          t.references :offer, index: true, null: false
          t.uuid :code, null: false
        end
    
        add_index :coupons, :code, unique: true
      end
    end
    

    Normally when you create a coupon you'd just do:

    offer.coupons.create!
    

    And it would generate the code for you in the before_create hook. But since you want to create like 100 of them at once, the simple way would be:

    100.times { offer.coupons.create! }
    

    But that's going to be kind of inefficient. I don't know of a way to get Rails to do an efficient INSERT of many records, so we're going to do it manually:

    objs = []
    100.times do
      objs.push "(#{offer.id}, '#{SecureRandom.uuid}', '#{Time.now}', '#{Time.now}')"
    end
    
    Coupon.connection.execute("INSERT INTO coupons (offer_id, code, created_at, updated_at) VALUES #{objs.join(", ")}")
    

    If there's a more Railsy way of doing this, someone please let us know.