Access parent in scope - ruby-on-rails

I have a rails 3.2 application with an easy hierarchy: A user has many clients, a client has many invoices.
I want the users to only see their own clients and invoices using scopes, by doing Client.by_user(current_user) and Invoice.by_user(current_user). For clients, I have this, which works fine:
scope :by_user, lambda { |user| where(user_id: user.id) }
However, if I try the same for invoices
scope :by_user, lambda { |user| where(client.user_id => user.id) }
it fails, telling me undefined local variable or method 'client'.
What am I doing wrong? I don't want to add user_ids to the invoices.

As #gregates said in comments, better for you to define associations for User, Client & Invoice models and then use user.clients, user.invoices, invoice.user etc.:
class User < ActiveRecord::Base
has_many :clients
has_many :invoices, through: :clients
end
class Client < ActiveRecord::Base
belongs_to :user
has_many :invoices
end
class Invoice < ActiveRecord::Base
belongs_to :client
has_one :user, through: :client
end
But if you prefer idea with scope, you should join clients table to invoices in your scope:
class Invoice < ActiveRecord::Base
...
scope :by_user, lambda { |user| joins(:client).where("clients.user_id = ?", user.id) }
...
end

Related

Rails aggregating polymorphic has_many relationship

In my app, an Account can be owned by a Household or a User. Users can access accounts that they own, and accounts that their household owns. I've defined accessible_accounts, that gives me an array of both household and user accounts, but is there a way to get the same result as a relation, so I can chain it with further conditionals? The order is unimportant (and indeed can be set with further chaining)
class User < ActiveRecord::Base
has_many :accounts, as: :owner
belongs_to :household
def accessible_accounts
ua = accounts.to_a
ha = []
if household
ha = household.accounts.to_a
end
(ua + ha).uniq
end
end
class Household < ActiveRecord::Base
has_many :users
has_many :accounts, as: :owner
end
class Account < ActiveRecord::Base
belongs_to :owner, polymorphic: true
end
I have a couple of thoughts on this:
First, you could express the Account that User has through Household by adding the following to the Accounts model:
has_many :accounts, through: household
However, I understand that you want a Relation that represents the merge/union of the accounts that a user owns directly with the accounts they own through their associated household. If that's true, then I think the following method added to User will give you what you want:
def accessible_accounts
Account.where(
'(ownable_id = ? and ownable_type = ?) or (ownable_id = ? and ownable_type = ?)',
id, User.to_s, household_id, Household.to_s)
end
I have not tested this, but I thought I'd go ahead and share it and count on feedback if I've misunderstood something.
In a proper polymorphic association I think your structure would be as following:
class User < ActiveRecord::Base
belongs_to :household
has_many :accounts, as: :ownable
end
class Household < ActiveRecord::Base
has_many :users
has_many :accounts, as: :ownable
end
class Account < ActiveRecord::Base
belongs_to :ownable, polymorphic: true
end
By the way, do you have any speacial reasons for not using Devise and CanCan for authentication & authorization?

Please help me simplify this simple Ruby on Rails function

Could someone tell me how to simplify the following function in my Invoice model? It should return all Invoices related to a particular Client (both connected via a Projects table).
def self.search_by_client_id(client_id)
if client_id
projects = Project.where(:client_id => client_id)
Invoice.where(:project_id => projects)
else
scoped
end
end
I really can't get my head around this. Thanks for any input!
This seems like the perfect opportunity for a scope!
scope :client, lambda{|id| includes(:projects).where('projects.client_id = ?', id)}
Invoice.client(4).all # returns all invoices for the client with the specified ID.
If I understand your question correctly, you should be able to do it using ActiveRecord associations:
class Client < ActiveRecord::Base
has_many :projects
has_many :invoices, :through => :project
end
class Project < ActiveRecord::Base
has_many :invoices
belongs_to :client
end
class Invoice < ActiveRecord::Base
belongs_to :project
has_one :client, :through => :project
end
Then, all Invoices related to a particular Client:
#client.invoices
To get a Client, associated to a particular invoice:
#invoice.client

What is the right way to query :has_many when condition is in :through

I have 3 model
class User < ActiveRecord::Base
has_many :profiles
has_many :companies, :through => :profiles
end
class Company < ActiveRecord::Base
has_many :profiles
has_many :users, :through => :profiles
end
class Profile < ActiveRecord::Base
belongs_to :company
belongs_to :user
end
when I want to query all companies form user. I just type
u = User.find(:first)
companies = u.companies
But when I want to query only companies that have
profile.is_publish == ture
What is the right way I should do
First: I should create method in User to do that
def published_companies
companies.where('profiles.is_publish' => true)
end
companies = u.published_companies
Second: I should create scope in Company
scope :published, joins(:profiles).where('profiles.is_publish' => true)
companies = u.companies.published
Third: I should create a scope in Profile
companies = u.profile.published.companies
the first way is easy one and the third is cool but I don't know How to do it.
Create the scope on Company like:
scope :published, where(:is_publish => true)
Then on user you call:
user.companies.published
To check the validness of the code you can use to_sql method.

Ruby on rails querying with join and condition statement

I need to query joining tables without using the primary key(id).
class User < ActiveRecord::Base
has_one :participant
end
class Participant < ActiveRecord::Base
belongs_to :user
end
The User has 'id' and 'name'.
The Participant has 'user_id' which is User.id
I am trying to find the Participant.id by querying with User.name
What I have tried is,
participant_id = Participant.all :joins => :users, :conditions => {:name => "Susie"}
If you're just looking for a specific user's participant id, you could just go:
User.find_by_name("Susie").participant.id
Since your user has a has_one relation to Participant(which belongs to it -- so basically a one-to-one) you can just go call participant on user. ActiveRecord takes care of the join magicks for you
Try the following:
class User < ActiveRecord::Base
has_one :participant
end
class Participant < ActiveRecord::Base
belongs_to :user
scope :foo, lambda {|name|
joins(:users).
where("name = ?", name)
}
end
If you're taking user input you're going to want to probably fix joins so it uses sanitize_sql_array. See here also for some query goodness

Many to many relationship , rails

I have a many-to-many relation between users and the channels they subscribe to. But when I look at my model dependency between user and user channels or channels and users channel instead there is a direct connection between users and channels. how do i put Users channels between to two?
User Model
class User < ActiveRecord::Base
acts_as_authentic
ROLES = %w[admin moderator subscriber]
has_and_belongs_to_many :channels
has_many :channel_mods
named_scope :with_role, lambda { |role| {:conditions => "roles_mask & #{2**ROLES.index(role.to_s)} > 0 "} }
def roles
ROLES.reject { |r| ((roles_mask || 0) & 2**ROLES.index(r)).zero? }
end
def roles=(roles)
self.roles_mask = (roles & ROLES).map { |r| 2**ROLES.index(r) }.sum
end
def role_symbols
role.map do |role|
role.name.underscore.to_sym
end
end
end
Channel Model
class Channel < ActiveRecord::Base
acts_as_taggable
acts_as_taggable_on :tags
has_many :messages
has_many :channel_mods
has_and_belongs_to_many :users
end
UsersChannel Model
class UsersChannels < ActiveRecord::Base
end
See the has_many :through documentation on the Rails guides which guides you through configuring a has_many relationship with an intervening model.
The HABTM relationship generates the UsersChannels auto-magically - if you want to access the model for the link table (add some more attributes to it for example - time_channel_watched or whatever), you'll have to change the models (and explicitly define and migrate a UsersChannel model with the attributes id:primary_key, user_id:integer, channel_id:integer) to :
class Channel < ActiveRecord::Base
has_many :users_channels, :dependent => :destroy
has_many :users, :through => :users_channels
end
class User < ActiveRecord::Base
has_many :users_channels, :dependent => :destroy
has_many :channels, :through => :users_channels
end
class UsersChannels < ActiveRecord::Base
belongs_to :user
belongs_to :channel
end
Note: since you're defining you're own link model you don't have to stay with the HABTM defined table name of UsersChannels - you could change the model name to something like "Watches". All of the above is pretty much in the Rails guide that has been mentioned.

Resources