2012-10-11 30 views
7

Ich versuche zur Zeit das folgende Szenario mit DBIx zu implementieren:DBIx und Vererbung in Perl

Die Tabelle Produkte „allgemeine Produkte“ und „Bundle-Produkte“ enthalten (Bündelprodukte Sammlungen von allgemeinen Produkten sind):

package Product; 
use base 'DBIx::Class::Core'; 
__PACKAGE__->table("products"); 
__PACKAGE__->add_columns(
    "productId", 
    { data_type => "varchar", is_nullable => 0, size => 10}, 
    "name", 
    { data_type => "varchar", is_nullable => 1, size => 150}, 
    "type", 
    { 
    data_type => "enum", 
    default_value => "general", 
    extra => { 
     list => ["general", "bundle"], 
    }, 
    is_nullable => 0, 
    }); 

Wie Sie sehen können, ob das Produkt ein allgemeinen Produkt oder ein Bündel Produkt wird in der Spalte Typ gespeichert.

Nun möchte ich gerne diese Informationen in der Klassenidentität kapseln: Ich möchte folgende Klassen haben:

  • Produkt (type spielt keine Rolle)
  • BundleProduct (type = 'Bündel')
  • GeneralProduct (type = 'General')

ich schrieb:

package BundleProduct; 
use base 'Product'; 

__PACKAGE__->resultset_attributes({ where => { 'type' => 'bundle' } }); 
1; 

und

package GeneralProduct; 
use base 'Product'; 

__PACKAGE__->resultset_attributes({ where => { 'type' => 'general' } }); 
1; 

Aber wenn

my @allProducts = $schema->resultset('BundleProduct')->all; 

alle allgemeinen Produkte sind Ausführung geholt. Obwohl die resultierenden Objekte der Instanz BundleProduct sind, enthält das generierte SQL die WHERE-Bedingung der Klasse GeneralProduct (type = 'allgemein'). Noch schlimmer: Wenn ich versuche, eine Product (Basisklasse BundleProduct und GeneralProduct) zu holen, wird auch die Bedingung type = 'allgemein' angewendet! Es scheint, dass die Definition innerhalb GeneralProduct alle anderen Definitionen überschreibt.

Was stimmt nicht mit meinem Design?

Antwort

0

Es kann immer auf allgemein Standard sein, da die Daten nicht zu einem Enum-Objekt aufgeblasen werden?

überrascht, dass es keine klaren Fehler nicht geben, aber vielleicht das Hinzufügen unten (zu Product Paket) wird Ihr Problem beheben:

__PACKAGE__->load_components(qw/InflateColumn::Object::Enum/); 

Zusätzliche oben auch versuchen is_enum => 1 zu Ihrer Typ Spaltendefinition hinzufügen:

type => { 
    data_type  => "enum", 
    is_enum  => 1, 
    default_value => "general", 
    is_nullable => 0, 
    extra => { 
    list => ["general", "bundle"], 
    }, 
}, 

NB. Dies sollte die Verwendung des Inflation-Objekts (Object::Enum) erzwingen, während ohne es glaube ich, es versucht, die enum nativen RDBMS zu verwenden, die Sie verwenden (wenn es vorhanden ist).

Daumen drücken das funktioniert.Wenn nicht, versuchen Sie, die default_value zu entfernen, um zu sehen, wie sich das auf Dinge auswirkt.

+0

@dreagtun Danke, aber ich denke, dass InflateColumn :: Object :: Enum nur für die Umwandlung von 'varchar'-Felder in' enum' Felder innerhalb der Anwendung bestimmt ist. [link] (http://search.cpan.org/~jmmills/DBIx-Class-InflateColumn-Object-Enum-0.04/lib/DBIx/Class/InflateColumn/Object/Enum.pm) –

+0

Blick auf die bereitgestellten Tests Beim 'DBIx :: Class :: InflateColumn :: Object :: Enum'-Modul lädt der Autor immer die Komponente und setzt auch' is_enum => 1'. Hier ist der Test, der zu dem passt, was Sie in Ihrem Beispiel versuchen: https://metacpan.org/source/JMMILLS/DBIx-Class-InflateColumn-Object-Enum-0.04/t/lib/TestDB/NativeEnumNoneNullable.pm Der Autor vielleicht übereifrig, aber vielleicht einen Versuch wert. Auch der Autor bietet keine 'default_value'-Tests, so dass es dort ein Problem geben könnte? Jedenfalls habe ich meine Antwort entsprechend aktualisiert. – draegtun

0

Dies könnte ein bisschen OT sein, aber ich scheine immer auf einige heimtückische Anwendung Implementierungsproblem beim Umgang mit Enum-Datentypen in meinen Schemas stoßen.

Also ich benutze sie nicht mehr. Ich benutze Fremdschlüsselbeziehungen und entweder einzelne Tabellen oder eine kombinierte Tabelle alle Codes halten:

<id, code_type, code_name> 
< 1, 'product_type', 'bundle'> 
< 2, 'product_type', 'general'> 

und ich komme dann aus dem Produkt auf product.product_type_id = code_table.id

Diese Technik PRODUCT_TYPE hat Anwendungsimplementierung gemacht viel, viel einfacher auf Kosten einer zusätzlichen Datenbankverwaltung zu Beginn des Projekts.

+1

Sie können die Verknüpfung vermeiden, indem Sie eine 'product_types'-Tabelle mit einem einzelnen Feld erstellen, dem Typnamen, der eine eindeutige Integritätsbedingung hat und ein Fremdschlüssel in der 'products'-Tabelle ist. Der Fremdschlüssel erzwingt die begrenzte Menge von Werten für das Feld, und da der Schlüssel der Name und nicht eine Zahl ist, müssen keine zusätzlichen Verknüpfungen erstellt werden. – friedo

+0

Wahr. aber ich benutze keine Daten mehr für Schlüssel. Egal, wie sehr ich es versuche, irgendwann möchte jemand die Schlüsseldaten ändern und dann haben Sie alle zusätzlichen Arbeiten, um diese Aktualisierungen zu erledigen. Wenn der Code häufig verwendet wird, ist er im Speicher, so dass der Join nicht lange dauert. Warum in aller Welt hat jeder Angst vor Joins? –

4

Die Verwendung von resultset_attributes wird nicht empfohlen. Sie sollten eine Ergebnismenge Klasse für Product mit Methoden bundle_products und general_products implementieren:

package My::Schema::ResultSet::Product; 
use base 'DBIx::Class::ResultSet'; 

sub bundle_products { shift->search({ type => 'bundle' }); } 
sub general_products { shift->search({ type => 'general' }); } 

Dann Sie bestimmte Produkte wie folgt suchen:

$schema->resultset('Product')->bundle_products->all; 
$schema->resultset('Product')->general_products->all; 

die documentation of resultset_attributes See.

Schauen Sie sich auch DBIx::Class::DynamicSubclass. Es fügt einige nützliche Funktionen hinzu, wenn Ergebnisse untergliedert werden.

+0

++ für DBIx :: Klasse :: DynamicSubclass in diesem Szenario – tospo

+0

... oder es "von Hand" wie im Kochbuch beschrieben: http://search.cpan.org/~ribashihi/DBIx-Class-0.08250/ lib/DBIx/Klasse/Handbuch/Cookbook.pod # Dynamic_Sub-classing_DBIx :: Class_proxy_classes_% 28AKA_multi-class_object_inflation_from_one_table% 29 – tospo