Ein Modell und eine Datenbank Tabelle erstellen
Bevor wir anfangen nehmen wie etwas an: Wo werden diese Klassen leben, und wie werden wir
Sie finden? Das Standardprojekt welches wir erstellt haben instanziert einen Autoloader. Wir
können Ihm andere Autoloader anhängen damit er weiss wo andere Klassen zu finden sind.
Typischerweise wollen wir das unsere verschiedenen MVC Klassen im selben Baum gruppiert sind
-- in diesem Fall application/ -- und meistens einen gemeinsamen Präfix
verwenden.
Zend_Controller_Front hat den Begriff von "Modulen", welche
individuelle Mini-Anwendungen sind. Module mimen die Verzeichnisstruktur welche das
zf Tool unter application/ einrichtet, und von allen
Klassen in Ihm wird angenommen das Sie mit einen gemeinsamen Präfix beginnen, dem Namen des
Moduls. application/ selbst ist ein Modul -- das "default" (Standard-)
Modul. Als solches richten wir das Autoloading für Ressourcen in diesem Verzeichnis ein, und
geben Ihm den Präfix "Default". Das kann getan werden indem eine weitere Bootstrap Ressource
erstellt wird.
Zend_Application_Module_Autoloader bietet die Funktionalität welche
benötigt wird um die verschiedenen Ressourcen unter einem Modul mit den richtigen
Verzeichnissen zu verbinden, und auch einen standardmäßigen Namensmechanismus. In unserer
Bootstrap Ressource instanzieren wir dass, und das wars schon. Diese Methode sieht wie folgt
aus:
'Default_',
'basePath' => dirname(__FILE__),
));
return $autoloader;
}
]]>
Die endgültige Bootstrap Klasse wird wie folgt aussehen:
'Default',
'basePath' => dirname(__FILE__),
));
return $autoloader;
}
protected function _initDoctype()
{
$this->bootstrap('view');
$view = $this->getResource('view');
$view->doctype('XHTML1_STRICT');
}
}
]]>
Nehmen wir jetzt also an was ein Guestbook ausmacht. Typischerweise sind Sie einfach eine
Liste ein Einträgen mit einem Kommentar (comment), einem
Zeitpunkt (timestamp) und oft einer Email Adresse.
Angenommen wir speichern diese in einer Datenbank, dann wollen wir auch einen
eindeutigen Identifikator für jeden Eintrag. Wir wollen in der Lage
sein einen Eintrag zu speichern, individuelle Einträge zu holen, und alle Einträge zu
empfangen. Als solches könnte das Modell einer einfachen Guestbook API wie folgt aussehen:
__get() und __set() bieten uns bequeme
Mechanismen an um auf individuelle Eigenschaften von Einträgen zuzugreifen und auf andere
Getter und Setter zu verweisen. Sie stellen auch sicher das nur Eigenschaften im Objekt
vorhanden sind die wir freigegeben haben.
find() und fetchAll() bieten die Fähigkeit
einen einzelnen Eintrag oder alle Einträge zu holen.
Von hier an können wir über die Einrichtung unserer Datenbank nachdenken.
Zuerst muss unsere Db Ressource initialisiert werden. Wie bei der
Layout und View kann die Konfiguration für die
Db Ressource angegeben werden. In der Datei
application/configs/application.ini müssen die folgenden Zeilen in den
richtigen Sektionen hinzugefügt werden.
Die endgültige Konfigurationsdatei sollte wie folgt aussehen:
Es ist zu beachten das die Datenbank(en) unter data/db/ gespeichert
wird. Diese Verzeichnisse sind zu erstellen und weltweit-schreibbar zu machen. Auf
Unix-artigen Systemen kann man das wie folgt durchführen:
Unter Windows muss man die Verzeichnisse im Explorer erstellen und die Zugriffsrechte so zu
setzen das jeder in das Verzeichnis schreiben darf.
Ab diesem Punkt haben wir eine Verbindung zu einer Datenbank; in unserem Fall ist es eine
verbindung zu einer Sqlite Datenbank die in unserem application/data/
Verzeichnis ist. Designen wir also eine einfache Tabelle die unsere Guestbook Einträge
enthalten wird.
Und damit wir gleich einige Arbeitsdaten haben, erstellen wir ein paar Zeilen an Information
um unsere Anwendung interessant zu machen.
Jetzt haben wir sowohl das Schema als auch einige Daten definiert. Schreiben wir also ein
Skript das wir jetzt ausführen können um diese Datenbank zu erstellen. Natürlich wird das
nicht in der Produktion benötigt, aber dieses Skriupt hilft Entwicklern die Notwendigkeiten
der Datenbank lokal zu erstellen damit Sie eine voll funktionsfähige Anwendung haben. Das
Skript ist als scripts/load.sqlite.php mit dem folgenden Inhalt zu
erstellen:
'Datenbank mit einigen Daten laden',
'env|e-s' => "Anwendungsumgebung für welche die Datenbank "
. "erstellt wird (Standard ist Development)",
'help|h' => 'Hilfe -- Verwendung',
));
try {
$getopt->parse();
} catch (Zend_Console_Getopt_Exception $e) {
// Schlechte Option übergeben: Verwendung ausgeben
echo $e->getUsageMessage();
return false;
}
// Wenn Hilfe angefragt wurde, Verwendung ausgeben
if ($getopt->getOption('h')) {
echo $getopt->getUsageMessage();
return true;
}
// Werte basierend auf Ihrer Anwesenheit oder Abwesenheit von CLI Optionen initialisieren
$withData = $getopt->getOption('w');
$env = $getopt->getOption('e');
defined('APPLICATION_ENV')
|| define('APPLICATION_ENV', (null === $env) ? 'development' : $env);
// Zend_Application initialisieren
$application = new Zend_Application(
APPLICATION_ENV,
APPLICATION_PATH . '/configs/application.ini'
);
// Die DB Ressource initialisieren und empfangen
$bootstrap = $application->getBootstrap();
$bootstrap->bootstrap('db');
$dbAdapter = $bootstrap->getResource('db');
// Den Benutzer informieren was abgeht
// (wir erstellen hier aktuell eine Datenbank)
if ('testing' != APPLICATION_ENV) {
echo 'Schreiben in die Guestbook Datenbank (control-c um abzubrechen): ' . PHP_EOL;
for ($x = 5; $x > 0; $x--) {
echo $x . "\r"; sleep(1);
}
}
// Prüfen um zu sehen ob wie bereits eine Datenbankdatei haben
$options = $bootstrap->getOption('resources');
$dbFile = $options['db']['params']['dbname'];
if (file_exists($dbFile)) {
unlink($dbFile);
}
// Dieser Block führt die aktuellen Statements aus welche von der Schemadatei
// geladen werden.
try {
$schemaSql = file_get_contents(dirname(__FILE__) . '/schema.sqlite.sql');
// Die Verbindung direkt verwenden um SQL im Block zu laden
$dbAdapter->getConnection()->exec($schemaSql);
chmod($dbFile, 0666);
if ('testing' != APPLICATION_ENV) {
echo PHP_EOL;
echo 'Datenbank erstellt';
echo PHP_EOL;
}
if ($withData) {
$dataSql = file_get_contents(dirname(__FILE__) . '/data.sqlite.sql');
// Die Verbindung direkt verwenden um SQL in Blöcken zu laden
$dbAdapter->getConnection()->exec($dataSql);
if ('testing' != APPLICATION_ENV) {
echo 'Daten geladen.';
echo PHP_EOL;
}
}
} catch (Exception $e) {
echo 'EIN FEHLER IST AUFGETRETEN:' . PHP_EOL;
echo $e->getMessage() . PHP_EOL;
return false;
}
// Generell gesprochen wird dieses Skript von der Kommandozeile aus aufgerufen
return true;
]]>
Jetzt führen wir dieses Skript aus. Von einem Terminal oder der DOS Kommandozeile ist das
folgende zu tun:
Man sollte eine ähnliche Ausgabe wie folgt sehen:
Jetzt haben wir eine voll funktionsfähige Datenbank und eine Tabelle für unsere Guestbook
Anwendung. Unsere nächsten paar Schritte sind die Ausarbeitung unseres Anwendungscodes. Das
inkludiert das Bauen einer Datenquelle (in unserem Fall verwenden wir
Zend_Db_Table), und einen Daten Mapper um diese Datenquelle mit
unserem Domain Modell zu verbinden. Letztendlich erstellen wir den Controller der mit diesem
Modell interagiert damit sowohl existierende Einträge angezeigt als auch neue Einträge
bearbeitet werden.
Wir verwenden ein Table Data
Gateway um uns mit unserer Datenquelle zu verbinden;
Zend_Db_Table bietet diese Funktionalität. Um anzufangen erstellen wir
eine Zend_Db_Table-basierende Tabellenklasse. Zuerst erstellen wir
das Verzeichnis application/models/DbTable/. Dann erstellen und
bearbeiten wir die Datei Guestbook.php in Ihm und fügen die folgenden
Inhalte ein:
Der Klassenpräfix ist zu beachten: Default_Model_DbTable. Der
Klassenpräfix "Default" von unserem Autoloader ist das erste Segment, und dann haben wir die
Komponente "Model_DbTable"; die letztere verweist auf das Verzeichnis
models/DbTable/ des Moduls.
Alles das ist wirklich notwendig wenn Zend_Db_Table erweitert wird
um einen Tabellennamen anzubieten und optional den primären Schlüssel (wenn es nicht die
"id" ist).
Jetzt erstellen wir einen Daten Mapper. Ein
Daten Mapper bildet ein Domain Objekt in der Datenbank ab. In unserem
Fall bildet es unser Modell Default_Model_Guestbook auf unsere
Datenquelle, Default_Model_DbTable_Guestbook, ab. Eine typische API
für einen Daten Mapper ist wie folgt:
Zusätzlich zu diesen Methoden, fügen wir Methoden für das Setzen und Holen des Table Data
Gateways hinzu. Die endgültige Klasse, welche unter
application/models/GuestbookMapper.php platziert ist, sieht wie folgt
aus:
_dbTable = $dbTable;
return $this;
}
public function getDbTable()
{
if (null === $this->_dbTable) {
$this->setDbTable('Default_Model_DbTable_Guestbook');
}
return $this->_dbTable;
}
public function save(Default_Model_Guestbook $guestbook)
{
$data = array(
'email' => $guestbook->getEmail(),
'comment' => $guestbook->getComment(),
'created' => date('Y-m-d H:i:s'),
);
if (null === ($id = $guestbook->getId())) {
unset($data['id']);
$this->getDbTable()->insert($data);
} else {
$this->getDbTable()->update($data, array('id = ?' => $id));
}
}
public function find($id, Default_Model_Guestbook $guestbook)
{
$result = $this->getDbTable()->find($id);
if (0 == count($result)) {
return;
}
$row = $result->current();
$guestbook->setId($row->id)
->setEmail($row->email)
->setComment($row->comment)
->setCreated($row->created);
}
public function fetchAll()
{
$resultSet = $this->getDbTable()->fetchAll();
$entries = array();
foreach ($resultSet as $row) {
$entry = new Default_Model_Guestbook();
$entry->setId($row->id)
->setEmail($row->email)
->setComment($row->comment)
->setCreated($row->created)
->setMapper($this);
$entries[] = $entry;
}
return $entries;
}
}
]]>
Jetzt ist es Zeit unsere Modellklasse leicht zu aktualisieren, um den Daten Mapper
aufzunehmen. So wie der Daten Mapper eine Referenz zur Datenquelle enthält, enthält das
Modell eine Referenz zum Daten Mapper. Zusätzlich machen wir es einfach das Modell
bekanntzumachen indem ein Array an Daten entweder an den Constructor oder an die
setOptions() Methode übergeben wird. Das endgültige Modell, welches
in application/models/Guestbook.php ist, sieht wie folgt aus:
setOptions($options);
}
}
public function __set($name, $value)
{
$method = 'set' . $name;
if (('mapper' == $name) || !method_exists($this, $method)) {
throw new Exception('Ungültige Guestbook Eigenschaft');
}
$this->$method($value);
}
public function __get($name)
{
$method = 'get' . $name;
if (('mapper' == $name) || !method_exists($this, $method)) {
throw new Exception('Ungültige Guestbook Eigenschaft');
}
return $this->$method();
}
public function setOptions(array $options)
{
$methods = get_class_methods($this);
foreach ($options as $key => $value) {
$method = 'set' . ucfirst($key);
if (in_array($method, $methods)) {
$this->$method($value);
}
}
return $this;
}
public function setComment($text)
{
$this->_comment = (string) $text;
return $this;
}
public function getComment()
{
return $this->_comment;
}
public function setEmail($email)
{
$this->_email = (string) $email;
return $this;
}
public function getEmail()
{
return $this->_email;
}
public function setCreated($ts)
{
$this->_created = $ts;
return $this;
}
public function getCreated()
{
return $this->_created;
}
public function setId($id)
{
$this->_id = (int) $id;
return $this;
}
public function getId()
{
return $this->_id;
}
public function setMapper($mapper)
{
$this->_mapper = $mapper;
return $this;
}
public function getMapper()
{
if (null === $this->_mapper) {
$this->setMapper(new Default_Model_GuestbookMapper());
}
return $this->_mapper;
}
public function save()
{
$this->getMapper()->save($this);
}
public function find($id)
{
$this->getMapper()->find($id, $this);
return $this;
}
public function fetchAll()
{
return $this->getMapper()->fetchAll();
}
}
]]>
Letztendlich, um diese Elemente alle zusammen zu verbinden, erstellen wir einen Guestbook
Controller der die Einträge auflistet welche aktuell in der Datenbank sind.
Um einen neuen Controller zu erstellen muss ein Terminal oder eine DOS Konsole geöffnet,
in das Projektverzeichnis navigiert und das folgende eingegeben werden:
zf.bat create controller guestbook
]]>
Das erstellt einen neuen Controller, GuestbookController, in
application/controllers/GuestbookController.php mit einer einzelnen
Aktions Methode, indexAction(). Er erstellt auch ein View Skript
Verzeichnis für den Controller, application/views/scripts/guestbook/,
mit einem View Skript für die Index Aktion.
Wir verwenden die "index" Aktion als Landeseite um alle Guestbook Einträge anzusehen.
Jetzt betrachten wir die grundsätzliche Anwendungslogik. Bei einem Treffer auf
indexAction() zeigen wir alle Guestbook Einträge an. Das würde wie
folgt aussehen:
view->entries = $guestbook->fetchAll();
}
}
]]>
Und natürlich benötigen wir ein View Skript um damit weiterzumachen.
application/views/scripts/guestbook/index.phtml ist zu bearbeiten damit
Sie wie folgt aussieht:
Im Guestbook eintragen
Guestbook Einträge:
entries as $entry): ?>
- escape($entry->email) ?>
- escape($entry->comment) ?>
]]>
Checkpoint
Jetzt gehen wir auf "http://localhost/guestbook". Man sollte das folgende im Browser
sehen:
Das Datenlade Skript verwenden
Das Datenlade Skript welches in diesem Kapitel beschrieben wird
(scripts/load.sqlite.php) kann verwendet werden um die Datenbank,
für jede Umgebung die man definiert hat, zu erstellen sowie Sie mit Beispieldaten zu
laden. Intern verwendet es Zend_Console_Getopt, was es erlaubt
eine Anzahl von Kommandozeilen Schalter anzubieten. Wenn man den "-h" oder "--help"
Schalter übergibt, werden die folgenden Optionen angegeben:
Der "-e" Schalter erlaubt es den Wert zu spezifizieren der für die Konstante
APPLICATION_ENV verwendet wird -- welcher es erlaubt eine SQLite
Datenbank für jede Umgebung zu erstellen die man definiert. Man sollte sicherstellen
dass das Skript für die Umgebung gestartet wird welche man für die eigene Anwendung
ausgewählt hat wenn man in Betrieb geht.