Edit the migrations:

  1. # db/migrations/20171024083639_create_users.rb
  2. Hanami::Model.migration do
  3. change do
  4. create_table :users do
  5. primary_key :id
  6. column :name, String, null: false
  7. column :created_at, DateTime, null: false
  8. column :updated_at, DateTime, null: false
  9. end
  10. end
  11. end
  1. # db/migrations/20171024083725_create_avatars.rb
  2. Hanami::Model.migration do
  3. change do
  4. create_table :avatars do
  5. primary_key :id
  6. foreign_key :user_id, :users, null: false, on_delete: :cascade
  7. column :url, String, null: false
  8. column :created_at, DateTime, null: false
  9. end
  10. end

Now we can prepare the database:

  1. # lib/bookshelf/repositories/user_repository.rb
  2. class UserRepository < Hanami::Repository
  3. associations do
  4. has_one :avatar
  5. end
  6. def create_with_avatar(data)
  7. assoc(:avatar).create(data)
  8. end
  9. def find_with_avatar(id)
  10. aggregate(:avatar).where(id: id).map_to(User).one
  11. end
  12. end

We have defined only for the operations that we need for our model domain. In this way, we avoid to bloat UserRepository with dozen of unneeded methods.

Let’s create an user with an avatar single database operation:

  1. repository = UserRepository.new
  2. user = repository.create_with_avatar(name: "Luca", avatar: { url: "https://avatars.test/luca.png" })
  3. # => #<User:0x00007fa166ac8550 @attributes={:id=>1, :name=>"Luca", :created_at=>2017-10-24 08:44:27 UTC, :updated_at=>2017-10-24 08:44:27 UTC, :avatar=>#<Avatar:0x00007fa166ac35c8 @attributes={:id=>1, :user_id=>1, :url=>"https://avatars.test/luca.png", :created_at=>2017-10-24 08:44:27 UTC, :updated_at=>2017-10-24 08:44:27 UTC}>}>
  4. user.id
  5. # => 1
  6. user.name
  7. user.avatar

Because we haven’t explicitly loaded the associated record, user.avatar is nil. We can use the method that we have defined on before (#find_with_avatar):

  1. user = repository.find_with_avatar(user.id)
  2. # => #<User:0x00007fa166a71048 @attributes={:id=>1, :name=>"Luca", :created_at=>2017-10-24 08:44:27 UTC, :updated_at=>2017-10-24 08:44:27 UTC, :avatar=>#<Avatar:0x00007fa166a70328 @attributes={:id=>1, :user_id=>1, :url=>"https://avatars.test/luca.png", :created_at=>2017-10-24 08:44:27 UTC, :updated_at=>2017-10-24 08:44:27 UTC}>}>
  3. user.avatar
  4. # => #<Avatar:0x00007fa166a70328 @attributes={:id=>1, :user_id=>1, :url=>"https://avatars.test/luca.png", :created_at=>2017-10-24 08:44:27 UTC, :updated_at=>2017-10-24 08:44:27 UTC}>

This time user.avatar has the associated avatar.

  1. # lib/bookshelf/repositories/user_repository.rb
  2. class UserRepository < Hanami::Repository
  3. # ...
  4. def add_avatar(user, data)
  5. assoc(:avatar, user).add(data)
  6. end
  7. def remove_avatar(user)
  8. assoc(:avatar, user).delete
  9. end
  10. def update_avatar(user, data)
  11. assoc(:avatar, user).update(data)
  12. end
  13. def replace_avatar(user, data)
  14. assoc(:avatar, user).replace(data)
  15. end