|
Jul
30
|
Achtung:
Dieses Turorial bezieht sich auf eine veraltete Version von Doctrine und funktioniert vermutlich mit den neueren 1.x und späteren 2.x Releasen nicht mehr.
Nach dem wir im letzten Beitrag die Grundlagen zu Doctrine gelernt haben, wollen wir heute ein wenig weiter in die Tiefe gehen.
Wir nehmen wieder unser Beispiel und nehmen an wir haben ein Produkt z.B. Coca Cola. Hierzu gibt es viele Varianten wie z.B. Zero, Light, Cherry. Diese wollen wir speichern und dabei das Original- bzw. Basisprodukt nur einmal speichern. Dies ist eine 1:n Beziehung, die wir in Doctrine abbilden möchten.
Der Grundaufbau der Includes etc. ist genau der selbe, wie wir ihn beim letzten Mal verwendet haben, daher will ich diesen nicht mehr erläutern.Beginnen wir nun direkt und erzeugen zunächst die Datenbank-Tabelle, in der wir die Variationsen speichern werden. Alles natürlich rudimentär. Hierzu dient folgender SQL-Befehl:
1 2 3 4 5 6 7 | CREATE TABLE `products_variations` ( `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, `products_id` BIGINT(20) UNSIGNED NOT NULL, `name` VARCHAR(255) COLLATE latin1_general_ci NOT NULL, `date_created` datetime NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci; |
Als nächstes legen wir die Datenbank-Klasse für die Tabelle an, hier kommt direkt die erste Besonderheit. Doctrine verwendet für das Einsetzen der Verbindungen zwischen den Klasse eine SetUp-Methode. Diese setzt dann die Verbindungen in folgender Weise:
1 2 | $this->hasOne('Data_Db_Products', array('local' => 'products_id', 'foreign' => 'id')); |
Es wird mittels hasOne eine Beziehung gesetzt und zwar wird der lokale Schlüssel products_id mit dem fremden Schlüssel id verknüpft. Dies funktioniert nur bei 1:n Beziehungen in dieser Weise. Für n:m Beziehungen gilt eine andere Schreibweise. Dies wird Thema eines anderen Beitrages sein
. Die fertige Datenbankklasse sieht nun folgendermaßen aus:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | class Data_Db_ProductsVariations extends Doctrine_Record { public function setTableDefinition ( ) { $this->setTableName('products_variations'); $this->hasColumn('id', 'integer', 4, array('notnull' => true, 'primary' => true, 'unsigned' > true, 'autoincrement' => true)); $this->hasColumn('products_id' , 'integer' , 8 , array ('notnull' => true)); $this->hasColumn('name' , 'string' , 3000 , array ('notnull' => true)); $this->hasColumn('date_created' , 'timestamp' , null); } public function setUp() { $this->hasOne('Data_Db_Products', array('local' => 'products_id', 'foreign' => 'id')); } } |
Nun hilft uns eine einseitige Verknüpfung in diesem Fall nichts, also setzen wir nun den “n”-Part in der Produkt-Klasse. Diese sieht in einsatzbereitem Zustand so aus:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | class Data_Db_Products extends Doctrine_Record { public function setTableDefinition ( ) { $this->setTableName('products'); $this->hasColumn('id', 'integer', 4, array('notnull' => true, 'primary' => true, 'unsigned' > true, 'autoincrement' => true)); $this->hasColumn('name' , 'string' , 255 , array ('notnull' => true)); $this->hasColumn('description' , 'string' , 3000 , array ('notnull' => true)); $this->hasColumn('date_created' , 'timestamp' , null); } public function setUp ( ) { $this->hasMany('Data_Db_ProductsVariations', array('local' => 'id', 'foreign' => 'products_id')); } } |
Jetzt sind wir soweit, dass wir weiter arbeiten können. Wir fügen nun zunächst mal ein Basis-Produkt ein, mit dem wir dann die anderen Beispiele durchgehen. In meinem Fall ist es das Produkt “Coca Cola”:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | try { $Product = new Data_Db_Products; $Product [ 'name' ] = 'Coca Cola'; $Product [ 'description' ] = 'Erfrischungsgetraenk'; $Product [ 'date_created' ] = date('Y-m-d'); $Product->save(); } catch ( Exception $e ) { var_dump($e->getMessage()); } |
Ist das Produkt angelegt, machen wir uns daran die Variationsen anzulegen. Hierzu gibt es zwei verschiedene Möglichkeiten. Die Möglichkeit A verwendet die Array-Implementation von Doctrine. Sie ist jedoch nicht wirklich performant, da durch die Schreibweise mit den Array-Klammern zunächst alle bisherigen Einträge gelesen werden müssen, um den korrekten Index berechnen zu können, ich stelle sie jedoch trotzdem vor, der Vollständigkeit halber:
1 2 3 4 5 | $Product = $Connection->getTable ( 'Data_Db_Products' )->find ( 1 );$ProductVariations = new Data_Db_ProductsVariations; $ProductVariations[ 'name' ] = 'Zero'; $ProductVariations[ 'date_created' ] = date('Y-m-d'); $Product->Data_Db_ProductsVariations[] = $ProductVariations; $Product->save(); |
Die zweite Variations ist durchaus performanter, führt aber zum selben Ergebnis, verursacht nur weniger Datenbank-Last:
1 2 3 4 5 6 | $Product = $Connection->getTable ( 'Data_Db_Products' )->find ( 1 ); $ProductVariations = new Data_Db_ProductsVariations; $ProductVariations[ 'name' ] = 'Light'; $ProductVariations[ 'products_id' ] = $Product [ 'id' ]; $ProductVariations[ 'date_created' ] = date('Y-m-d'); $ProductVariations->save(); |
Jetzt haben wir ein Produkt mit zwei Variationsen in die Datenbank gespeichert. Diese wollen wir nun auslesen. Auch dazu gibt es zwei verschiedene Möglichkeiten, die je nach Einsatzzweck beide sinnvoll sind. Will man wirklich explizit nur ein Produkt auslesen, dann verwendet man den Finder, möchte man evtl. viele verschiedene Produkte mit ihren Variationsen auslesen, verwendet man den Query. In diesem Fall ist der Query natürlich nicht so sinnvoll, aber er sei der Vollständigkeit halber vorgestellt. Bei dieser Gelegenheit schauen wir uns dann auch mal einen Query an, den Doctrine generiert. Hierzu müssen wir nur ein echo auf das Query-Objekt machen, denn Doctrine implementiert die __toString()-Methode:
1 2 3 4 | $Product = $Connection->getTable ( 'Data_Db_Products' )->find ( 1 ); $Query = new Doctrine_Query; $Query->from ( 'Data_Db_Products p , p.Data_Db_ProductsVariations' ); $Query->where ( 'p.id = ' . (int)$Product [ 'id' ] ); |
Doctrine generiert dabei folgenden Query:
1 2 3 4 5 6 7 8 9 10 11 12 13 | SELECT p.id AS p__id, p.name AS p__name, p.description AS p__description, p.date_created AS p__date_created, p2.id AS p2__id, p2.products_id AS p2__products_id, p2.name AS p2__name, p2.date_created AS p2__date_created FROM product p LEFT JOIN products_variations p2 ON p.id = p2.products_id WHERE p.id = 1 |
Wir sehen nun, dass Doctrine einen LeftJoin erstellt. Dies können wir natürlich auch beeinflussen, aber das soll dann Thema eines anderen Beitrags sein. Führen wir diesen Query nun aus
1 | $Result = $Query->execute(array(1), Doctrine::FETCH_ARRAY); |
erhalten wir folgendes Ergebnis. Wobei zu sagen ist, dass ich absichtlich nur ein Array abhole, um Performance zu sparen. Man könnte natürlich auch eine Objekthirarchie abholen.
Array
(
[0] => Array
(
[id] => 1
[name] => Coca Cola
[description] => Erfrischungsgetraenk
[date_created] => 2007-07-30 00:00:00
[Data_Db_ProductsVariations] => Array
(
[0] => Array
(
[id] => 1
[products_id] => 1
[name] => Zero
[date_created] => 2007-07-30 00:00:00
)[1] => Array
(
[id] => 2
[products_id] => 1
[name] => Light
[date_created] => 2007-07-30 00:00:00
))
)
)
Wir erhalten nun ein Array mit dem gesuchten Produkt und mit deinen Variationsen, mehr wollten wir ja nicht
. Schauen wir uns nun noch die zweite Variante an (per Finder). Hier haben wir (noch) nicht die Möglichkeit nur ein Array abzuholen. Wir suchen also das Produkt mit der ID 1 und können dann per Pfeiloperator auf die Variationsen zugreifen, die verknüpft sind. Doctrine wird dann im Moment des Zugriffs die betreffenden Daten nachladen (Martin Fowler’s Lazy Loading)
1 2 3 4 5 6 7 8 9 | $Product = $Connection->getTable ( 'Data_Db_Products' )->find ( 1 ); foreach ( $Product->Data_Db_ProductsVariations as $Variations ) { echo $Variations["name"]; echo "<br />"; } |
Dabei erhalten wir folgendes Ergebnis:
Zero
Light
Es hat also funktioniert
. Ich hoffe dies war ein weiterer Einstieg in die Arbeit mit Doctrine
. Bei der Arbeit mit Doctrine kann man viele spannende Dinge der Objektorientierung lernen und es findt sich bei fast jedem Überlegen ein neuer “cooler” Weg die Daten zu verarbeiten. Beim nächsten Beitrag werden wir uns mit dem Verknüpfen von n:m Beziehungen beschäftigen. Genauer mit der Verknüpfung von Packungseinheiten zu ProduktVariationsen.
Kommentare sind natürlich willkommen
August 2nd, 2007 at 19:47
Nachdem im letzten Beitrag das Thema 1:n Beziehungen beleuchtet haben, wenden wir uns nun der letzten Beziehungsart zu, die man mit einer Datenbankrelation abbilden kann, nämlich der n:m Beziehung.
Als Basis dazu nehmen wir die Klassen und Datenbanktabel