Reputation: 8342
I have the following classes
class State < ActiveRecord::Base
has_many :cities
has_many :products, as: :geography
end
class City < ActiveRecord::Base
has_many :neighborhoods
has_many :products, as: :geography
end
class Neighborhood < ActiveRecord::Base
has_many :products, as: :geography
end
class Product < ActiveRecord::Base
belongs_to :geography, polymorphic: true
end
class User < ActiveRecord::Base
belongs_to :state
end
And obviously I have another class
User
. The user just has permissions for see the products
in his/her state
(except if the user is admin
, in which case he/she can see all the products
) How I can get all the products of a User
?
I tried to add some has_many, :through
to State
as follows
has_many :cities_activities, through: :cities, source: :products
has_many :neighborhoods_activities, through: :neighborhoods, source: :products
def owned_activities
self.products + self.cities_activities + self.neighborhoods_activities
end
but owned_activities
returns an Array
not a ActiveRecord::Relation
(I need some way of return a ActiveRecord::Relation
, so I can apply on it chained scopes
).
I am patching the code with if
-blocks and the code is getting messing and ugly, How can I do this in a clean, rails way?
@JTG suggested using merge
, but apparently it don't work in a nested way (or for more of three tables/models):
Started GET "/api/products.json?order=total_cost&page=1&per_page=18" for 127.0.0.1 at 2014-07-20 09:27:29 -0500
Processing by ProductsController#index as JSON
Parameters: {"order"=>"total_cost", "page"=>"1", "per_page"=>"18"}
Geokit is using the domain: localhost
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
Role Load (0.3ms) SELECT "roles".* FROM "roles" WHERE "roles"."id" = $1 ORDER BY "roles"."id" ASC LIMIT 1 [["id", 5]]
State Load (0.2ms) SELECT "states".* FROM "states" WHERE "states"."id" = $1 ORDER BY "states"."id" ASC LIMIT 1 [["id", 17]]
(9.2ms) SELECT COUNT(*) FROM "products" INNER JOIN "cities" ON "products"."geography_id" = "cities"."id" AND "products"."geography_type" = 'City' INNER JOIN "polygons" ON "products"."geography_id" = "neighborhoods"."id
" AND "products"."geography_type" = 'Neighborhood' INNER JOIN "cities" ON "neighborhoods"."cities_id" = "cities"."id" WHERE "products"."geography_id" = $1 AND "products"."geography_type" = $2 AND "cities"."states_id" = $1 [["geography_id", 17], ["geography_type", "State"], ["states_id", 17]]
PG::DuplicateAlias: ERROR: table name "cities" specified more than once
: SELECT COUNT(*) FROM "products" INNER JOIN "cities" ON "products"."geography_id" = "cities"."id" AND "products"."geography_type" = 'City' INNER JOIN "neighborhoods" ON "products"."geography_id" = "neighborhoods"."id" AND "products"."geography_type" = 'Neighborhood' INNER JOIN "cities" ON "neighborhoods"."cities_id" = "cities"."id" WHERE "products"."geography_id" = $1 AND "products"."geography_type" = $2 AND "cities"."states_id" = $1
ERROR: table name "cities" specified more than once
-- Clase:
Upvotes: 0
Views: 2118
Reputation: 3540
You can't use merge will combine the queries with AND
and what you need is a SQL OR
query. That is unfortunately not supported by any ActiveRecord method. You can use arel to create this query, but as it will be very complex and contain a lot of subqueries do I find it easier to just find the id's you need with normal queries and then use those.
To do that can you implement something like this in State
.
class State < ActiveRecord::Base
has_many :cities
has_many :neighborhoods, through: :cities
has_many :products, as: :geography
has_many :city_products, through: :cities, source: :products
has_many :neighborhood_products, through: :neighborhoods, source: :products
def all_products
state_product_ids = product_ids
city_product_ids = city_products.pluck(:id)
neighborhood_product_ids = neighborhood_products.pluck(:id)
all_product_ids = [state_product_ids, city_product_ids, neighborhood_product_ids].flatten.uniq
Product.where(id: all_product_ids)
end
end
You can now query a user for all products like this user.state.all_products
.
Upvotes: 1
Reputation: 20171
A polymorphic association is not suited optimally for these relationships...
You can use a nested has_many
.
class State < ActiveRecord::Base
has_many :cities
has_many :products, through: :cities
end
class City < ActiveRecord::Base
has_many :neighborhoods
has_many :products, through: :neighborhoods
end
class Neighborhood < ActiveRecord::Base
has_many :products
end
class Product < ActiveRecord::Base
belongs_to :neighborhood
end
class User < ActiveRecord::Base
belongs_to :state
end
Then you should be able to run user.state.products
Even better would be to add has_many :products, through: :state
to user.rb so you could run user.products
Upvotes: 1