вівторок, 13 грудня 2011 р.

Асоціації в DataMapper

Прочитавши заголовок цієї статті, читач мабуть зрозуміє про що піде мова нижче. У далекому 2009 я писав статтю про DataMapper, у якій згадувались асоціації між моделями. Сьогодні з власного досвіду спробую описати найбільш поширені асоціації та роботу з ними.
Для початку освіжимо пам'ять.
DataMapper - це ORM бібліотека (англ. Object-relational mapping, Обє'ктно-реляційна проекція - технологія, яка зв'язую бази даних з концепцією об'єктно-орієнтовного програмування, створюючи "віртуальну об'єктну базу даних").

Асоціації - це спосіб оголошення відносин між моделями. Вони надають ряд методів, які дозволяють створювати відносини та отримувати пов'язані моделі.

Зараз ORM використовуються повсюдно - ніхто не намагається працювати з базою вручну. Функціонал дозволяє зробити багато речей простіше. Робота з асоціаціями стала в рази легшою. Можна не використовувати SQL-запити, а працювати з даними, як зі звичайними об'єктами.

Я писав цю статтю виключно для себе, тому методика викладання навряд чи сподобається вам своєю легкістю і доступністю :))
Картинка для затравки.
Зацікавило? Тоді ласкаво прошу під кат.


Вхідні дані

  • Користувач(Account).
  • Подія(Event).
  • Коментар(Comment)
  • Лайк(Like)

1 асоціація
  • Подія має одного користувача(Event.owner => Account)
  • Користувач є власником безлічі подій(Account.events => Event)

2 асоціація
  • Коментар має одного користувача(Comment.owner => Account)
  • Користувач є власником безлічі коментарів(Account.comments => Comment)

3 асоціація
  • Подія має безліч коментарів(Event.comments => Comment)
  • Коментар належить одній події(Comment.event => Event)

4 асоціація
  • Користувач може "лайкати" подію(Account.likeds => Event)
  • Подія має безліч лайків(Event.likes => Account)

Моделі та асоціації

Визначаємо моделі для таблиць accounts, events, comments:
class Account
  include DataMapper::Resource

  property :id, Serial
  property :name, String
end

class Event
  include DataMapper::Resource

  property :id, Serial
  property :name, String
end

class Comment
  include DataMapper::Resource

  property :id, Serial
  property :body, Text
end

На малюнку нижче приведена схема бази даних, а цифрами позначені асоціації між її таблицями.


Далі по черзі описуємо наші асоціації:

1 асоціація

Подія має одного користувача, а користувач може бути власником безлічі подій.
Тут потрібно використовувати зв'язок "один-до-багатьох", який реалізовується через додаткову таблицю authorships.
class Account
  include DataMapper::Resource

  has n, :authorships
  has n, :events, :through => :authorships

end

class Event
  include DataMapper::Resource

  has n, :authorships
  has 1, :owner, :model => 'Account', :through => :authorships, :via => :account
end

class Authorship
  include DataMapper::Resource

  belongs_to :account, :key => true
  belongs_to :event, :key => true
end

Створимо користувача Anton і подію, автором якої буде цей користувач:
account = Account.create(:name => 'Anton')
account.save

event = Event.create(:name => 'Everyone invited!')
event.owner = account
event.save

Події, автором яких є Anton:
account.events.each do |event|
  puts "#{event.owner.name}: #{event.name}"
end

2 асоціація

Коментар має одного користувача, а користувач може бути власником безлічі коментарів.
Як і у попередній асоціації використовуємо зв'язок "один-до-багатьох", який реалізовується через додаткову таблицю commentators.
class Account
  include DataMapper::Resource

  has n, :commentators
  has n, :comments, :through => :commentators
end

class Comment
  include DataMapper::Resource

  has n, :commentators
  has 1, :owner, :model => 'Account', :through => :commentators, :via => :account
end

class Commentator
  include DataMapper::Resource

  belongs_to :account, :key => true
  belongs_to :comment, :key => true
end

3 асоціація

Подія має безліч коментарів.
Тут все просто. Використовуємо зв'язок "один-до-багатьох".
class Event
  include DataMapper::Resource

  has n, :comments
end

class Comment
  include DataMapper::Resource

  belongs_to :event
end

Створимо двох користувачів Zoriana та Olia, які прокоментують подію створену користувачем Anton:
guest1 = Account.create(:name => 'Zoriana')
guest1.save

guest2 = Account.create(:name => 'Olia')
guest2.save

comment1 = Comment.create(:body => 'Right here!')
comment1.owner = guest1
event.comments << comment1
comment1.save

comment2 = Comment.create(:body => 'Right now!')
comment2.owner = guest2
event.comments << comment2
comment2.save

Коментарі до події:
event.comments.each do |comment|
  puts "#{comment.owner.name}: #{comment.body}"
end


Події, які коментувала Zoriana:
guest1.comments.each{ |comment| puts "#{comment.event.name} - #{comment.body}" }

4 асоціація

Користувач може "лайкати" подію, а подія може мати безліч лайків від користувачів.
Тут будемо використовувати зв’язок "багато-до-багатьох", який реалізовується через додаткову таблицю likeables
class Account
  include DataMapper::Resource

  has n, :likeables
  has n, :likeds, :model => 'Event', :through => :likeables, :via => :event
end

class Event
  include DataMapper::Resource

  has n, :likeables
  has n, :likes, :model => 'Account', :through => :likeables, :via => :account
end

class Likeable
  include DataMapper::Resource

  belongs_to :account, :key => true
  belongs_to :event, :key => true
end

Користувачам Anton та Zoriana сподобалась подія:
event.likes << account
event.likes << guest1
event.save

Користувачі, яким сподобалась подія:
event.likes.each{|like| puts like.name}

Події, які сподобалися користувачу Zoriana:
guest1.likeds.each{ |like| puts "#{like.name}" }

Нижче наведений повний код моделей з асоціаціями між ними:


Замість постскриптуму. Офіційна документація про асоціації в DataMapper.

Немає коментарів: