Datamapperでクラスのないテーブルは可能ですか?


8

次の属性を持つItemクラスがあります。

itemId,name,weight,volume,price,required_skills,required_items

最後の2つの属性は複数値になるため、それらを削除して次のような新しいスキームを作成します。

itemID,required_skillitemIDは外部キー、itemID and required_skillは主キーです。)

今、私はこの新しいテーブルを作成/使用する方法に困惑しています。私が思いついたオプションは次のとおりです。

1)ItemsとRequired_skillsの関係は1対多であるため、requiredSkillクラスを作成することができます。これは、one_to Itemにn個のRequiredSkillが含まれています。その後、私は行うことができますItem.get(1).requiredskills。これは私にとって最も論理的に聞こえ、次のようなメソッドとクエリを提供します。

sugar.components.create :raw_material => water.name
sugar.components.create :raw_material => sugarcane.name
sugar.components
sugar.components.count

2)required_skillsは(ルールに似ているため)定数と考えることができるため、ハッシュデータベース、gdbmデータベース、または別のsqlテーブルに入れて、そこからクエリを実行することもできます。

私の質問は:データマッパーのモデルレステーブルのようなものはありますか?データマッパーはテーブルの作成と整合性に責任があり、データマッパーの方法でそれをクエリすることができますが、SQLで行うようにクラスを必要としません?

最初の方法を使用して問題を解決しました。正規化プロセスごとに新しいクラスを作成しました(上記の1対多の関連付けのように見えます)。しかし、私はオブジェクト指向プログラミングに不慣れであり、そのような正規化ごとに新しいクラスを作成することが、データマッパーでそれを行う通常の方法であるのか、それともハックであるのかわかりません。これと、これを行うためのより良い方法があるかどうかは、私が知りたいことです。

あずきっく

再読datamapper.org団体に数回、今私はDataMapperのは確かに参加するために別々のクラスを必要としていることがわかります。だからあなたは私の質問に答えました。しかし、ロバート・ハーベイが賞金を置いたので、私はダイナミックな方法についての応答をもう少し待つ責任を感じています。

あなたのコードは不満を述べましたCannot find the child_model Container for Item in containers。私はそれを以下のような自己参照関連付けの2番目の例でうまく動作させることができました(他のものへの参照としてここに置く):

class Item
  class Link
    include DataMapper::Resource
    storage_names[:default] = "requirement_links"

    belongs_to :require, "Item", :key => true
    belongs_to :required, "Item", :key => true
  end

  include DataMapper::Resource

  property :id, Serial
  property :name, String, :required => true

  has n, :links_to_required_items, "Item::Link", :child_key => [:require_id]
  has n, :links_to_requiring_items, "Item::Link", :child_key => [:required_id]

  has n, :required_items, self,
    :through => :links_to_required_items,
    :via => :required
  has n, :requiring_items, self,
    :through => :links_to_requiring_items,
    :via => :require

 def require(others)
    required_items.concat(Array(others))
    save
    self
  end

  def unrequire(others)
    links_to_required_items.all(:required => Array(others)).destroy!
    reload
    self
  end
end

だから私はできる:

jelly = Item.get :name => "Jelly"
sugar = Item.get :name => "Sugar"
jelly.require sugar

アイテムを要求し、:

jelly.required_items.each { |i|; puts i.name }

本当に素晴らしい要件をリストアップします。

あなたの答えを読んだ後、私はまだデータベーススキーマをさらに正規化していないようです。正直なところ、原材料と製品の関係を自己参照として定義することのポイントはわかりません。つまり、それが小さなプログラムであれ{:jelly => ":sugar => 3, :water => 5"}ば、YAGNIの原則に従って、必要なアイテムと金額を反映するようなハッシュを使用することは確かです。最初のオプションのようにそれを行うと、自己参照関連付けによって提供されるものと同じくらい単純なクエリとメソッドがすでに提供されます。(ただし、オブジェクトへのメソッド呼び出しではなく、ストアドプロシージャのように見えることを認めなければなりません。)

それでは、私の単純なアプローチと比較して、理解/実装が難しい自己参照関連付けを使用する利点を説明していただけませんか?私はOOPに不慣れで、私はアンダーモデリングのようなものかと思います。

回答:


2

SQLの概念レベルで探しているのは、ジャンクションテーブル(マップ、リンク、リゾルバー、ピボットも、多対多の関係を処理するための一般的な名前です)だと思います。これらのジャンクションテーブルは通常、中間テーブルです。ただし、追加の属性を追加できます。

記述された疑似スキーマの意図は少しあいまいですが、私が意図したのは、アイテムには複数のスキルが必要になる可能性があるということです。アイテムは他のアイテムも必要とする場合があり、それぞれに必要なスキルがあり、場合によっては必要なアイテムを所有しているなど、さまざまなレベルの深さまであります。'多対多'の自己参照関係における循環参照( 'containerItemMaps'で発生する可能性があるものなど)に注意してください。次の疑似スキーマは、OPの意図をどのように想定するかを反映しています。

items (itemId PK, itemName, weight, volume, price)

skillMaps ( (itemId, skillId) PK)

skills (skillId PK, skillName)

containerItemMaps ( (containerItemId, componentItemId) PK)
    -- containerItemId is the parent/requiring item id
    -- componentItemId is the child/required item id

ActiveRecordの用語では、このような状況で使用するデータモデルの関係の関係のタイプとして、「has_and_belongs_to_many」推奨しています。詳細については、datamapper.org Associationsのページをご覧ください。具体的には、「持っており、多(または多対多)に属している」および「自己参照多対多の関係」というセクション

私はこの時点ではルビーの男ではないので、例を示すためにルビーコードをいじくり回すことができるだけですが、これはあなたのアイテムクラスがどのように見えるかについての私の最善の近似です:

# revised
class Item
   include DataMapper::Resource

   property :id, Serial

   property :name, String, :required => true
   property :weight, Float
   property :volume, Float
   property :price, Float 

   has n, :componentMaps, :child_key => [:container_id]
   has n, :components, self, :through => :componentMaps, :via => :component

   has n, :skillMaps, :child_key => [:skill_id]
   has n, :skills, :through => :skillMaps, :via => :skill    
end

そして、必要なアイテムなど、多対多のアイテムを自己参照するためのマップテーブル:

#revised
class ComponentMap
  include DataMapper::Resource

  belongs_to :container, 'Item', :key => true
  belongs_to :component, 'Item', :key => true

  property :quantity, Integer, :default => 1
end

完全を期すために:

class SkillMap
  include DataMapper::Resource

  belongs_to :item, 'Item', :key => true
  belongs_to :skill, 'Skill', :key => true

  property :mastery, Enum[:none, :aprentice, :journeyman, :craftsman, :master ], :default => :none

end

class Skill
    include DataMapper::Resource

    property :id, Serial
    property :name, String, :required => true

    has n, :skillMap, :child_key =>[:skill_id]     
end

改訂:

あなたの懸念に注意して、コンパイルされたコードと出力されたSQLがより理想的であることを確認するために、インタープレーターとデバッガーをインストールしました。もともと、私は文書の大まかな検査だけをしていました。上記の構造は、マッピングから一般に適切に構造化されたSQLを生成するはずです。

使用するフィールドと構造に関係なく、また選択するORM(データマッパーまたはその他のプロバイダー)に関係なく、デバッガーを介してコードを実行し、発生するSQLに注意を払う必要があります。あなたが最初に期待するかもしれないもの。

ジャンクションテーブル(skillMapとcomponentMap)に関する2番目の注意:追加のフィールド(数量と習得)が含まれていることに注意してください。これらは、最初に指定されていなくても、最初に説明された種類のアプリケーションに自然に適合しているようです。レシピでは、いくつかの成分は多くの異なる組み合わせの間で共通ですが、レシピごとの量は異なります。材料などのスキルについては、特定のアクティビティを実行するために必要なスキルレベルが異なるため、ジャンクションテーブルskillMapにマスタリーフィールドを追加しました。

もちろん、適切なビジネスルールとヘルパー関数(要素の追加と削除、要素のグループの追加と削除など、コレクションの構成にプログラムでアクセスするため)を追加することもできます。

うまくいけば、これは、ストレートハッシュよりもジャンクションテーブルを検討して使用する理由が少しよくなることを示しています。もちろん、特定のアプリケーションはそれぞれ異なり、おそらくアイテムとスキルおよびアイテムと他のアイテムとの関係の追加の側面を指定する機能は、あなたのケースでは必要ありません。

関係を明示的に定義する際に追加のコントロールを用意して利用することには、動的/マジックマッピングに依存するよりも多くの利点があります。場合によっては、それが本当に必要だと思うこともあり、多対多の場合には、これが示されていると思います。1対多の場合、関係を推測するのは簡単であり、マッピングを生成するより動的な方法(たとえば、has n、:<attribute group>、:through => Resource)を使用してもかまいません。


上記の編集を参照してください。短い言葉では表現できなくて申し訳ありません。
2012年

どうもありがとう。2日間で、在庫のいくつかのオプションを比較しました。has n, :items, :through => Inventoryアプローチを使用することで、ハッシュのようなアプローチよりも効率的なクエリが得られることがわかりました。ところで、どのデバッガを使いましたか?
barerd

SQL部分の場合: 'DataMapper :: Logger.new($ stdout、:debug)'そしてルビー側では、gem 'ruby-debug'; 両方とも日食内から
JustinC 2012年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.