リソースとテーブルのギャップを飛び越える
テーブル・ビューで実現する
複数のテーブルを参照するSQLの機能として、誰もが思いつくのは、テーブル・ビューです。幸い、ActiveRecordでは、対応するデータがテーブル・ビューであっても、通常通りに動作します。ここでは、販売に対するテーブル・ビューを作成して、それに対応するActiveRecordクラスを定義してみます。
テーブル・ビューは、コンソール上でも作成できますが、Migrationからでも作成できます。次のように、executeメソッドで指定することで作成できます*1。
- [*1] ただし、executeでは、schema.rb(現在のテーブル構成を記録するもの)には反映されません。テスト実行時には工夫が必要です。
あとは、作成したテーブル・ビュー(shop_sales)に対応するActiveRecordを、ほかのモデルと同じ手順で定義するだけです。ただし、保存処理は、独自に定義する必要があります。保存処理の実装例を、次に示します。
保存処理は、トランザクションを使用し、失敗した時にロール・バックするようにしました。
ここで、論理モデルの話からは少し離れますが、名前空間について補足しておきます。上記のコードは、次の図4に示すように、modelsディレクトリに分けて配置しています。
図4: 各ファイルの配置 |
shopで分けた理由は、モデル名の重複を避けることと、分離しやすくすることです。例えば、Shop以下をプラグイン化して再利用する場合、Shop以外のコンポーネントからは、次のように、モジュール付きで呼び出す必要があります。これにより、コード上から依存関係を見つけやすくなります。
同様の理由から、テーブル名にもshopという接頭語を付けて定義します。これは、上記のコードにあるように、table_name_prefixメソッドを使用することによって自動的に、テーブル名に付加されてマッピングされます。これらは、一般的によく使われるテクニックなので、覚えておくと良いでしょう。
さらに、今回は、実テーブルに対応するモデルをphysicalディレクトリに配置し、テーブル・ビューに対応するモデルをtable_viewディレクトリに配置しました。これは、使えるAPIが双方で一部異なることを、モジュール名の違いによって開発者に示す狙いがあります。
それでは、定義したモデルを、コンソールから操作してみましょう。
コントローラ側で呼び出すような処理が、正常に動作していることが分かります。scaffoldが生成するような、シンプルで美しいコントローラを実装できそうです。また、販売に関連するロジックも、ほかのモデルから分離させて、本クラスに集約できそうです。