Reputation: 23
I am having real trouble accessing the value of an additional parameter called permission on a has_many through. It is probably something simple. My 3 Models are
class User < ActiveRecord::Base
has_many :players_users
has_many :players, through: :players_users
end
class Player < ActiveRecord::Base
has_many :players_users
has_many :users, through: :players_users
end
class PlayersUser < ActiveRecord::Base
belongs_to :user
belongs_to :player
validates :player_id, uniqueness: { scope: :user_id }
end
My controller saves the record without issue. Adding the permission value to the correct joining table.
def create
@players = Player.new(players_params)
@user= current_user
if @players.save
@player = Player.last
@user.save && @user.players_users.create(:player_id =>@player.id, :permission =>"owner")
redirect_to '/players'
else
render 'new'
end
end
However I seem unable to access it properly I have tried
perm = User.find(current_user).players_users.includes(:Permission)
if perm == "owner"
Which gives an ActiveRecord::AssociationNotFoundError, association named 'Permission' was not found on PlayersUser; perhaps you misspelled it?
I have also tried
perm = User.players_users.where(player_id = @player.id && user_id = current_user)
perm.permission
or
perm = User.Player.where(player_id = @player.id && user_id = current_user)
or
perm = User.players.where(player_id = @player.id && user_id = current_user)
Which gives an undefined method error. undefined method `Player'
I know this is something small in my setup but cannot figure out what it is. Any help appreciated.
Upvotes: 2
Views: 400
Reputation: 454
players_users
and players
are associated with User object, so you can fetch the results as,
current_user.players_users.pluck(:permission)
Upvotes: 1
Reputation: 76774
I've solved this issue before with my own code.
I'll post that in a second, but first you need to refactor your controller, the current is inefficient:
#app/controllers/players_controller.rb
class PlayersController < ApplicationController
def create
@player = current_user.players.new players_params
if @player.save
current_user.player_users.find(@player.id).update(permission: "owner")
redirect_to players_path
end
end
private
def player_params
params.require(:player).permit(....)
end
end
To access the permission
of the player_user
, you'll need to use the following:
@permission = current_user.player_users.find(@player.id).permission
--
A much more DRY
approach (if you're explicitly setting permission
as owner
each time) would be to introduce an enum
into the Player
model to act as a default:
#app/models/player.rb
class Player < ActiveRecord::Base
enum permission: [:owner, :member, :moderator] #-> defaults to :owner
end
This will do away with having to define the permission
in the create
method (unless of course you want to change it):
#app/controllers/players_controller.rb
class PlayersController < ApplicationController
def create
@player = current_user.players.new players_params
redirect_to players_path if @player.save
end
end
To understand this, you must remember that since player_users
is a join association, ActiveRecord
will automatically populate it if you create a player on the current_user
object (current_user.players
).
In regard to pulling the permission
data, I built a script which appends the permission to the player
object (uses proxy_association.target
etc):
#current
@player = current_user.players.find params[:player_id]
@permission = current_user.player_users.find(params[:player_id]).permission
#script
@player = current_user.players.find params[:player_id]
@permission = @player.permission
It works similarly to an SQL Alias column
- so whilst you cannot manipulate the data, it will allow you to call @user.players.find(params[:player_id].permission
.. except it's all done in memory:
#app/models/user.rb
class User < ActiveRecord::Base
has_many :player_users
has_many :players, through: :player_users, -> { extending PlayerPermission }
end
#app/models/concerns/player_permission.rb
module PlayerPermission
#Load
def load
permissions.each do |permission|
proxy_association.target << permission
end
end
#Private
private
#Permissions
def permissions
return_array = []
through_collection.each_with_index do |through,i|
associate = through.send(reflection_name)
associate.assign_attributes({permission: items[i]})
return_array.concat Array.new(1).fill( associate )
end
return_array
end
#######################
# Variables #
#######################
#Association
def reflection_name
proxy_association.source_reflection.name
end
#Foreign Key
def through_source_key
proxy_association.reflection.source_reflection.foreign_key
end
#Primary Key
def through_primary_key
proxy_association.reflection.through_reflection.active_record_primary_key
end
#Through Name
def through_name
proxy_association.reflection.through_reflection.name
end
#Through
def through_collection
proxy_association.owner.send through_name
end
#Captions
def items
through_collection.map(&:permission)
end
#Target
def target_collection
#load_target
proxy_association.target
end
end
--
As an aside, convention is to keep all aspects of a model
name singular
(ProductUser
).
Upvotes: 1