テーブル・ビューで実現する
複数のテーブルを参照するSQLの機能として、誰もが思いつくのは、テーブル・ビューです。幸い、ActiveRecordでは、対応するデータがテーブル・ビューであっても、通常通りに動作します。ここでは、販売に対するテーブル・ビューを作成して、それに対応するActiveRecordクラスを定義してみます。
テーブル・ビューは、コンソール上でも作成できますが、Migrationからでも作成できます。次のように、executeメソッドで指定することで作成できます*1。
- [*1] ただし、executeでは、schema.rb(現在のテーブル構成を記録するもの)には反映されません。テスト実行時には工夫が必要です。
01 | <!--//--><
|
図4: 各ファイルの配置 |
shopで分けた理由は、モデル名の重複を避けることと、分離しやすくすることです。例えば、Shop以下をプラグイン化して再利用する場合、Shop以外のコンポーネントからは、次のように、モジュール付きで呼び出す必要があります。これにより、コード上から依存関係を見つけやすくなります。
1 | <!--//--><![ CDATA [// ><!-- |
4 | TableView::Sale.find(params[ :id ]) |
6 | Shop::TableView::Sale.find(params[ :id ]) |
同様の理由から、テーブル名にもshopという接頭語を付けて定義します。これは、上記のコードにあるように、table_name_prefixメソッドを使用することによって自動的に、テーブル名に付加されてマッピングされます。これらは、一般的によく使われるテクニックなので、覚えておくと良いでしょう。
さらに、今回は、実テーブルに対応するモデルをphysicalディレクトリに配置し、テーブル・ビューに対応するモデルをtable_viewディレクトリに配置しました。これは、使えるAPIが双方で一部異なることを、モジュール名の違いによって開発者に示す狙いがあります。
それでは、定義したモデルを、コンソールから操作してみましょう。
01 | <!--//--><![ CDATA [// ><!-- |
03 | > Shop::TableView::Sale.all |
05 | > Shop::TableView::Sale.find( 1 ) |
07 | > Shop::TableView::Sale.find( 1 ).item |
10 | > sale = Shop::TableView::Sale. new |
16 | > Shop::TableView::Sale. new ( :item_id => 1 , :unit => 1 , :address => 'Sapporo' ).save |
コントローラ側で呼び出すような処理が、正常に動作していることが分かります。scaffoldが生成するような、シンプルで美しいコントローラを実装できそうです。また、販売に関連するロジックも、ほかのモデルから分離させて、本クラスに集約できそうです。