Hallo,
schon seit laengerem wollte ich euch mal QCodo vorstellen, das in Europa noch nicht sehr weit verbreitet zu sein scheint, hier in den USA ist es mir jedoch schon oefter untergekommen, nicht nur weil es unser Standard-Framework hier in der Agentur ist. Ist aber einen Blick wert Kann sein dass ich dazu etwas ausschweife, ich werde auch kurz meinen Programmierstil vorstellen und auch nicht zutief in die Materie eindringen, aber zumindest die erste Huerde sollte dadurch genommen werden
QCodo/ZCodo's OR-Mapper (Object-Relational-Mapping) wird als Input mit einem MySQL-Datenbank-Schema gefuettert und parsed daraus PHP-Code, der einem volle CRUD-Funktionalitaet (Create, Read, Update, Delete) bietet. Zusaetzlich werden automatisch oder ueber Konfiguration festgelegte Beziehungen zu anderen Tabellen mit in die Klassen-Generierung einbezogen. Auch werden indizierte Spalten oder UNIQUE-Felder beruecksichtigt, in dem Zugriffsmethoden fuer die Klasse generiert werden.
Auch haben die Entwickler etwas nachgedacht und generieren nicht nur eine Klasse, sondern gleich 2, denn schliesslich moechte man der Klasse eigene Funktionalitaeten hinzufuegen, die bei einer Neugenerierung (z.B. nach Schemaaenderung) nicht gleich wieder ueberschrieben werden sollen. Fuer eine Tabelle namens "Article" bekommen wir also die leere Klasse "Article", die wir mit unserem Code erweitern koennen, sowie die Klasse "ArticleGen", von der "Article" erbt und die QCodo bei jeder Code-Generierung ueberschreibt.
Unsere Systemvoraussetzungen sind PHP 5 und die MySQL-Engine innoDB. Letzteres ist wichtig, da wird die Beziehung der Tabellen untereinander implizit und nicht explizit in der Konfiguration festlegen.
Wenn ihr dem Tutorial nachprogrammieren wollt, installiert jetzt bitte QCodo:
http://www.qcodo.com/downloads/
und konfiguriert es, so dass ihr die Startseite ohne Fehler (magic_quotes_gpc-Meldung) erhaltet. Als Quickfix:
Einfach in "wwwroot/includes/configuration.inc.php", der Konstanten __DOCROOT__ ein dirname(__FILE__) . "/.." zuweisen und gegebenenfalls eine ".htaccess" Datei mit "php_flag magic_quotes_gpc off" in den Ordner "wwwroot" legen, ganz nach unten scrollen und die Datenbankverbindung eingeben, fertig.
Ich verzichte heute mal auf das Alben/CDs Beispiel und tue so, als wolle ich mit QCodo einen kleinen Shop mit Usern, Kategorien und Artikeln basteln. Verwenden wir der Einfachheit halber ein einfaches (uebrigens falsches) Konzept:
Category, User, Cart, Article, Cart_Ref, Billing.
Ich werde hier nur kurz auf User, Article und Category eingehen, zum Testen koennt ihr ja die restlichen Tabellen/Klassen verwenden. Es geht wirklich schnell! Auch muesst das Konzept nicht 1:1 uebernehmen, vielleicht lernt ihr mehr, wenn ihr euer Schema selbst erstellt. Achtet darauf, die Tabellen in Einzahl zu benennen und die Primaerschluessel mit "id" zu benennen. Spalten oder Tabellennamen die in MySQL mit _ versehen werden, werden von QCodo in PHP in Camel-Schreibweise umgewandelt. Aus der Tabelle "Cart_Ref" wird also die PHP-Klasse "CartRef" (und "CartRefGen"), aus "user_name" wird "UserName". Auch verwendet QCodo intern einen 3-Zeichen-Prefix fuer Variablen, "obj" also fuer Objekte, "str" fuer Strings etc. Methodennamen beginnen mit einem Grossbuchstaben. Ich kann mein Coding immer relativ schnell umschalten, auf das jeweilige Framework und ich finde es lohnt sich, ein einheitliches Coding pro Projekt zu verwenden. Aber das ist hier zweitrangig.
Hier das Schema, nehmen wirs nicht so genau:
Im Anhang findet ihr den von mir verwendeten SQL-Code, die Verbindungen sind ON DELETE/UPDATE CASCADE, mit Ausnahme der Article/Category Beziehung (zur Veranschaulichung), hier haben wir eine 0:1 Beziehung (NULL fuer category_id erlaubt -> ON DELETE SET NULL).
Wenn wir nun den Codegenerator aufrufen, damit er unsere Datenbank scannt:
/_devtools/codegen.php
.. sollte die folgende Ausgabe erscheinen:
Wir finden unsere Klassen in includes/data_classes bzw. includes/data_classes/generated. Unabhaengig davon wurden uebrigens auch DataGrid und Form-Klassen erstellt, mit deren Hilfe ihr eure Tabellen fuellen koennt. Dazu stellt QCodo ein eigenartiges, aber auch ziemlich geniales Formular-Management zur Verfuegung, welches aber nicht Teil dieses Tutorials ist. Schaut euch dazu aber einfach mal die "Examples" der QCodo-Website an, man braucht einige Zeit um einzusteigen, aber wenn man QCodo fuers OR-Mapping verwendet, sollte man nach einiger Zeit auch mit QForms zurechtkommen.
Zurueck zum OR-Mapping. Schaut euch die generierten Klassen einmal an (z.B. Outline-View von Eclipse/PDT), ihr werdet sehen, dass in "UserGen" die statischen Zugriffsmethoden LoadById (Alias Load), LoadByEmail und LoadByName generiert wurden, ganz nach unserem Schema, in der diese Spalten indiziert (Primaerschluessel) bzw. als UNIQUE gesetzt wurden. Zugriff auf die Inhalte der Datenbank erhaltet ihr also so:
Ihr koennt das Objekt nun nach belieben manipulieren:
Beachtet, dass moeglicherweise beim Speichern eine Q..Exception geworfen wird, z.B. wenn ein NOT NULL Feld NULL geblieben ist oder andere Fehler aufgetreten sind. Einen User anlegen, loeschen oder kopieren geht natuerlich auch:
Zum Kopieren koennt ihr die Parameter der Save-Methode verwenden.
Vergesst aber nicht, dass die *Gen-Klassen fuer Erweiterungen tabu bleiben, sonst zerstoert ihr das Konzept des OR-Mappings bei QCodo.
Wenn wir nun unsere generierte "ArticleGen" Klasse anschauen, werden wir sehen, dass auch hier unsere Beziehung mit LoadArrayByCategoryId() umgesetzt wurde. Die Methode ist statisch und liefert uns als Ergebnis einen Array aller "Article" Objekte:
Meine Vorgehensweise beim URL-gesteuerten Anzeigen von Objekten ist dann uebrigens die, dass ich jedem Objekt eine AutoloadId() bzw. Autoload() Methode spendiere, die etwa so aussieht:
Jede Klasse kennt also ihren Parameter, mit dem sie angesprochen wird, und so bleibt jede Information dort, wo sie hingehoert. Im Code saehe der Zugriff auf diese Artikel also bei mir etwa so aus:
Wobei die Exceptions und das Templating Eigenbau sind. Mit SetContext merge ich $objCategory gewissermassen ins $this-Objekt von Template, um direkt darauf zuzugreifen. Siehe <h1> Tag, der die Name-Eigenschaft fuer das Category-Objekt abgreift. Technisch umgesetzt mit __get/__set.
Also meine Template-Dateien sehen dann etwa so aus:
article_list.tpl.php
(ich benutze fuer Templates keine ; und nur die : end* Syntax, wenn man das Konzept durchzieht kommt eine ziemlich uebersichtliche Sache bei raus)
GetArticles() ist wiederum eine selbstgeschriebene Methode, die eigentlich nur folgendes macht:
Category.class.php
Sieht aber schoener aus (auch wenn ich dadurch eine neue Abhaengigkeit schaffe) und ausserdem kann ich dann zusaetzliche Filter einbauen, die dann nicht im Template stehen muessen. Jeder LoadArrayBy*() Funktion koennen uebrigens Clauses uebergeben werden, also Sortierungen oder neue Bedingungen. Stellt euch vor ihr habt noch einen "Visible" Flag oder aehnliches. Wenn ihr darauf einen Index setzt, koennt ihr aber uebrigens auch dafuer die Methode generieren lassen, sie hiesse dann
LoadArrayByCategoryIdVisible().
Manchmal spendiere ich auch eine GetSiblings() Methode, die sich eventuell auch fuer "Article" anbieten wuerde:
Ich versuche auch, Variablennamen nicht abzukuerzen und pro Begriff auch nur ein Wort zu verwenden, dass sich durch die ganze Applikation zieht (Tabelle-, Klasse-, Variablen-, Parameternamen). Wenn es also in der Datenbank eine Category gibt, finde ich es sinnvoll, die URL-Parameter auch "category_id" und die Objekte "objCategory" zu benennen. Wenn man anfaengt das ganze abzukuerzen ($objCat, $objCategory, "catid", "id", ..) ist das meiner Meinung nach der Anfang von Chaos. Ich fahre damit ziemlich gut. Aber das nur am Rande.
Ein Rueckwaertszugriff ist mit diesen Klassen also auch moeglich, also wenn ihr zu einem "Article" auch noch die "Category" braucht:
article_view.php
Natuerlich koennt ihr auch diese Objekte wieder wie die normalen Objekte verwenden, also Save() oder Load*() Operationen darauf ausfuehren.
Lasst euch nicht abschrecken von einem Framework, probiert es mal aus, stutzt euch die schlechte Ordner- und Konfigurations-Struktur von QCodo etwas zusammen und ihr werdet eine ganze Menge neuer Erfahrungen in OO-Programmierung sammeln. Ich hab dadurch zumindest gemerkt, was Abstraktion wirklich heisst, ich programmiere natuerlich immernoch viel Kleinklein, aber zumindest ums Datenbanking und das leidige SELECT/UPDATE/INSERT muss ich mich nicht mehr kuemmern.
Das wars vorerst zu diesem Tutorial, ihr seht, es ist eigentlich nicht viel, aber letztlich doch ein ganze Menge, was einem da an Arbeit abgenommen wird.
Nochmal am Ende sei auf die Examples auf der QCodo-Website hingewiesen, mit der Quellcode-Betrachtung wirklich ne runde Sache und das tolle ist ja, man versteht sofort was der Code macht und genau das sollte ja auch das Ziel sein. Fragen dazu? Einfach hier stellen, wuerde mich ueber einen Erfahrungsaustausch oder eine Diskussion freuen.
http://www.qcodo.com/
http://examples.qcodo.com/examples/
http://www.qcodo.com/downloads/
http://www.zcodo.com
(neue abtruennige Version, da der alte Maintainer als einziger mit Zugriff aufs SVN zu langsam auf Aenderungen/Erweiterungen reagiert hat)
Anhang:
dump.sql
schon seit laengerem wollte ich euch mal QCodo vorstellen, das in Europa noch nicht sehr weit verbreitet zu sein scheint, hier in den USA ist es mir jedoch schon oefter untergekommen, nicht nur weil es unser Standard-Framework hier in der Agentur ist. Ist aber einen Blick wert Kann sein dass ich dazu etwas ausschweife, ich werde auch kurz meinen Programmierstil vorstellen und auch nicht zutief in die Materie eindringen, aber zumindest die erste Huerde sollte dadurch genommen werden
QCodo/ZCodo's OR-Mapper (Object-Relational-Mapping) wird als Input mit einem MySQL-Datenbank-Schema gefuettert und parsed daraus PHP-Code, der einem volle CRUD-Funktionalitaet (Create, Read, Update, Delete) bietet. Zusaetzlich werden automatisch oder ueber Konfiguration festgelegte Beziehungen zu anderen Tabellen mit in die Klassen-Generierung einbezogen. Auch werden indizierte Spalten oder UNIQUE-Felder beruecksichtigt, in dem Zugriffsmethoden fuer die Klasse generiert werden.
Auch haben die Entwickler etwas nachgedacht und generieren nicht nur eine Klasse, sondern gleich 2, denn schliesslich moechte man der Klasse eigene Funktionalitaeten hinzufuegen, die bei einer Neugenerierung (z.B. nach Schemaaenderung) nicht gleich wieder ueberschrieben werden sollen. Fuer eine Tabelle namens "Article" bekommen wir also die leere Klasse "Article", die wir mit unserem Code erweitern koennen, sowie die Klasse "ArticleGen", von der "Article" erbt und die QCodo bei jeder Code-Generierung ueberschreibt.
Unsere Systemvoraussetzungen sind PHP 5 und die MySQL-Engine innoDB. Letzteres ist wichtig, da wird die Beziehung der Tabellen untereinander implizit und nicht explizit in der Konfiguration festlegen.
Wenn ihr dem Tutorial nachprogrammieren wollt, installiert jetzt bitte QCodo:
http://www.qcodo.com/downloads/
und konfiguriert es, so dass ihr die Startseite ohne Fehler (magic_quotes_gpc-Meldung) erhaltet. Als Quickfix:
Einfach in "wwwroot/includes/configuration.inc.php", der Konstanten __DOCROOT__ ein dirname(__FILE__) . "/.." zuweisen und gegebenenfalls eine ".htaccess" Datei mit "php_flag magic_quotes_gpc off" in den Ordner "wwwroot" legen, ganz nach unten scrollen und die Datenbankverbindung eingeben, fertig.
Ich verzichte heute mal auf das Alben/CDs Beispiel und tue so, als wolle ich mit QCodo einen kleinen Shop mit Usern, Kategorien und Artikeln basteln. Verwenden wir der Einfachheit halber ein einfaches (uebrigens falsches) Konzept:
Category, User, Cart, Article, Cart_Ref, Billing.
Ich werde hier nur kurz auf User, Article und Category eingehen, zum Testen koennt ihr ja die restlichen Tabellen/Klassen verwenden. Es geht wirklich schnell! Auch muesst das Konzept nicht 1:1 uebernehmen, vielleicht lernt ihr mehr, wenn ihr euer Schema selbst erstellt. Achtet darauf, die Tabellen in Einzahl zu benennen und die Primaerschluessel mit "id" zu benennen. Spalten oder Tabellennamen die in MySQL mit _ versehen werden, werden von QCodo in PHP in Camel-Schreibweise umgewandelt. Aus der Tabelle "Cart_Ref" wird also die PHP-Klasse "CartRef" (und "CartRefGen"), aus "user_name" wird "UserName". Auch verwendet QCodo intern einen 3-Zeichen-Prefix fuer Variablen, "obj" also fuer Objekte, "str" fuer Strings etc. Methodennamen beginnen mit einem Grossbuchstaben. Ich kann mein Coding immer relativ schnell umschalten, auf das jeweilige Framework und ich finde es lohnt sich, ein einheitliches Coding pro Projekt zu verwenden. Aber das ist hier zweitrangig.
Hier das Schema, nehmen wirs nicht so genau:
Im Anhang findet ihr den von mir verwendeten SQL-Code, die Verbindungen sind ON DELETE/UPDATE CASCADE, mit Ausnahme der Article/Category Beziehung (zur Veranschaulichung), hier haben wir eine 0:1 Beziehung (NULL fuer category_id erlaubt -> ON DELETE SET NULL).
Wenn wir nun den Codegenerator aufrufen, damit er unsere Datenbank scannt:
/_devtools/codegen.php
.. sollte die folgende Ausgabe erscheinen:
Code:
There were 6 tables available to attempt code generation: Successfully generated DB ORM Class: Article (with 1 relationship) Successfully generated DB ORM Class: Billing (with 1 relationship) Successfully generated DB ORM Class: Cart (with 1 relationship) Successfully generated DB ORM Class: CartRef (with 2 relationships) Successfully generated DB ORM Class: Category (with no relationships) Successfully generated DB ORM Class: User (with no relationships)
Wir finden unsere Klassen in includes/data_classes bzw. includes/data_classes/generated. Unabhaengig davon wurden uebrigens auch DataGrid und Form-Klassen erstellt, mit deren Hilfe ihr eure Tabellen fuellen koennt. Dazu stellt QCodo ein eigenartiges, aber auch ziemlich geniales Formular-Management zur Verfuegung, welches aber nicht Teil dieses Tutorials ist. Schaut euch dazu aber einfach mal die "Examples" der QCodo-Website an, man braucht einige Zeit um einzusteigen, aber wenn man QCodo fuers OR-Mapping verwendet, sollte man nach einiger Zeit auch mit QForms zurechtkommen.
Zurueck zum OR-Mapping. Schaut euch die generierten Klassen einmal an (z.B. Outline-View von Eclipse/PDT), ihr werdet sehen, dass in "UserGen" die statischen Zugriffsmethoden LoadById (Alias Load), LoadByEmail und LoadByName generiert wurden, ganz nach unserem Schema, in der diese Spalten indiziert (Primaerschluessel) bzw. als UNIQUE gesetzt wurden. Zugriff auf die Inhalte der Datenbank erhaltet ihr also so:
PHP-Code:
<?php
$objUser = User::Load(1); // id = 1
$objUser = User::LoadByName("Fritz"); // name = Fritz
$objUser = User::LoadByEmail("fritz@gibtsgar.net"); // email = ..
?>
Ihr koennt das Objekt nun nach belieben manipulieren:
PHP-Code:
<?php
$objUser = User::Load(1);
if (!$objUser instanceof User) { // im Fehlerfall ist $objUser NULL
throw new Exception(..);
}
$objUser->Name = "Max";
$objUser->Email = "max@gibtsgar.net";
$objUser->Save();
?>
Beachtet, dass moeglicherweise beim Speichern eine Q..Exception geworfen wird, z.B. wenn ein NOT NULL Feld NULL geblieben ist oder andere Fehler aufgetreten sind. Einen User anlegen, loeschen oder kopieren geht natuerlich auch:
PHP-Code:
<?php
$objUser = new User();
// oder ihr schreibt euch eine statische Methode fuer User:
$objUser = User::CreateNew("Peter", "peter@gibtsgar.net");
$objUser->Save();
// loeschen:
$objUser->Delete();
?>
Vergesst aber nicht, dass die *Gen-Klassen fuer Erweiterungen tabu bleiben, sonst zerstoert ihr das Konzept des OR-Mappings bei QCodo.
Wenn wir nun unsere generierte "ArticleGen" Klasse anschauen, werden wir sehen, dass auch hier unsere Beziehung mit LoadArrayByCategoryId() umgesetzt wurde. Die Methode ist statisch und liefert uns als Ergebnis einen Array aller "Article" Objekte:
PHP-Code:
<?php
foreach (Article::LoadArrayByCategoryId(5) as $objArticle) {
printf("<h1>%s<h1>
%s</p>", $objArticle->Name, $objArticle->Description);
}
?>
PHP-Code:
<?php
class Category .. {
// ..
public static function AutoloadId() {
return (int)@$_REQUEST["category_id"];
}
public static function Autoload() {
return self::Load(self::AutoloadId());
}
// ..
}
?>
Jede Klasse kennt also ihren Parameter, mit dem sie angesprochen wird, und so bleibt jede Information dort, wo sie hingehoert. Im Code saehe der Zugriff auf diese Artikel also bei mir etwa so aus:
PHP-Code:
<?php
try {
$objCategory = Category::Autoload();
if (!$objCategory instanceof Category) {
throw new CategoryException(CategoryException::IdNotFound, Category::AutoloadId());
}
$objTemplate = new Template();
$objTemplate->SetContext($objCategory);
$objTemplate->Render();
} catch (ApplicationException $objExc) {
$objExc->Run();
}
?>
Wobei die Exceptions und das Templating Eigenbau sind. Mit SetContext merge ich $objCategory gewissermassen ins $this-Objekt von Template, um direkt darauf zuzugreifen. Siehe <h1> Tag, der die Name-Eigenschaft fuer das Category-Objekt abgreift. Technisch umgesetzt mit __get/__set.
Also meine Template-Dateien sehen dann etwa so aus:
article_list.tpl.php
PHP-Code:
<? $this->Header->Render() ?>
<h1><?= $this->Name ?></h1>
<? foreach ($this->GetArticles() as $objArticle): ?>
<? endforeach ?>
<? $this->Footer->Render() ?>
GetArticles() ist wiederum eine selbstgeschriebene Methode, die eigentlich nur folgendes macht:
Category.class.php
PHP-Code:
<?php
public function GetArticles() {
return Article::LoadArrayByCategoryId($this->Id);
}
?>
Sieht aber schoener aus (auch wenn ich dadurch eine neue Abhaengigkeit schaffe) und ausserdem kann ich dann zusaetzliche Filter einbauen, die dann nicht im Template stehen muessen. Jeder LoadArrayBy*() Funktion koennen uebrigens Clauses uebergeben werden, also Sortierungen oder neue Bedingungen. Stellt euch vor ihr habt noch einen "Visible" Flag oder aehnliches. Wenn ihr darauf einen Index setzt, koennt ihr aber uebrigens auch dafuer die Methode generieren lassen, sie hiesse dann
LoadArrayByCategoryIdVisible().
Manchmal spendiere ich auch eine GetSiblings() Methode, die sich eventuell auch fuer "Article" anbieten wuerde:
PHP-Code:
<?php
class Article .. {
// ..
public function GetSiblings() {
return self::LoadArrayByCategoryId($this->CategoryId);
}
// ..
}
?>
Ich versuche auch, Variablennamen nicht abzukuerzen und pro Begriff auch nur ein Wort zu verwenden, dass sich durch die ganze Applikation zieht (Tabelle-, Klasse-, Variablen-, Parameternamen). Wenn es also in der Datenbank eine Category gibt, finde ich es sinnvoll, die URL-Parameter auch "category_id" und die Objekte "objCategory" zu benennen. Wenn man anfaengt das ganze abzukuerzen ($objCat, $objCategory, "catid", "id", ..) ist das meiner Meinung nach der Anfang von Chaos. Ich fahre damit ziemlich gut. Aber das nur am Rande.
Ein Rueckwaertszugriff ist mit diesen Klassen also auch moeglich, also wenn ihr zu einem "Article" auch noch die "Category" braucht:
article_view.php
PHP-Code:
<?php
try {
$objArticle = Article::Autoload();
// check
echo $objArticle->Category->Name; // Ausgabe des Category Name
} // ..
?>
Natuerlich koennt ihr auch diese Objekte wieder wie die normalen Objekte verwenden, also Save() oder Load*() Operationen darauf ausfuehren.
Lasst euch nicht abschrecken von einem Framework, probiert es mal aus, stutzt euch die schlechte Ordner- und Konfigurations-Struktur von QCodo etwas zusammen und ihr werdet eine ganze Menge neuer Erfahrungen in OO-Programmierung sammeln. Ich hab dadurch zumindest gemerkt, was Abstraktion wirklich heisst, ich programmiere natuerlich immernoch viel Kleinklein, aber zumindest ums Datenbanking und das leidige SELECT/UPDATE/INSERT muss ich mich nicht mehr kuemmern.
Das wars vorerst zu diesem Tutorial, ihr seht, es ist eigentlich nicht viel, aber letztlich doch ein ganze Menge, was einem da an Arbeit abgenommen wird.
Nochmal am Ende sei auf die Examples auf der QCodo-Website hingewiesen, mit der Quellcode-Betrachtung wirklich ne runde Sache und das tolle ist ja, man versteht sofort was der Code macht und genau das sollte ja auch das Ziel sein. Fragen dazu? Einfach hier stellen, wuerde mich ueber einen Erfahrungsaustausch oder eine Diskussion freuen.
http://www.qcodo.com/
http://examples.qcodo.com/examples/
http://www.qcodo.com/downloads/
http://www.zcodo.com
(neue abtruennige Version, da der alte Maintainer als einziger mit Zugriff aufs SVN zu langsam auf Aenderungen/Erweiterungen reagiert hat)
Anhang:
dump.sql
Code:
-- phpMyAdmin SQL Dump -- version 2.11.4 -- http://www.phpmyadmin.net -- -- Host: localhost:3306 -- Generation Time: May 07, 2008 at 11:25 PM -- Server version: 5.0.51 -- PHP Version: 5.2.5 SET FOREIGN_KEY_CHECKS=0; SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"; SET AUTOCOMMIT=0; START TRANSACTION; -- -- Database: `phpfriend` -- -- -------------------------------------------------------- -- -- Table structure for table `article` -- CREATE TABLE IF NOT EXISTS `article` ( `id` int(10) unsigned NOT NULL auto_increment, `category_id` int(10) unsigned default NULL, `name` varchar(50) NOT NULL default '', `description` text NOT NULL, `price` decimal(9,2) NOT NULL default '0.00', PRIMARY KEY (`id`), KEY `category_id` (`category_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; -- -------------------------------------------------------- -- -- Table structure for table `billing` -- CREATE TABLE IF NOT EXISTS `billing` ( `id` int(10) unsigned NOT NULL auto_increment, `cart_id` int(10) unsigned NOT NULL default '0', `address` text NOT NULL, `creditcard` text NOT NULL, `last_update` timestamp NOT NULL default CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `cart_id` (`cart_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; -- -------------------------------------------------------- -- -- Table structure for table `cart` -- CREATE TABLE IF NOT EXISTS `cart` ( `id` int(10) unsigned NOT NULL auto_increment, `user_id` int(10) unsigned NOT NULL default '0', `total` decimal(9,2) NOT NULL default '0.00', `last_update` timestamp NOT NULL default CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `user_id` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; -- -------------------------------------------------------- -- -- Table structure for table `cart_ref` -- CREATE TABLE IF NOT EXISTS `cart_ref` ( `id` int(10) unsigned NOT NULL auto_increment, `cart_id` int(10) unsigned NOT NULL default '0', `article_id` int(10) unsigned NOT NULL default '0', `quantity` int(10) unsigned NOT NULL default '0', PRIMARY KEY (`id`), KEY `cart_id` (`cart_id`), KEY `article_id` (`article_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; -- -------------------------------------------------------- -- -- Table structure for table `category` -- CREATE TABLE IF NOT EXISTS `category` ( `id` int(10) unsigned NOT NULL auto_increment, `name` varchar(50) NOT NULL default '', PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; -- -------------------------------------------------------- -- -- Table structure for table `user` -- CREATE TABLE IF NOT EXISTS `user` ( `id` int(10) unsigned NOT NULL auto_increment, `name` varchar(50) NOT NULL default '', `email` varchar(100) NOT NULL default '', `password` varchar(32) NOT NULL default '', `last_update` timestamp NOT NULL default CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`), UNIQUE KEY `email` (`email`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; -- -- Constraints for dumped tables -- -- -- Constraints for table `article` -- ALTER TABLE `article` ADD CONSTRAINT `article_ibfk_1` FOREIGN KEY (`category_id`) REFERENCES `category` (`id`) ON DELETE SET NULL ON UPDATE CASCADE; -- -- Constraints for table `billing` -- ALTER TABLE `billing` ADD CONSTRAINT `billing_ibfk_1` FOREIGN KEY (`cart_id`) REFERENCES `cart` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; -- -- Constraints for table `cart` -- ALTER TABLE `cart` ADD CONSTRAINT `cart_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; -- -- Constraints for table `cart_ref` -- ALTER TABLE `cart_ref` ADD CONSTRAINT `cart_ref_ibfk_2` FOREIGN KEY (`article_id`) REFERENCES `article` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, ADD CONSTRAINT `cart_ref_ibfk_1` FOREIGN KEY (`cart_id`) REFERENCES `cart` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; SET FOREIGN_KEY_CHECKS=1; COMMIT;
Kommentar