tabelle senza classi possibili con Datamapper?

8

Ho una classe Item con i seguenti attributi:

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

Poiché gli ultimi due attributi saranno multivalore, li ho rimossi e creato nuovi schemi come:

itemID,required_skill ( itemID è una chiave esterna, itemID and required_skill è la chiave primaria.)

Ora, sono confuso su come creare / utilizzare questa nuova tabella. Ecco le opzioni che mi sono venute in mente:

1) La relazione tra articoli e Required_skills è uno-a-molti, quindi potrei creare una classe RequiredSkill, che appartiene all'elemento, che a sua volta ha n RequiredSkills. Quindi posso fare Item.get(1).requiredskills . Mi sembra molto logico e mi fornisce metodi e query come:

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

2) Poiché require_skills può essere considerato come una costante (poiché assomigliano alle regole), posso inserirli in un database hash o gdbm o un'altra tabella sql e interrogare da lì, cosa che non preferisco.

La mia domanda è: c'è qualcosa come una tabella modellistica in datamapper, in cui il datamapper è responsabile della creazione e dell'integrità della tabella e mi permette di interrogarlo in modalità datamapper, ma non richiede una classe, come posso fare in sql?

Ho risolto il mio problema utilizzando il primo modo: ho creato una nuova classe per ogni processo di normalizzazione (che appare come associazione uno a molti sopra). Tuttavia, io sono nuovo alla programmazione orientata agli oggetti e non so se la creazione di una nuova classe per ciascuna di tali normalizzazioni sia il solito modo di farlo in datamapper, o piuttosto un hack? Questo, e se c'è un modo migliore per farlo, è quello che mi piacerebbe sapere.

@JustinC

Rileggendo più volte le datamapper.org associazioni , ora vedo che il datamapper richiede certamente classi separate per i join . Quindi hai risposto alla mia domanda. Tuttavia, da quando Robert Harvey ha piazzato la taglia, sento la responsabilità di aspettare un po 'di più per una risposta su un modo dinamico.

Il tuo codice si è lamentato di Cannot find the child_model Container for Item in containers . Sono riuscito a farlo funzionare con il secondo esempio di associazione autoreferenziale come di seguito (mettendo qui come riferimento ad altri):

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

Quindi posso fare:

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

per richiedere elementi e:

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

per elencare i requisiti, che sono davvero fantastici.

Dopo aver letto la tua risposta, vedo che devo ancora normalizzare ulteriormente lo schema del mio database. Per essere onesti, non vedo il punto di definire la relazione tra materie prime e prodotti come autoreferenziali. Voglio dire, se quello fosse un programma di piccole dimensioni, certamente userei un hash come {:jelly => ":sugar => 3, :water => 5"} per riflettere gli articoli e gli importi richiesti, secondo il principio di YAGNI. Farlo come nella prima opzione mi fornisce già query e metodi semplici come quelli forniti dall'associazione autoreferenziale. (Tuttavia, devo ammettere che sembra più una stored procedure piuttosto che una chiamata di metodo a un oggetto.)

Quindi, potrebbe esserti molto utile spiegare i vantaggi dell'uso di un'associazione autoreferenziale, che è difficile da capire / implementare per me, rispetto al mio approccio più semplice? Sono nuovo di OOP e mi chiedo se sono un po 'sottodimensionato.

    
posta barerd 20.10.2012 - 00:04
fonte

1 risposta

2

Penso che quello che stai cercando a livello concettuale SQL sia una tabella di giunzioni (mappa, link, resolver, pivot sono anche nomi comuni per la gestione di molte e molte relazioni). Queste tabelle di giunzione sono generalmente tabelle intermedie; tuttavia, gli attributi aggiuntivi possono e sono spesso aggiunti a loro.

L'intenzione dello pseudo schema dichiarato è un po 'torbida, ma penso che quello che intendevi è che gli oggetti possono richiedere più abilità; gli oggetti possono anche richiedere altri oggetti, ciascuno con le proprie abilità richieste, possibilmente possedere elementi necessari, e così via, a molti livelli in profondità. Fai attenzione ai riferimenti circolari nelle tue [molte-a-molte] relazioni autoreferenziali, come ciò che potrebbe accadere in "containerItemMaps". Il seguente pseudo-schema riflette come immagino l'intento dell'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

La terminologia ActiveRecord suggerisce "has_and_belongs_to_many" come tipo di associazione una relazione in un modello di dati da utilizzare in questa situazione. Per maggiori informazioni si consiglia di consultare la pagina all'indirizzo datamapper.org Associations . In particolare le sezioni intitolate "Ha e appartiene a molti (o molti a molti)" e "Rapporti auto-referenziali da molte a molte"

Perché non sono un rubino a questo punto, posso solo confondere con il codice rubino per dare un esempio, ma questa è la mia migliore approssimazione di come dovrebbe essere la tua classe di articoli:

# 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

E la tabella delle mappe per auto-referenziare molti elementi, ad esempio .. elementi richiesti:

#revised
class ComponentMap
  include DataMapper::Resource

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

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

Per completezza:

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

Revisioni:

Notando le tue preoccupazioni, sono andato e ho installato un interprete e un debugger per verificare il codice compilato e lo sql emesso era più ideale. Inizialmente, stavo solo uscendo dall'esame sommario della documentazione. Le strutture di cui sopra dovrebbero produrre sql generalmente ben strutturati dalle mappature.

Indipendentemente dai campi e dalle strutture che usi, e indipendentemente dall'ORM che scegli (datamapper o qualche altro provider), dovrai eseguire il codice attraverso il debugger e prestare attenzione al sql che emette quando a volte i mapping non sono è necessariamente quello che potreste prima aspettarvi.

Una seconda nota sulle tabelle di giuntura (skillMap e componentMap): nota la mia inclusione di campi aggiuntivi (quantità e padronanza). Questi sembrano essere perfettamente adatti al tipo di applicazione originariamente descritta, anche se non originariamente specificata. In una ricetta, alcuni ingredienti sono comuni tra molte combinazioni diverse, tuttavia la quantità da ricetta a ricetta varia. Per le abilità, come gli ingredienti, il livello di abilità necessario per eseguire determinate attività varia, e quindi ho aggiunto un campo di padronanza alla tabella di abilità skillMap.

Ovviamente, probabilmente vorrai aggiungere regole di business e funzioni helper appropriate (per accedere alla composizione delle raccolte in modo programmatico come aggiungere e rimuovere elementi, aggiungere e rimuovere gruppi di elementi e così via).

Speriamo che questo dimostri un po 'meglio una ragione per cui potresti voler considerare e usare la tabella di giunzione su un hash lineare. Naturalmente ogni specifica applicazione è diversa, e forse nel tuo caso non è necessaria la capacità di specificare ulteriori aspetti della relazione tra oggetti e abilità e oggetti e altri elementi.

Avere e utilizzare il controllo extra nella definizione esplicita delle relazioni presenta molti vantaggi rispetto a fare affidamento su una mappatura dinamica / magica. In alcuni casi, sento che è davvero necessario, e penso che nel caso di molti a molti, questo è dimostrato. Per uno-a-molti, la relazione è più facile da dedurre e l'utilizzo di un metodo più dinamico di generazione dei mapping (ad esempio has n,: < attributo group & gt ;,: through = > Resource) sarebbe accettabile.

    
risposta data 25.10.2012 - 17:39
fonte

Leggi altre domande sui tag