MotionDataWrapper を使って、ブログのエントリとカテゴリみたいな、 多対多のリレーションシップに挑戦した。
すんなりいくだろうと思っていたけど、意外に嵌ってしまったので、 メモを残しておく。
エンティティとリレーションシップの定義
Xcode Data Modeler を使って Data Model ファイルを作成。
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 を指定するのがポイント。