dom lizarraga

    dominiclizarraga@hotmail.com

    Follow as Polymorphic

    5 minutes to read

    In this blog post I’ll share my findings when developing a Follow model (User can follow another User, Post, Comapny) with a polymorphic property.

    With the next models you can create a ‘Following’ feature that can be applied to almost any model you want, just add one line of code.

    class User < ApplicationRecord
      # Users that follow this user 'Followers'
      has_many :followers, class_name: 'Follow', as: :followable
    
      # Entities this user follows 'Following'
      has_many :following, class_name: 'Follow', foreign_key: 'user_id'
    end
    
    class Follow < ApplicationRecord
      belongs_to :user # who is making/pushing the button "Follow" (doing the following)
      belongs_to :followable, polymorphic: true
    
      # This validation doesn't allow that user_1 follow user_2 twice
      validates :user_id, uniqueness: { scope: [:followable_type, :followable_id] }
    end
    

    Let’s try out the code in rails console

    rails console
    
    u1 = User.first
    u2 = User.last
    
    follow = u1.following.create(followable: u2)
    
    Follow Create (3.8ms)  INSERT INTO "follows" ("user_id", "followable_type", "followable_id", "created_at", "updated_at") 
    VALUES ($1, $2, $3, $4, $5) RETURNING "id"  [["user_id", 1], ["followable_type", "User"], ["followable_id", 18], 
    ["created_at", "2024-02-29 05:25:28.228037"], ["updated_at", "2024-02-29 05:25:28.228037"]]
      TRANSACTION (0.6ms)  COMMIT
    => #<Follow:0x0000000102d226e0
     id: 6,
     user_id: 1,
     followable_type: "User",
     followable_id: 18,
     created_at: Thu, 29 Feb 2024 05:25:28.228037000 UTC +00:00,
     updated_at: Thu, 29 Feb 2024 05:25:28.228037000 UTC +00:00>
    
    Follow.count
    => 1
    
    u1.following.create(followable: u2)
    => TRANSACTION (0.8ms)  ROLLBACK (due to model validation)
    
    # class User
    # def follows?(user)
    #   following.exists?(followable: user)
    # end
    
    u1.follows? u2
    => true
    
    u2.followers
    => [#<Follow:0x000000012eb126d0
      id: 6,
      user_id: 1,
      followable_type: "User",
      followable_id: 18,
      created_at: Thu, 29 Feb 2024 05:12:17.215129000 UTC +00:00,
      updated_at: Thu, 29 Feb 2024 05:12:17.215129000 UTC +00:00>]
    
    u1.following
    [#<Follow:0x0000000102b63fc0
      id: 6,
      user_id: 1,
      followable_type: "User",
      followable_id: 18,
      created_at: Thu, 29 Feb 2024 05:12:17.215129000 UTC +00:00,
      updated_at: Thu, 29 Feb 2024 05:12:17.215129000 UTC +00:00>,
     #<Follow:0x0000000102d226e0
     ...]
    

    And it worked for other models like (Post, Company, etc)

    class Post < ApplicationRecord
      has_many :followers, class_name: 'Follow', as: :followable
    end
    user = User.find(user_id)
    post = Post.find(post_id)
    
    user.following.create(followable: post)
      TRANSACTION (0.6ms)  COMMIT
    => 
    #<Follow:0x0000000150038080
     id: 8,
     user_id: 1,
     followable_type: "Post",
     followable_id: 92,
     created_at: Thu, 29 Feb 2024 05:56:29.921783000 UTC +00:00,
     updated_at: Thu, 29 Feb 2024 05:56:29.921783000 UTC +00:00>
    
    

    We’ve explored creating a follower system in Rails, showcasing the flexibility of polymorphic associations and the importance of validations. This guide provides a solid foundation for adding social functionalities to your Rails applications, ensuring a robust and scalable feature set.