与 Hanami 实现多对多(和其他)关系
Implementing many-to-many (and other) relationships with Hanami
我在文档中搜索了如何实现实体之间的关系(例如,一对多、多对多等),但没有找到任何示例。
所以我尝试了一个合理的猜测。这是我尝试实现一个可以用 Tag
s 标记的 Person
:
require 'moocho_query'
require 'hanami/model'
require 'hanami/model/adapters/file_system_adapter'
class Person
include Hanami::Entity
attributes :name, :age, :tags
end
class Tag
include Hanami::Entity
attributes :name
end
class PersonRepository
include Hanami::Repository
end
class TagRepository
include Hanami::Repository
end
Hanami::Model.configure do
adapter type: :file_system, uri: './file_db'
mapping do
collection :people do
entity Person
repository PersonRepository
attribute :id, Integer
attribute :name, String
attribute :age, Integer
attribute :tags, type: Array[Tag]
end
collection :tags do
entity Tag
repository TagRepository
attribute :id, Integer
attribute :name, String
end
end
end.load!
me = Person.new(name: 'Jonah', age: 99)
t1 = Tag.new(name: 'programmer')
t2 = Tag.new(name: 'nice')
me.tags = [t1, t2]
PersonRepository.create(me)
load!
调用失败,出现以下错误:
/Users/x/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/hanami-utils-0.7.0/lib/hanami/utils/class.rb:90:in `load_from_pattern!': uninitialized constant (Hanami::Model::Mapping::Coercers::{:type=>[Tag]}|
{:type=>[Tag]}) (NameError)
from /Users/jg/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/hanami-model-0.6.0/lib/hanami/model/mapping/attribute.rb:80:in `coercer'
from /Users/jg/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/hanami-model-0.6.0/lib/hanami/model/mapping/attribute.rb:53:in `load_coercer'
在 Hanami 中实现关系的正确方法是什么?
从 0.7.0 版开始,无法实现实体之间的关系。这就是文档中也没有操作方法的原因。
出于好奇,我使用 tweet 查询了这个,可以将其视为实体关系的官方词汇。
作为变通方法,在 Hanami 中,实体只是您持久保存到数据库的对象,这意味着实体的持久性详细信息可能与其架构不同。
我建议在 Person
对象上使用 tags
方法。在此方法中,您可以检索此人的标签。像这样:
def self.tags
TagRepository.query do
where(id: [tag-id-1, tag-id-2, ... , tag-id-n])
end.all
end
尽管您需要将与此人相关联的标签 ID 作为此人的属性或使用联接 table.
保存到数据库中
知道这个实现会有 n+1
查询问题。
从 Hanami 0.9.0 开始,初步支持关联。第一个是一对多。请检查它,看看它是否对您有意义。谢谢
我知道这是一个老问题,但我会留下这个答案以防有人在这里绊倒:
Hanami 在 1.1 版本中添加了多对多支持
http://hanamirb.org/guides/1.1/associations/has-many-through/
基本设置
% bundle exec hanami generate model user
create lib/bookshelf/entities/user.rb
create lib/bookshelf/repositories/user_repository.rb
create db/migrations/20171024083639_create_users.rb
create spec/bookshelf/entities/user_spec.rb
create spec/bookshelf/repositories/user_repository_spec.rb
% bundle exec hanami generate model story
create lib/bookshelf/entities/story.rb
create lib/bookshelf/repositories/story_repository.rb
create db/migrations/20171024085712_create_stories.rb
create spec/bookshelf/entities/story_spec.rb
create spec/bookshelf/repositories/story_repository_spec.rb
% bundle exec hanami generate model comment
create lib/bookshelf/entities/comment.rb
create lib/bookshelf/repositories/comment_repository.rb
create db/migrations/20171024085858_create_comments.rb
create spec/bookshelf/entities/comment_spec.rb
create spec/bookshelf/repositories/comment_repository_spec.rb
迁移
用户table:
# db/migrations/20171024083639_create_users.rb
Hanami::Model.migration do
change do
create_table :users do
primary_key :id
column :name, String, null: false
column :created_at, DateTime, null: false
column :updated_at, DateTime, null: false
end
end
end
故事 table:
# db/migrations/20171024085712_create_stories.rb
Hanami::Model.migration do
change do
create_table :stories do
primary_key :id
foreign_key :user_id, :users, null: false, on_delete: :cascade
column :text, String, null: false
column :created_at, DateTime, null: false
column :updated_at, DateTime, null: false
end
end
end
评论table:
# db/migrations/20171024085858_create_comments.rb
Hanami::Model.migration do
change do
create_table :comments do
primary_key :id
foreign_key :user_id, :users, null: false, on_delete: :cascade
foreign_key :story_id, :stories, null: false, on_delete: :cascade
column :text, String, null: false
column :created_at, DateTime, null: false
column :updated_at, DateTime, null: false
end
end
end
存储库
用户资料库:
# lib/bookshelf/repositories/user_repository.rb
class UserRepository < Hanami::Repository
associations do
has_many :stories
has_many :comments
end
end
故事库:
# lib/bookshelf/repositories/story_repository.rb
class StoryRepository < Hanami::Repository
associations do
belongs_to :user
has_many :comments
has_many :users, through: :comments
end
def find_with_comments(id)
aggregate(:user, comments: :user).where(id: id).map_to(Story).one
end
def find_with_commenters(id)
aggregate(:users).where(id: id).map_to(Story).one
end
end
评论库:
# lib/bookshelf/repositories/comment_repository.rb
class CommentRepository < Hanami::Repository
associations do
belongs_to :story
belongs_to :user
end
end
用法
user_repo = UserRepository.new
author = user_repo.create(name: "Luca")
# => #<User:0x00007ffe71bc3b18 @attributes={:id=>1, :name=>"Luca", :created_at=>2017-10-24 09:06:57 UTC, :updated_at=>2017-10-24 09:06:57 UTC}>
commenter = user_repo.create(name: "Maria G")
# => #<User:0x00007ffe71bb3010 @attributes={:id=>2, :name=>"Maria G", :created_at=>2017-10-24 09:07:16 UTC, :updated_at=>2017-10-24 09:07:16 UTC}>
story_repo = StoryRepository.new
story_repo.create(user_id: author.id, text: "Hello, folks")
# => #<Story:0x00007ffe71b4ace0 @attributes={:id=>1, :user_id=>1, :text=>"Hello folks", :created_at=>2017-10-24 09:09:59 UTC, :updated_at=>2017-10-24 09:09:59 UTC}>
story = story_repo.find_with_comments(story.id)
# => #<Story:0x00007fd45e327e60 @attributes={:id=>2, :user_id=>1, :text=>"Hello folks", :created_at=>2017-10-24 09:09:59 UTC, :updated_at=>2017-10-24 09:09:59 UTC, :user=>#<User:0x00007fd45e326bc8 @attributes={:id=>1, :name=>"Luca", :created_at=>2017-10-24 09:06:57 UTC, :updated_at=>2017-10-24 09:06:57 UTC}>, :comments=>[#<Comment:0x00007fd45e325930 @attributes={:id=>1, :user_id=>2, :story_id=>2, :text=>"Hi and welcome!", :created_at=>2017-10-24 09:12:30 UTC, :updated_at=>2017-10-24 09:12:30 UTC, :user=>#<User:0x00007fd45e324490 @attributes={:id=>2, :name=>"Maria G", :created_at=>2017-10-24 09:07:16 UTC, :updated_at=>2017-10-24 09:07:16 UTC}>}>]}>
story.comments
# => [#<Comment:0x00007fe289f2d618 @attributes={:id=>1, :user_id=>2, :story_id=>2, :text=>"Hi and welcome!", :created_at=>2017-10-24 09:12:30 UTC, :updated_at=>2017-10-24 09:12:30 UTC, :commenter=>#<User:0x00007fe289f2c420 @attributes={:id=>2, :name=>"Maria G", :created_at=>2017-10-24 09:07:16 UTC, :updated_at=>2017-10-24 09:07:16 UTC}>}>]
我在文档中搜索了如何实现实体之间的关系(例如,一对多、多对多等),但没有找到任何示例。
所以我尝试了一个合理的猜测。这是我尝试实现一个可以用 Tag
s 标记的 Person
:
require 'moocho_query'
require 'hanami/model'
require 'hanami/model/adapters/file_system_adapter'
class Person
include Hanami::Entity
attributes :name, :age, :tags
end
class Tag
include Hanami::Entity
attributes :name
end
class PersonRepository
include Hanami::Repository
end
class TagRepository
include Hanami::Repository
end
Hanami::Model.configure do
adapter type: :file_system, uri: './file_db'
mapping do
collection :people do
entity Person
repository PersonRepository
attribute :id, Integer
attribute :name, String
attribute :age, Integer
attribute :tags, type: Array[Tag]
end
collection :tags do
entity Tag
repository TagRepository
attribute :id, Integer
attribute :name, String
end
end
end.load!
me = Person.new(name: 'Jonah', age: 99)
t1 = Tag.new(name: 'programmer')
t2 = Tag.new(name: 'nice')
me.tags = [t1, t2]
PersonRepository.create(me)
load!
调用失败,出现以下错误:
/Users/x/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/hanami-utils-0.7.0/lib/hanami/utils/class.rb:90:in `load_from_pattern!': uninitialized constant (Hanami::Model::Mapping::Coercers::{:type=>[Tag]}|
{:type=>[Tag]}) (NameError)
from /Users/jg/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/hanami-model-0.6.0/lib/hanami/model/mapping/attribute.rb:80:in `coercer'
from /Users/jg/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/hanami-model-0.6.0/lib/hanami/model/mapping/attribute.rb:53:in `load_coercer'
在 Hanami 中实现关系的正确方法是什么?
从 0.7.0 版开始,无法实现实体之间的关系。这就是文档中也没有操作方法的原因。
出于好奇,我使用 tweet 查询了这个,可以将其视为实体关系的官方词汇。
作为变通方法,在 Hanami 中,实体只是您持久保存到数据库的对象,这意味着实体的持久性详细信息可能与其架构不同。
我建议在 Person
对象上使用 tags
方法。在此方法中,您可以检索此人的标签。像这样:
def self.tags
TagRepository.query do
where(id: [tag-id-1, tag-id-2, ... , tag-id-n])
end.all
end
尽管您需要将与此人相关联的标签 ID 作为此人的属性或使用联接 table.
保存到数据库中知道这个实现会有 n+1
查询问题。
从 Hanami 0.9.0 开始,初步支持关联。第一个是一对多。请检查它,看看它是否对您有意义。谢谢
我知道这是一个老问题,但我会留下这个答案以防有人在这里绊倒:
Hanami 在 1.1 版本中添加了多对多支持
http://hanamirb.org/guides/1.1/associations/has-many-through/
基本设置
% bundle exec hanami generate model user
create lib/bookshelf/entities/user.rb
create lib/bookshelf/repositories/user_repository.rb
create db/migrations/20171024083639_create_users.rb
create spec/bookshelf/entities/user_spec.rb
create spec/bookshelf/repositories/user_repository_spec.rb
% bundle exec hanami generate model story
create lib/bookshelf/entities/story.rb
create lib/bookshelf/repositories/story_repository.rb
create db/migrations/20171024085712_create_stories.rb
create spec/bookshelf/entities/story_spec.rb
create spec/bookshelf/repositories/story_repository_spec.rb
% bundle exec hanami generate model comment
create lib/bookshelf/entities/comment.rb
create lib/bookshelf/repositories/comment_repository.rb
create db/migrations/20171024085858_create_comments.rb
create spec/bookshelf/entities/comment_spec.rb
create spec/bookshelf/repositories/comment_repository_spec.rb
迁移
用户table:
# db/migrations/20171024083639_create_users.rb
Hanami::Model.migration do
change do
create_table :users do
primary_key :id
column :name, String, null: false
column :created_at, DateTime, null: false
column :updated_at, DateTime, null: false
end
end
end
故事 table:
# db/migrations/20171024085712_create_stories.rb
Hanami::Model.migration do
change do
create_table :stories do
primary_key :id
foreign_key :user_id, :users, null: false, on_delete: :cascade
column :text, String, null: false
column :created_at, DateTime, null: false
column :updated_at, DateTime, null: false
end
end
end
评论table:
# db/migrations/20171024085858_create_comments.rb
Hanami::Model.migration do
change do
create_table :comments do
primary_key :id
foreign_key :user_id, :users, null: false, on_delete: :cascade
foreign_key :story_id, :stories, null: false, on_delete: :cascade
column :text, String, null: false
column :created_at, DateTime, null: false
column :updated_at, DateTime, null: false
end
end
end
存储库
用户资料库:
# lib/bookshelf/repositories/user_repository.rb
class UserRepository < Hanami::Repository
associations do
has_many :stories
has_many :comments
end
end
故事库:
# lib/bookshelf/repositories/story_repository.rb
class StoryRepository < Hanami::Repository
associations do
belongs_to :user
has_many :comments
has_many :users, through: :comments
end
def find_with_comments(id)
aggregate(:user, comments: :user).where(id: id).map_to(Story).one
end
def find_with_commenters(id)
aggregate(:users).where(id: id).map_to(Story).one
end
end
评论库:
# lib/bookshelf/repositories/comment_repository.rb
class CommentRepository < Hanami::Repository
associations do
belongs_to :story
belongs_to :user
end
end
用法
user_repo = UserRepository.new
author = user_repo.create(name: "Luca")
# => #<User:0x00007ffe71bc3b18 @attributes={:id=>1, :name=>"Luca", :created_at=>2017-10-24 09:06:57 UTC, :updated_at=>2017-10-24 09:06:57 UTC}>
commenter = user_repo.create(name: "Maria G")
# => #<User:0x00007ffe71bb3010 @attributes={:id=>2, :name=>"Maria G", :created_at=>2017-10-24 09:07:16 UTC, :updated_at=>2017-10-24 09:07:16 UTC}>
story_repo = StoryRepository.new
story_repo.create(user_id: author.id, text: "Hello, folks")
# => #<Story:0x00007ffe71b4ace0 @attributes={:id=>1, :user_id=>1, :text=>"Hello folks", :created_at=>2017-10-24 09:09:59 UTC, :updated_at=>2017-10-24 09:09:59 UTC}>
story = story_repo.find_with_comments(story.id)
# => #<Story:0x00007fd45e327e60 @attributes={:id=>2, :user_id=>1, :text=>"Hello folks", :created_at=>2017-10-24 09:09:59 UTC, :updated_at=>2017-10-24 09:09:59 UTC, :user=>#<User:0x00007fd45e326bc8 @attributes={:id=>1, :name=>"Luca", :created_at=>2017-10-24 09:06:57 UTC, :updated_at=>2017-10-24 09:06:57 UTC}>, :comments=>[#<Comment:0x00007fd45e325930 @attributes={:id=>1, :user_id=>2, :story_id=>2, :text=>"Hi and welcome!", :created_at=>2017-10-24 09:12:30 UTC, :updated_at=>2017-10-24 09:12:30 UTC, :user=>#<User:0x00007fd45e324490 @attributes={:id=>2, :name=>"Maria G", :created_at=>2017-10-24 09:07:16 UTC, :updated_at=>2017-10-24 09:07:16 UTC}>}>]}>
story.comments
# => [#<Comment:0x00007fe289f2d618 @attributes={:id=>1, :user_id=>2, :story_id=>2, :text=>"Hi and welcome!", :created_at=>2017-10-24 09:12:30 UTC, :updated_at=>2017-10-24 09:12:30 UTC, :commenter=>#<User:0x00007fe289f2c420 @attributes={:id=>2, :name=>"Maria G", :created_at=>2017-10-24 09:07:16 UTC, :updated_at=>2017-10-24 09:07:16 UTC}>}>]