Dave C
Dave C

Reputation: 387

Multiple Polymorphic Attributes on one Model

I've been creating an application which has several different model types which act as different page types. I currently have a Posts model with two polymorphic attributes: Postable and Posterable, where these are the pages on which posts are posted, and the authors of each post respectively.
For example, I want users to be able to author and receive a post, whilst I also want a Group to be able to receive a post and author one via an admin user.

My Post model is currently as follows:

class Post < ActiveRecord::Base
before_create :set_latlong

belongs_to :posterable, polymorphic: true
    belongs_to :postable, polymorphic: true

default_scope -> { order('created_at DESC') }
validates :content, presence: true, length: { maximum: 140 }
validates :posterable_id, presence: true
validates :posterable_type, presence: true

def set_latlong
    self.latitude = posterable.latitude
    self.longitude = posterable.longitude
end

def self.from_users_favourited_by(user)
    favourited_user_ids = "SELECT favourite_id FROM favouriteusers
                                WHERE favourited_id = :user_id"
    where("posterable_id IN (#{favourited_user_ids}) OR posterable_id = :user_id", user_id: user.id)
end

end

Post scheme is:

  create_table "posts", force: true do |t|
t.string   "content"
t.integer  "posterable_id"
t.string   "posterable_type"
t.float    "latitude"
t.float    "longitude"
t.integer  "postable_id"
t.string   "postable_type"
t.datetime "created_at"
t.datetime "updated_at"

end

I believe these are all workable too allow for various models to post on various model pages. However, I'm not quite sure how to implement this quite yet, the Post controller is as follows:

class PostssController < ApplicationController

before_action :authenticate_user!, only: [:create, :destroy] before_filter :load_posterable

def new @post = Post.new(post_params) end

def create @posterable = load_posterable @post = @posterable.posts.build(post_params) @post.postable = find_postable if @post.save flash[:success] = "Post created!" redirect_to root_url else @feed_items = [] render 'static_pages/home' end end

def destroy @post = Post.find(params[:id]) if @post.present? @post.destroy end redirect_to root_url end

private

def post_params
    params.require(:post).permit(:content)
end

def load_posterable
  resource, id = request.path.split('/')[1, 2]
  resource_name = resource.singularize.classify
  if resource_name = "user"
    @posterable = current_user
  else 
    @posterable = resource_name.constantize.find(id)
  end
end

def find_postable
  resource, id = request.path.split('/')[1, 2]
  resource_name = resource.singularize.classify
end

end

I believe the key to making this work is separating the "resource" in the load_posterable and find_postable methods, which I got from the Railscast tutorial on polymorphic association, but I don't really know how I would differentiate between these within the same model?

Upvotes: 1

Views: 451

Answers (1)

Mike Szyndel
Mike Szyndel

Reputation: 10593

Ok, so you need a Profile model, which user/admin will use to act on the site with.

You should create and select it automatically for users (default one), and then separate one for each group one becomes an admin of.

Profile in itself should not really hold any attributes, just delegate them over association (maybe a polymorphic one if you wish) to the user or other model.

This way you have quite a clean interface, Profile class will be kind-of decorator for your models.

On controller level you should have a current_profile method similar to current_user in Devise which will store/retrieve profile id from session.

See: http://en.wikipedia.org/wiki/Decorator_pattern

Upvotes: 0

Related Questions