Propel Fremdschlüssel über mehrere MySQL Datenbanken anlegen
Wenn man Propel (ein ORM für SQL-Datenbanken in PHP) mit einer MySQL Datenbank nutzt, dann macht es absolut Sinn die Beziehung der Objekte sauber zu verknüpfen. Dieses geschieht indem man im Schema die Beziehungen über Fremdschlüssel (foreign keys) angibt. Es spielt dabei keine Rolle, ob in der Datenbank zwischen den Tabellen (z. B. bei InnoDB) tatsächlich Fremdschlüssel gesetzt werden können oder nicht (wie bspw. bei MyISAM).
Nicht wirklich komplizierter (aber leider wesentlich schlechter dokumentiert) wird es dann, wenn die Tabellen in verschiedenen Datenbanken liegen. Daher möchte ich Euch meine Lösung hier einmal zur Verfügung stellen.
Generell kann man in Propel bspw. für jede Datenbank eine eigene Datei pro Schema erstellen, jedoch funktioniert das nur, wenn man zwischen den Datenbanken keine Fremdschlüssel setzen will (also keine Beziehungen zwischen den Objekten herstellen muss oder möchte).
In Propel kann man aber auch das komplette Schema in einer einzigen Schema Datei unterbringen und genau das muss auch gemacht werden, damit die Verknüpfun von Tabellen datebankübergreifend funktioniert.
Als Beispiel nehmen wir einmal an, wir haben die Datenbank persons
mit der Tabelle author
(MyISAM) und die Datenbank bookstore
mit der Tabelle book
(InnoDB).
Ich habe weitestgehend das Originalbeispiel von der Propel Seite übernommen und die nötigen Änderungen eingearbeitet (siehe hervorgehobene Zeilen). Und so sieht das Schema dann mit wenigen Änderungen aus:
<?xml version="1.0" encoding="UTF-8"?> <database name="bookstore" schema="bookstore" defaultIdMethod="native"> <table name="book"> <column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" /> <column name="title" type="varchar" size="255" required="true" /> <column name="isbn" type="varchar" size="24" phpName="ISBN" /> <column name="author_id" type="integer" required="true" /> <foreign-key foreignTable="author" foreignSchema="persons" skipSql="true"> <reference local="author_id" foreign="id" /> </foreign-key> </table> <table name="author" phpName="Author" schema="persons"> <column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" /> <column name="first_name" type="varchar" size="128" /> <column name="last_name" type="varchar" size="128" /> </table> </database>
Wichtig zum generellen Verständnis:
- Das Ganze funktioniert mit MySQL, da MySQL kein Schema kennt bzw. nicht zwischen Schema und Datenbank unterscheidet. (siehe auch MySQL Glossary).
- Das Name des Attribut
schema
muss immer dem Datenbanknamen entsprechen, in der sich die Tabelle befindet. - Das hinzufügen des Schema Teils bewirkt, dass im SQL Statement dem Tabellennamen das Schema vorangestellt wird. So sagt man in MySQL in welcher Datenbank die entsprechende Tabelle liegt.
- Da alle Tabellen über eine Verbindung „angesprochen“ werden muss darauf geachtet werden, dass der Datenbankuser, der für das eigentliche Schema hinterlegt wurde, auch ausreichende Berechtigungen für die anderen Schemen hat.
Dem Tag database
in Zeile 2 wurde das Attribut schema="bookstore"
hinzugefügt. Wichtig zu wissen: Alle Tabellen, bei denen nicht explizit ein anderes Schema angegben wird, gehören damit zum Schema bookstore
. Dieser Teil ist optional solange man nur von bookstore
Fremdschlüssel zu persons
setzen will.
In Zeile 8 wurde der Tag foreign-key
um den Teil foreignSchema="persons"
ergänzt. Damit können wir in das andere Schema persons
springen. Optional kann man noch den Teil skipSql="true"
hinzufügen, falls datenbankseitig entweder keine Fremdschlüssel gesetzt werden können (weil MyISAM Tabellen das bspw. nicht können) oder man dieses nicht möchte.
Schließlich muss nun jede Tabelle, die sich nicht im Schema bookstore
befindet, noch explizit um dem Namen der Datenbank erweitert werden. Hier wird also der Tag table
in dem Beispiel in Zeile 12 um um den Teil schema="persons"
erweitert.
Nachdem die Propel Klassen erzeugt wurden kann die man nun über das Buch direkt auf den Autoren zugreifen:
$books = BookQuery::create() ->joinAuthor() ->find() ; foreach ($books as $book) { printf( '%s: %s von %s, %s', $book->getISBN(), $book->getTitle(), $book->getAuthor()->getLastName(), $book->getAuthor()->getFirstName() ); }