Rails4 Friendly_id Unique Slug Formatting

前端 未结 4 1958
抹茶落季
抹茶落季 2021-02-15 22:15

I am using friendly_id gem for slugging my models. Since the slug has to be unique when i enter the same data to check i get a long hashed appending in the slug.



        
4条回答
  •  慢半拍i
    慢半拍i (楼主)
    2021-02-15 23:09

    Today I came accross this issue and while other answer helped me get started, I wasn't satisfied because, like you, I wanted to have the slugs appear in sequence like explore, explore-2, explore-3.

    So, here's how I fixed it:

    class Thing < ActiveRecord::Base
      extend FriendlyId
      friendly_id :slug_candidates, use: :slugged
    
      validates :name, presence: true, uniqueness: { case_sensitive: false }
      validates :slug, uniqueness: true
    
      def slug_candidates
        [:name, [:name, :id_for_slug]]
      end
    
      def id_for_slug
        generated_slug = normalize_friendly_id(name)
        things = self.class.where('slug REGEXP :pattern', pattern: "#{generated_slug}(-[0-9]+)?$")
        things = things.where.not(id: id) unless new_record?
        things.count + 1
      end
    
      def should_generate_new_friendly_id?
        name_changed? || super
      end
    end
    

    I used uniqueness validation for :slug just in case this model is being used in concurrent code.

    Here you can see it working:

    irb(main):001:0> Thing.create(name: 'New thing')
       (0.1ms)  begin transaction
       (0.2ms)  SELECT COUNT(*) FROM "things" WHERE (slug REGEXP 'new-thing(-[0-9]+)?$')
      Thing Exists (0.1ms)  SELECT  1 AS one FROM "things" WHERE ("things"."id" IS NOT NULL) AND "things"."slug" = ? LIMIT 1  [["slug", "new-thing"]]
      Thing Exists (0.1ms)  SELECT  1 AS one FROM "things" WHERE LOWER("things"."name") = LOWER('New thing') LIMIT 1
      Thing Exists (0.1ms)  SELECT  1 AS one FROM "things" WHERE "things"."slug" = 'new-thing' LIMIT 1
      SQL (0.4ms)  INSERT INTO "things" ("name", "slug") VALUES (?, ?)  [["name", "New thing"], ["slug", "new-thing"]]
       (115.7ms)  commit transaction
    => #
    irb(main):002:0> Thing.create(name: 'New thing')
       (0.2ms)  begin transaction
       (0.9ms)  SELECT COUNT(*) FROM "things" WHERE (slug REGEXP 'new-thing(-[0-9]+)?$')
      Thing Exists (0.1ms)  SELECT  1 AS one FROM "things" WHERE ("things"."id" IS NOT NULL) AND "things"."slug" = ? LIMIT 1  [["slug", "new-thing"]]
      Thing Exists (0.1ms)  SELECT  1 AS one FROM "things" WHERE ("things"."id" IS NOT NULL) AND "things"."slug" = ? LIMIT 1  [["slug", "new-thing-2"]]
      Thing Exists (0.1ms)  SELECT  1 AS one FROM "things" WHERE LOWER("things"."name") = LOWER('New thing') LIMIT 1
      Thing Exists (0.1ms)  SELECT  1 AS one FROM "things" WHERE "things"."slug" = 'new-thing-2' LIMIT 1
       (0.1ms)  rollback transaction
    => #
    irb(main):003:0> Thing.create(name: 'New-thing')
       (0.2ms)  begin transaction
       (0.5ms)  SELECT COUNT(*) FROM "things" WHERE (slug REGEXP 'new-thing(-[0-9]+)?$')
      Thing Exists (0.1ms)  SELECT  1 AS one FROM "things" WHERE ("things"."id" IS NOT NULL) AND "things"."slug" = ? LIMIT 1  [["slug", "new-thing"]]
      Thing Exists (0.1ms)  SELECT  1 AS one FROM "things" WHERE ("things"."id" IS NOT NULL) AND "things"."slug" = ? LIMIT 1  [["slug", "new-thing-2"]]
      Thing Exists (0.3ms)  SELECT  1 AS one FROM "things" WHERE LOWER("things"."name") = LOWER('New-thing') LIMIT 1
      Thing Exists (0.3ms)  SELECT  1 AS one FROM "things" WHERE "things"."slug" = 'new-thing-2' LIMIT 1
      SQL (0.4ms)  INSERT INTO "things" ("name", "slug") VALUES (?, ?)  [["name", "New-thing"], ["slug", "new-thing-2"]]
       (108.9ms)  commit transaction
    => #
    irb(main):004:0> Thing.create(name: 'New!thing')
       (0.2ms)  begin transaction
       (0.6ms)  SELECT COUNT(*) FROM "things" WHERE (slug REGEXP 'new-thing(-[0-9]+)?$')
      Thing Exists (0.0ms)  SELECT  1 AS one FROM "things" WHERE ("things"."id" IS NOT NULL) AND "things"."slug" = ? LIMIT 1  [["slug", "new-thing"]]
      Thing Exists (0.1ms)  SELECT  1 AS one FROM "things" WHERE ("things"."id" IS NOT NULL) AND "things"."slug" = ? LIMIT 1  [["slug", "new-thing-3"]]
      Thing Exists (0.1ms)  SELECT  1 AS one FROM "things" WHERE LOWER("things"."name") = LOWER('New!thing') LIMIT 1
      Thing Exists (0.1ms)  SELECT  1 AS one FROM "things" WHERE "things"."slug" = 'new-thing-3' LIMIT 1
      SQL (0.1ms)  INSERT INTO "things" ("name", "slug") VALUES (?, ?)  [["name", "New!thing"], ["slug", "new-thing-3"]]
       (112.4ms)  commit transaction
    => #
    irb(main):005:0> 
    

    Also, if you use sqlite3 adapter you'll need to install sqlite3_ar_regexp gem (it won't be very fast, because SQLite doesn't have REGEXP() and it evaluates Ruby code instead).

提交回复
热议问题