だいたい703日前に更新最終更新日時: 2008-08-25 (月) 21:22:36 703日前
現在の位置
Memorycraft Wiki > SproutCoreのモダンなモデル層3
SproutCoreのモダンなモデル層3
オブジェクト間のリレーションは、このモデル層の要です。オブジェクトとそのリレーションはSproutCoreのモデル層がサポートする、オブジェクトグラフ の主要な要素として構成されています。
オブジェクトグラフ
レコードを作成し、それをStoreに追加できることはとてもよいのですが、それらレコードを互いに関連づけしたいときもあります。Storeに保存された一連のオブジェクトと、それらの間のリレーションを、まとめて”オブジェクトグラフ”と呼びます
- NOTE
- ここでは”グラフ”は配列やディクショナリと同じように数理的なオブジェクトを指します、パワポに貼り付けるようなものではありません。数理的なグラフは2つの要素:点と線、から成り立っています。SproutCoreのオブジェクトグラフでは、モデルオブジェクトが点、そしてそれらの間のリレーションが線になります。詳しくはWikipediaを参照してください
オブジェクトグラフの背景にある概念は、「グラフ内にある1つのオブジェクトの参照さえ取得できれば、グラフ内のほかのオブジェクトへの参照も取得できる。」という考え方です。ここでいう”他のオブジェクトへの”参照の方法としてSproutCoreのkey -valueコーディングが使用されます。
パート1で、key-valueコーディングを使って、todoオブジェクトのisComputedプロパティにどうやってアクセスしたかを思い出しましょう
var yn = todo.get('isCompleted') ;
この場合、戻り値はJavaScript?のBooleanオブジェクトです。代わりにSC.Recordオブジェクトを戻して、オブジェクトグラフをつかってリレーションをたどってみましょう。
まず、アプリケーションの設定をしましょう
$ sproutcore my_app $ cd my_app $ sc-gen model my_app/todo_list $ sc-gen model my_app/todo
OKです。基本の構成が出来ました。SproutCoreの開発サーバーを起動しましょう。
$ sc-server
FireFox?で http://localhost:4020/my_app をロードし、FireBug?を起動して、コンソールに以下を入力します。
>>> SC.Store.addRecord( MyApp.TodoList.create({ 'name': "My List" }) );
>>> SC.Store.addRecord( MyApp.Todo.create({ 'name': "Something to do" }) );
>>> SC.Store.records();
[MyApp.TodoList({ guid="@468" }) name=My List _bindings=[0] _observers=[0], MyApp.Todo({ guid="@571" }) name=Something to do. _bindings=[0] _observers=[0]]
パート2と同様に、レコードが作成されStoreに追加されました。しかしレコードを追加するたびにアプリケーションをリロードするのは面倒なので、それを修正しましょう。
フィクスチャーの使用
clients/my_app/main.js に上記のコードを加えることもできますが、SproutCoreにはアプリケーションのロード時に自動でレコードを生成する、フィクスチャーという優れものがあります。では、その利点を味わってみましょう。clients/my_app/fixures/todo_list.js を開いて、以下のようにします。
// ==========================================================================
// MyApp.TodoList Fixtures
// ==========================================================================
require('core') ;
MyApp.FIXTURES = MyApp.FIXTURES.concat([
{ guid: 1,
type: 'TodoList',
name: "My List"
}
]);
同じく clients/my_app/fixures/todo.js を以下のようにします。
// ==========================================================================
// MyApp.Todo Fixtures
// ==========================================================================
require('core') ;
MyApp.FIXTURES = MyApp.FIXTURES.concat([
{ guid: 1,
type: 'Todo',
name: "Something to do"
}
]);
開発モードでは、アプリケーションがロードされると、MyApp?.FIXTURES がこれらのレコードテンプレートを格納します。
開発中は MyApp?.FIXTURESはただの空の配列になります。
clients/my_app/main.js の以下を見てください。
MyApp.server.preload(MyApp.FIXTURES) ;
この行はフィクスチャーが実際のレコードに変換される部分です。このpreload()メソッドでは最終的にレコードをSC.Storeに保存します。よって、前と同じようにStoreからレコードを検索することもできます。
Store内のレコードを検索する
フィクスチャーを使うひとつの大きな利点は、レコードに固定のguidプロパティを与えられることです。それはつまりStoreを簡単に検索できるということでもあります。これはユニットテストをするときやFireBug?コンソールをいじるときに絶大な効果を発揮します。
このフィクスチャーをテストするために、ページを更新して、FireBug?に以下のとおり入力します。
>>> SC.Store.records();
[MyApp.Todo({ guid=1 }) guid=1 _bindings=[0] _observers=[0] _properties=[1], MyApp.TodoList({ guid=1 }) guid=1 _bindings=[0] _observers=[0] _properties=[1]]
すばらしい、正しくロードされました。固定のguidプロパティがあるので1つずつでも探すことができます。
>>> SC.Store.findRecords({guid:1}, MyApp.Todo);
[MyApp.Todo({ guid=1 }) guid=1 _bindings=[0] _observers=[0] _properties=[1]]
>>> SC.Store.findRecords({guid:1}, MyApp.TodoList);
[MyApp.TodoList({ guid=1 }) guid=1 _bindings=[0] _observers=[0] _properties=[1]]
さらに簡単な方法は、SC.Recordから継承しているfind()メソッドを使うことです。
>>> MyApp.Todo.find(1);
MyApp.Todo({ guid=1 }) guid=1 _bindings=[0] _observers=[0] _properties=[1]
>>> MyApp.TodoList.find(1);
MyApp.TodoList({ guid=1 }) guid=1 _bindings=[0] _observers=[0] _properties=[1]
次に進む前に、フィクスチャーにtype:MyApp?.Todoではなくtype:Todoとしたことに注意してください。SproutCoreはどうやってMyApp?.Todoが必要だとわかったのでしょう?答えはclients/my_app/core.js の以下の部分にあります。
server: SC.Server.create({ prefix: ['MyApp'] }),
この、MyApp?プリフィックスによってレコードタイプをサーバーに教えていたのです。
リレーションの設定
目標は以下のコードを書くことです。(これはまだ動きません。あくまで目標です。)
>>> /* todo と todoList がどこかで定義されていると仮定します */
>>> todo.get('todoList');
MyApp.TodoList({ guid=1 }) name=My List _bindings=[0] _observers=[0]
>>> todoList.get('todos');
SC.Collection:233 conditions=Object _bindings=[0] _observers=[0]
説明すると、TODOリストはたくさんのTODOを持っています。そしてTODOは一つのTODOリストを持っています。
1対1のリレーション
おそらく読者の多くは、TodoオブジェクトのプロパティとしてTodoリストのIDを保存する、Railsのアプローチには慣れ親しんでいると思います。これはSQLデータベースのデータにマッピングするストレートな方法なので、Railsアプリケーションには自然なことなのです。これと同じアプローチがSproutCoreでも動作します。
- NOTE:
- SproutCoreでは、Railsの id プロパティはguidと呼びます。意味と使い方はまったく同じです。
まずこれから、todo → todoListのリレーションの走査を処理します。目標は、todoのリストのオブジェクトを返すtodoListというtodoオブジェクトのプロパティを求めることです。
SproutCoreでは、Todoリストのオブジェクトのguidにつけるリレーションの名前を todoListとします。それからtodoListを実際の通りに、モデルオブジェクトの型の定義を変更します。(SproutCore はRailsのように名前からは判断できません.)
- NOTE:
- Railsなら, todos テーブルに todo_list_id を持ち、Todoクラスの定義に has_one :todo_list を用意します。
clients/my_app/fixtures/todo.js をこのように変更します。
// ==========================================================================
// MyApp.Todo Fixtures
// ==========================================================================
require('core') ;
MyApp.FIXTURES = MyApp.FIXTURES.concat([
{ guid: 1,
type: 'Todo',
name: "Something to do",
todoList: 1 // the guid of the todo list object this todo is related to
}
]);
todoListリレーションをサポートするようにtodoモデルを更新する必要があります。
clients/my_app/models/todo.js を以下のように変更します。
// ==========================================================================
// MyApp.Todo
// ==========================================================================
require('core');
/** @class
(Document your class here)
@extends SC.Record
@author AuthorName
@version 0.1
*/
MyApp.Todo = SC.Record.extend(
/** @scope MyApp.Todo.prototype */ {
todoListType: 'MyApp.TodoList'
}) ;
FireBug?のコンソールにもどり、以下を実行します。
>>> MyApp.Todo.find(1).get('todoList');
MyApp.TodoList({ guid=1 }) guid=1 _bindings=[0] _observers=[0] _properties=[1]
ビンゴ!速攻で1対1のリレーションができました。それでは逆もやってみましょう。
1対多のリレーション
- WARNING:
- 以下のコードは Gem 0.9.12以降の SproutCoreでしか動きません. 古いバージョンでの回避策はメーリングリストをご覧ください.
まず、todosのフィクスチャーを強化して、もう少しインスタンスを増やします。 clients/my_app/fixtures/todo.js をこのように変更します。
// ==========================================================================
// MyApp.Todo Fixtures
// ==========================================================================
require('core') ;
MyApp.FIXTURES = MyApp.FIXTURES.concat([
{ guid: 1,
type: 'Todo',
name: "Something to do",
todoList: 1 // the guid of the todo list object this todo is related to
},
{ guid: 2,
type: 'Todo',
name: "Something else to do",
todoList: 1
},
{ guid: 3,
type: 'Todo',
name: "Gee, I'm busy.",
todoList: 1
}
]);
todosのリレーションを追加するためにtodoリストモデルを変更します。 clients/my_app/models/todo_list.js を以下のようにします。
// ==========================================================================
// MyApp.TodoList
// ==========================================================================
require('core');
/** @class
(Document your class here)
@extends SC.Record
@author AuthorName
@version 0.1
*/
MyApp.TodoList = SC.Record.extend(
/** @scope MyApp.TodoList.prototype */ {
todos: SC.Record.hasMany('MyApp.Todo', 'todoList')
}) ;
- NOTE:
- Railsなら, has_many :todos を TodoList? クラス定義に追加します。.
変更したら、FireFox?にもどってページを更新し、FireBug?のコマンドラインで以下を実行します
>>> var c = MyApp.TodoList.find(1).get('todos'); // c will be an SC.Collection
>>> c.count();
2
うまくいきました!todoリストをとらえ、すべてのtodosをコレクションとして捕らえるようにtodosリレーションも張ることができました。SproutCoreの真のパワーをみるには、つづけてコンソールで以下を続けます。
>>> var rec = MyApp.Todo.create({ 'name': "Something to do" });
>>> rec.set('todoList', MyApp.TodoList.find(1));
>>> SC.Store.addRecord( rec );
>>> c.count();
3
みなさん、これがSproutCoreのすばらしさの理由です:Storeを変更しただけで、リレーションのコレクションは、特別なコードもいらずに自動的に最新(つまりcount()が3に増えました)をキープします。
考えてみてください:どこからでも(Ajax経由でリモートサーバーからとか)持ってきたデータをレコードにつっこむと、コレクションが自動的に更新されます。 そして、SproutCoreはバインディングを使用して、ビューとコントローラーそしてコントローラーとモデルが接続されているので、ビューまで更新されるということなのです。
SproutCoreでは、バインディングによってUIはStoreと自動的に同期しているのです。
これで、モダンなモデル層シリーズのパート3を終わりにします。
- 関連ページ
- SproutCore88日前
- Memorycraft Wiki701日前
- SproutCore ドキュメント翻訳701日前
- SproutCoreのモダンなモデル層703日前
- SproutCoreのモダンなモデル層2703日前