Reputation: 11
I confused about matching between two models. I have researched on stackoverflow and google until 2 days but no way... There is more questions and answer about matching and filtering but it is about one way searching, listing. May be I couldn’t understand I’m not sure. For example: I have contents and users in my app. I shared contents and I don’t want to make visible, likeable for all users. Users should see who;
My first idea was create profile for users and take information from them.
Also create profile filter for Content
model and fill the form like belov.
But problem is at final step;
“How i compare them for only users can see and like who has true attributes same as Content
filter attributes.
Models;
User
and Content
Profile
belongs_to user
Contentfilter
belongs_to Content
Than; Show contens if current_user profile attributes same as Content filter.
Do you have any suggestion? Thanks.
Upvotes: 0
Views: 734
Reputation: 3378
I'm going to assume you mean any matching content but I'm providing a solution for both ALL or ANY matching profile filters.
Here's one quick solution on how you can achieve what you want.
Given the following models:
# app/models/user.rb
class User < ApplicationRecord
has_one :profile, as: :profilable
def all_matching_content
profile: Profile.where(
"? >= age", profile.age).
where(
gender: profile.gender,
city: profile.city,
license: profile.license).
where(profilable_type: "Content"))
end
def any_matching_content
Content.where(
profile: Profile.where("? >= age", profile.age).
or(Profile.where(gender: profile.gender)).
or(Profile.where(city: profile.city)).
or(Profile.where(license: profile.license)).
where(profilable_type: "Content"))
end
end
end
# app/models/profile.rb
class Profile < ApplicationRecord
belongs_to :profilable, polymorphic: true, optional: true
end
# app/models/content.rb
class Content < ApplicationRecord
has_one :profile, as: :profilable
end
And the following migrations:
# db/migrate/20181114004031_create_users.rb
class CreateUsers < ActiveRecord::Migration[5.2]
def change
create_table :users do |t|
t.string :name, index: true
t.timestamps
end
end
end
# db/migrate/20181114004134_create_profiles.rb
class CreateProfiles < ActiveRecord::Migration[5.2]
def change
create_table :profiles do |t|
t.references :profilable, polymorphic: true, null: true
t.boolean :license, index: true
t.string :city, index: true
t.string :gender, index: true
t.integer :age, index: true
t.timestamps
t.index [:license, :city, :gender, :age], name: "profile_composite_index"
end
end
end
# db/migrate/20181114004307_create_contents.rb
class CreateContents < ActiveRecord::Migration[5.2]
def change
create_table :contents do |t|
t.string :title, index: true
t.timestamps
end
end
end
And the following seeds:
# db/seeds.rb
User.create!(
name: "John",
profile: Profile.create!(
license: true,
city: "London",
gender: "Male",
age: 19
)
)
User.create!(
name: "Jane",
profile: Profile.create!(
license: false,
city: "London",
gender: "Female",
age: 17
)
)
User.create!(
name: "Rose",
profile: Profile.create!(
license: true,
city: "Edinburgh",
gender: "Female",
age: 21
)
)
User.create!(
name: "Hans",
profile: Profile.create!(
license: true,
city: "Berlin",
gender: "Male",
age: 24
)
)
Content.create!(
title: "London introduces new tax",
profile: Profile.create!(
city: "London"
)
)
Content.create!(
title: "New license requirements in Berlin",
profile: Profile.create!(
city: "Berlin",
license: true
)
)
Content.create!(
title: "Women should get new immunization",
profile: Profile.create!(
gender: "Female"
)
)
Content.create!(
title: "Adults only expo opens Wednesday",
profile: Profile.create!(
age: 18
)
)
You'll be able to do the following:
> john = User.find_by(name: "John")
> john.all_matching_content
=> #<ActiveRecord::Relation []>
> john.any_matching_content.map(&:title)
=> ["London introduces new tax", "New license requirements in Berlin", "Adults only expo opens Wednesday"]
> jane = User.find_by(name: "Jane")
> jane.any_matching_content.map(&:title)
=> ["London introduces new tax", "Women should get new immunization"]
> rose = User.find_by(name: "Rose")
> rose.any_matching_content.map(&:title)
=> ["New license requirements in Berlin", "Women should get new immunization", "Adults only expo opens Wednesday"]
> hans = User.find_by(name: "Hans")
> hans.any_matching_content.map(&:title)
=> ["New license requirements in Berlin", "Adults only expo opens Wednesday"]
The solution works by sharing a polymorphic Profile
model with User
and Content
. It then matches Content
with the User
instance by passing the user's Profile
attributes to a query for matching profiles that belong to Content
types.
Note that the #all_matching_content
method requires that all profile attributes match, basically it means all conditons in the query are "AND"ed. The #any_matching_content
method uses "OR" conditions instead.
I have uploaded a quick project to GitHub with this example already coded:
https://github.com/JurgenJocubeit/so-53289847
Upvotes: 1