MotionDataWrapper で多対多のリレーションシップを実現する

MotionDataWrapper を使って、ブログのエントリとカテゴリみたいな、 多対多のリレーションシップに挑戦した。

すんなりいくだろうと思っていたけど、意外に嵌ってしまったので、 メモを残しておく。

エンティティとリレーションシップの定義

Xcode Data Modeler を使って Data Model ファイルを作成。

f:id:griefworker:20131114195139p:plain

Data Model ファイルは resources に保存する。

クラスの作成

MotionDataWrapper::Model を継承したクラスを作成する。

class Entry < MotionDataWrapper::Model
end

class Category < MotionDataWrapper::Model
end

MotionDataWrapper の使い方は、前に書いたエントリを参照。

オブジェクトどうしを関係付ける

例えば、エントリにカテゴリを関係付けてみる。

category = Category.create(title: "foo")

# initWithEntity を使っている理由は、
# Entry.new だと NSManagedObjectContext が Entry に設定されないから。
# カテゴリを追加した状態で保存するとエラーが発生してしまう。
# カテゴリを追加しないなら new でもいい。
entry = Entry.alloc.initWithEntity(
  Entry.entity_description,
  insertIntoManagedObjectContext:App.delegate.managedObjectContext
)
entry.title = "hoge"

# カテゴリを関係付け
entry.addCategoriesObject(category)

# 保存
entry.save

最初、entry.categoris で取得できる NSMutableSet にカテゴリを追加していた。 変更内容がキャッシュされているのか、一見上手く動いていたけど、 シミュレータを再起動したら関係付けが保存されていなかった…。

本来、CoreData が動的に生成するメソッドを使わないといけなかった。 Entry クラスなら次の通り。

Entry#addCategoriesObject
Entry#removeCategoriesObject
Entry#addCategories
Entry#removeCategories

この CoreData が動的に生成するメソッドの存在に気付かず嵌った。 2日くらい悩んだ。

Objective-C + Xcode の場合、NSManagedObject 派生クラスを新規作成すると、上記のメソッドが定義されているから存在に気付ける。

関係付けられたオブジェクトを取得

カテゴリに属するエントリを取得。

category.entries # NSMutableSet が返ってくる
category.entries.allObjects # Array で取り出す

エントリの数が多い場合は、カテゴリ + αで絞り込む。

entries = Entry.where("ANY categories = ?", category). # カテゴリで絞り込み
                where("created_at >= ?", NSDate.today) # today は sugarcube が提供するメソッド

多対多の関係にあるオブジェクトで絞り込むとき、ANY を指定するのがポイント。