I am asking almost to the same question asked here: Encrypted ID in URLs
Only I don't necessarily need the ID encrypted, I use a random string code. And I have tried a similar solution to what João Carlos Ottobboni answered then, with FriendlyId, as below:
In my models/coupon.rb
class Coupon < ApplicationRecord
extend FriendlyId
friendly_id :code #code is already a coupon column
validates :code, uniqueness: true
before_create :generate_code
def generate_code
all_letters = [('A'..'Z'), ('0'..'9')].map(&:to_a).flatten
random_string = (0...6).map { all_letters [rand(36)] }.join
self.code = random_string
end
And in my controllers/coupon_controller.rb
class CouponsController < ApplicationController
before_action :set_coupon, only: [:show, :edit, :update, :redeem, :destroy]
...
private
def set_coupon
@coupon = Coupon.friendly.find(params[:id])
end
When I'm redirected to a specific coupon path it actually works, redirects me to coupons/SJKE3K
instead of coupons/13
. But it doesn't prevent me from been able to type 13 in the url and it redirects me too. How can i avoid that? I need the coupons to be accessible only through the code and not through the id.
Coupon.friendly.find(params[:id])
allows both code and id to be used to find the record. If you switch to Coupon.find_by!(code: params[:id])
only the code will be able to find the coupon.
In that case you don't need Friendly ID at all, you can just continue manually generating the code and finding the record with it.
That will require a bit more work when generating forms that use the coupon or routes including the coupon as these will always use the integer ID by default - you'll have to explicitly pass id: coupon.code
as an argument to the url/path helpers and pass url: my_path(id: coupon.code)
or similar when creating forms.