quickstart-create-model.xml 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- EN-Revision: 21819 -->
  3. <!-- Reviewed: 21819 -->
  4. <sect1 id="learning.quickstart.create-model">
  5. <title>Ein Modell und eine Datenbanktabelle erstellen</title>
  6. <para>
  7. Bevor wir anfangen, wollen wir uns überlegen: Wo werden diese Klassen leben, und wie werden wir
  8. sie finden? Das Standardprojekt, welches wir erstellt haben, instanziert einen Autoloader. Wir
  9. können ihm andere Autoloader anhängen, damit er weiss, wo andere Klassen zu finden sind.
  10. Typischerweise wollen wir, dass unsere verschiedenen MVC Klassen im selben Baum gruppiert sind
  11. -- in diesem Fall <filename>application/</filename> -- und meistens ein gemeinsames Präfix
  12. verwenden.
  13. </para>
  14. <para>
  15. <classname>Zend_Controller_Front</classname> kennt den Begriff von "Modulen", welche
  16. individuelle Mini-Anwendungen sind. Module mimen die Verzeichnisstruktur, welche das
  17. <command>zf</command>-Tool unter <filename>application/</filename> einrichtet, und von allen
  18. Klassen darin wird angenommen, dass sie mit einen gemeinsamen Präfix beginnen, dem Namen des
  19. Moduls. <filename>application/</filename> selbst ist ein Modul -- das "default" oder
  20. "application" Modul. Als solches richten wir das Autoloading für Ressourcen in diesem
  21. Verzeichnis ein.
  22. </para>
  23. <para>
  24. <classname>Zend_Application_Module_Autoloader</classname> bietet die Funktionalität, welche
  25. benötigt wird, um die verschiedenen Ressourcen unter einem Modul mit den richtigen
  26. Verzeichnissen zu verbinden, und auch einen standardmäßigen Namensmechanismus. Standardmäßig
  27. wird eine Instanz der Klasse wärend der Initialisierung des Bootstrap-Objekts erstellt;
  28. unser Application Bootstrap verwendet standardmäßig das Modulpräfix "Application". Daher
  29. beginnen alle unsere Modelle, Formulare, und Tabellenklassen mit dem Klassenpräfix
  30. "Application_".
  31. </para>
  32. <para>
  33. Überlegen wir uns nun, was ein Guestbook ausmacht. Typischerweise ist das einfach eine
  34. Liste von Einträgen mit einem <emphasis>Kommentar</emphasis> (comment), einem
  35. <emphasis>Zeitpunkt</emphasis> (timestamp) und oft einer <emphasis>Email-Adresse</emphasis>.
  36. Angenommen wir speichern diese in einer Datenbank, dann wollen wir auch einen
  37. <emphasis>eindeutigen Identifikator</emphasis> für jeden Eintrag. Wir wollen in der Lage
  38. sein, einen Eintrag zu speichern, individuelle Einträge zu holen, und alle Einträge zu
  39. empfangen. Als solches könnte das Modell einer einfachen Guestbook-<acronym>API</acronym>
  40. wie folgt aussehen:
  41. </para>
  42. <programlisting language="php"><![CDATA[
  43. // application/models/Guestbook.php
  44. class Application_Model_Guestbook
  45. {
  46. protected $_comment;
  47. protected $_created;
  48. protected $_email;
  49. protected $_id;
  50. public function __set($name, $value);
  51. public function __get($name);
  52. public function setComment($text);
  53. public function getComment();
  54. public function setEmail($email);
  55. public function getEmail();
  56. public function setCreated($ts);
  57. public function getCreated();
  58. public function setId($id);
  59. public function getId();
  60. }
  61. class Application_Model_GuestbookMapper
  62. {
  63. public function save(Application_Model_Guestbook $guestbook);
  64. public function find($id);
  65. public function fetchAll();
  66. }
  67. ]]></programlisting>
  68. <para>
  69. <methodname>__get()</methodname> und <methodname>__set()</methodname> bieten uns bequeme
  70. Mechanismen an um auf individuelle Eigenschaften von Einträgen zuzugreifen und auf andere
  71. Getter und Setter zu verweisen. Sie stellen auch sicher, dass nur Eigenschaften im Objekt
  72. vorhanden sind, die wir freigegeben haben.
  73. </para>
  74. <para>
  75. <methodname>find()</methodname> und <methodname>fetchAll()</methodname> bieten die Fähigkeit,
  76. einen einzelnen Eintrag oder alle Einträge zu holen, wärend <methodname>save()</methodname>
  77. das Speichern der Einträge im Datenspeicher übernimmt.
  78. </para>
  79. <para>
  80. Von hier an können wir über die Einrichtung unserer Datenbank nachdenken.
  81. </para>
  82. <para>
  83. Zuerst muss unsere <classname>Db</classname>-Ressource initialisiert werden. Wie bei
  84. <classname>Layout</classname> und <classname>View</classname> kann die Konfiguration für die
  85. <classname>Db</classname>-Ressource angegeben werden. Dies kann mit dem Befehl
  86. <command>zf configure db-adapter</command> getan werden:
  87. </para>
  88. <programlisting language="shell"><![CDATA[
  89. % zf configure db-adapter \
  90. > 'adapter=PDO_SQLITE&dbname=APPLICATION_PATH "/../data/db/guestbook.db"' \
  91. > production
  92. A db configuration for the production has been written to the application config file.
  93. % zf configure db-adapter \
  94. > 'adapter=PDO_SQLITE&dbname=APPLICATION_PATH "/../data/db/guestbook-testing.db"' \
  95. > testing
  96. A db configuration for the production has been written to the application config file.
  97. % zf configure db-adapter \
  98. > 'adapter=PDO_SQLITE&dbname=APPLICATION_PATH "/../data/db/guestbook-dev.db"' \
  99. > development
  100. A db configuration for the production has been written to the application config file.
  101. ]]></programlisting>
  102. <para>
  103. Jetzt muss die Datei <filename>application/configs/application.ini</filename> bearbeitet
  104. werden, und man kann sehen, dass die folgenden Zeilen in den betreffenden Abschnitten
  105. hinzugefügt wurden.
  106. </para>
  107. <programlisting language="ini"><![CDATA[
  108. ; application/configs/application.ini
  109. [production]
  110. ; ...
  111. resources.db.adapter = "PDO_SQLITE"
  112. resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook.db"
  113. [testing : production]
  114. ; ...
  115. resources.db.adapter = "PDO_SQLITE"
  116. resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-testing.db"
  117. [development : production]
  118. ; ...
  119. resources.db.adapter = "PDO_SQLITE"
  120. resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-dev.db"
  121. ]]></programlisting>
  122. <para>
  123. Die endgültige Konfigurationsdatei sollte wie folgt aussehen:
  124. </para>
  125. <programlisting language="ini"><![CDATA[
  126. ; application/configs/application.ini
  127. [production]
  128. phpSettings.display_startup_errors = 0
  129. phpSettings.display_errors = 0
  130. bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
  131. bootstrap.class = "Bootstrap"
  132. appnamespace = "Application"
  133. resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
  134. resources.frontController.params.displayExceptions = 0
  135. resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts"
  136. resources.view[] =
  137. resources.db.adapter = "PDO_SQLITE"
  138. resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook.db"
  139. [staging : production]
  140. [testing : production]
  141. phpSettings.display_startup_errors = 1
  142. phpSettings.display_errors = 1
  143. resources.db.adapter = "PDO_SQLITE"
  144. resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-testing.db"
  145. [development : production]
  146. phpSettings.display_startup_errors = 1
  147. phpSettings.display_errors = 1
  148. resources.db.adapter = "PDO_SQLITE"
  149. resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-dev.db"
  150. ]]></programlisting>
  151. <para>
  152. Es ist zu beachten, dass die Datenbank(en) unter <filename>data/db/</filename> gespeichert
  153. wird. Diese Verzeichnisse sind zu erstellen und weltweit-schreibbar zu machen. Auf
  154. Unix-artigen Systemen kann man das wie folgt durchführen:
  155. </para>
  156. <programlisting language="shell"><![CDATA[
  157. % mkdir -p data/db; chmod -R a+rwX data
  158. ]]></programlisting>
  159. <para>
  160. Unter Windows muss man die Verzeichnisse im Explorer erstellen und die Zugriffsrechte so
  161. setzen, dass jeder in das Verzeichnis schreiben darf.
  162. </para>
  163. <para>
  164. Ab diesem Punkt haben wir eine Verbindung zu einer Datenbank; in unserem Fall ist es eine
  165. Verbindung zu einer Sqlite-Datenbank, die in unserem <filename>application/data/</filename>
  166. Verzeichnis ist. Erstellen wir also eine einfache Tabelle, die unsere Guestbook-Einträge
  167. enthalten wird.
  168. </para>
  169. <programlisting language="sql"><![CDATA[
  170. -- scripts/schema.sqlite.sql
  171. --
  172. -- Man muss das Datenbank Schema mit diesem SQL laden.
  173. CREATE TABLE guestbook (
  174. id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
  175. email VARCHAR(32) NOT NULL DEFAULT 'noemail@test.com',
  176. comment TEXT NULL,
  177. created DATETIME NOT NULL
  178. );
  179. CREATE INDEX "id" ON "guestbook" ("id");
  180. ]]></programlisting>
  181. <para>
  182. Und damit wir gleich einige Arbeitsdaten haben, fügen wir ein paar Zeilen ein,
  183. um unsere Anwendung interessant zu machen.
  184. </para>
  185. <programlisting language="sql"><![CDATA[
  186. -- scripts/data.sqlite.sql
  187. --
  188. -- Man kann damit beginnen die Datenbank zu befüllen, indem die folgenden SQL
  189. -- Anweisungen ausgeführt werden.
  190. INSERT INTO guestbook (email, comment, created) VALUES
  191. ('ralph.schindler@zend.com',
  192. 'Hallo! Hoffentlich geniesst Ihr dieses Beispiel einer ZF Anwendung!
  193. DATETIME('NOW'));
  194. INSERT INTO guestbook (email, comment, created) VALUES
  195. ('foo@bar.com',
  196. 'Baz baz baz, baz baz Baz baz baz - baz baz baz.',
  197. DATETIME('NOW'));
  198. ]]></programlisting>
  199. <para>
  200. Jetzt haben wir sowohl das Schema als auch einige Daten definiert. Schreiben wir also ein
  201. Skript, das wir jetzt ausführen können, um diese Datenbank zu erstellen. Natürlich wird das
  202. nicht in der Produktion benötigt, aber dieses Skriupt hilft Entwicklern die Notwendigkeiten
  203. der Datenbank lokal zu erstellen, damit sie eine voll funktionsfähige Anwendung haben. Das
  204. Skript ist als <filename>scripts/load.sqlite.php</filename> mit dem folgenden Inhalt zu
  205. erstellen:
  206. </para>
  207. <programlisting language="php"><![CDATA[
  208. // scripts/load.sqlite.php
  209. /**
  210. * Skript für das Erstellen und Laden der Datenbank
  211. */
  212. // Initialisiert den Pfad und das Autoloading der Anwendung
  213. defined('APPLICATION_PATH')
  214. || define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));
  215. set_include_path(implode(PATH_SEPARATOR, array(
  216. APPLICATION_PATH . '/../library',
  217. get_include_path(),
  218. )));
  219. require_once 'Zend/Loader/Autoloader.php';
  220. Zend_Loader_Autoloader::getInstance();
  221. // Definiert einige CLI Optionen
  222. $getopt = new Zend_Console_Getopt(array(
  223. 'withdata|w' => 'Datenbank mit einigen Daten laden',
  224. 'env|e-s' => "Anwendungsumgebung für welche die Datenbank "
  225. . "erstellt wird (Standard ist Development)",
  226. 'help|h' => 'Hilfe -- Verwendung',
  227. ));
  228. try {
  229. $getopt->parse();
  230. } catch (Zend_Console_Getopt_Exception $e) {
  231. // Schlechte Option übergeben: Verwendung ausgeben
  232. echo $e->getUsageMessage();
  233. return false;
  234. }
  235. // Wenn Hilfe angefragt wurde, Verwendung ausgeben
  236. if ($getopt->getOption('h')) {
  237. echo $getopt->getUsageMessage();
  238. return true;
  239. }
  240. // Werte basierend auf ihrer Anwesenheit oder Abwesenheit von CLI Optionen initialisieren
  241. $withData = $getopt->getOption('w');
  242. $env = $getopt->getOption('e');
  243. defined('APPLICATION_ENV')
  244. || define('APPLICATION_ENV', (null === $env) ? 'development' : $env);
  245. // Zend_Application initialisieren
  246. $application = new Zend_Application(
  247. APPLICATION_ENV,
  248. APPLICATION_PATH . '/configs/application.ini'
  249. );
  250. // Die DB Ressource initialisieren und empfangen
  251. $bootstrap = $application->getBootstrap();
  252. $bootstrap->bootstrap('db');
  253. $dbAdapter = $bootstrap->getResource('db');
  254. // Den Benutzer informieren was abgeht
  255. // (wir erstellen hier aktuell eine Datenbank)
  256. if ('testing' != APPLICATION_ENV) {
  257. echo 'Schreiben in die Guestbook Datenbank (control-c um abzubrechen): ' . PHP_EOL;
  258. for ($x = 5; $x > 0; $x--) {
  259. echo $x . "\r"; sleep(1);
  260. }
  261. }
  262. // Prüfen um zu sehen ob wir bereits eine Datenbankdatei haben
  263. $options = $bootstrap->getOption('resources');
  264. $dbFile = $options['db']['params']['dbname'];
  265. if (file_exists($dbFile)) {
  266. unlink($dbFile);
  267. }
  268. // Dieser Block führt die aktuellen Statements aus welche von der Schemadatei
  269. // geladen werden.
  270. try {
  271. $schemaSql = file_get_contents(dirname(__FILE__) . '/schema.sqlite.sql');
  272. // Die Verbindung direkt verwenden um SQL im Block zu laden
  273. $dbAdapter->getConnection()->exec($schemaSql);
  274. chmod($dbFile, 0666);
  275. if ('testing' != APPLICATION_ENV) {
  276. echo PHP_EOL;
  277. echo 'Datenbank erstellt';
  278. echo PHP_EOL;
  279. }
  280. if ($withData) {
  281. $dataSql = file_get_contents(dirname(__FILE__) . '/data.sqlite.sql');
  282. // Die Verbindung direkt verwenden um SQL in Blöcken zu laden
  283. $dbAdapter->getConnection()->exec($dataSql);
  284. if ('testing' != APPLICATION_ENV) {
  285. echo 'Daten geladen.';
  286. echo PHP_EOL;
  287. }
  288. }
  289. } catch (Exception $e) {
  290. echo 'EIN FEHLER IST AUFGETRETEN:' . PHP_EOL;
  291. echo $e->getMessage() . PHP_EOL;
  292. return false;
  293. }
  294. // dieses Skript von der Kommandozeile aus aufgerufen
  295. return true;
  296. ]]></programlisting>
  297. <para>
  298. Jetzt führen wir dieses Skript aus. Von einem Terminal oder der DOS Kommandozeile ist das
  299. folgende zu tun:
  300. </para>
  301. <programlisting language="shell"><![CDATA[
  302. % php scripts/load.sqlite.php --withdata
  303. ]]></programlisting>
  304. <para>
  305. Man sollte eine ähnliche Ausgabe wie folgt sehen:
  306. </para>
  307. <programlisting language="text"><![CDATA[
  308. path/to/ZendFrameworkQuickstart/scripts$ php load.sqlite.php --withdata
  309. Schreiben in die Guestbook Datenbank (control-c um abzubrechen):
  310. 1
  311. Datenbank erstellt
  312. Daten geladen.
  313. ]]></programlisting>
  314. <para>
  315. Jetzt haben wir eine voll funktionsfähige Datenbank und eine Tabelle für unsere Guestbook
  316. Anwendung. Unsere nächsten paar Schritte sind die Ausarbeitung unseres Anwendungscodes. Das
  317. schliesst das Bauen einer Datenquelle (in unserem Fall verwenden wir
  318. <classname>Zend_Db_Table</classname>), und einen Daten Mapper um diese Datenquelle mit
  319. unserem Domain Modell zu verbinden, ein. Letztendlich erstellen wir den Controller, der mit diesem
  320. Modell interagiert, damit sowohl existierende Einträge angezeigt als auch neue Einträge
  321. bearbeitet werden.
  322. </para>
  323. <para>
  324. Wir verwenden ein <ulink
  325. url="http://martinfowler.com/eaaCatalog/tableDataGateway.html">Table Data
  326. Gateway</ulink>, um uns mit unserer Datenquelle zu verbinden;
  327. <classname>Zend_Db_Table</classname> bietet diese Funktionalität. Um anzufangen erstellen
  328. wir eine <classname>Zend_Db_Table</classname>-basierende Tabellenklasse. Wie wir es für
  329. Layouts und den Datenbankadapter getan haben, können wir das <command>zf</command> Tool
  330. verwenden um uns zu assistieren, indem der Befehl <command>create db-table</command>
  331. verwendet wird. Dieser benötigt mindestens zwei Argumente, den Namen, mit dem man auf die
  332. Klasse referenzieren will, und die Datenbanktabelle auf die sie zeigt.
  333. </para>
  334. <programlisting language="shell"><![CDATA[
  335. % zf create db-table Guestbook guestbook
  336. Creating a DbTable at application/models/DbTable/Guestbook.php
  337. Updating project profile 'zfproject.xml'
  338. ]]></programlisting>
  339. <para>
  340. Wenn man den Verzeichnisbaum ansieht, dann wird man feststellen, dass ein neues Verzeichnis
  341. <filename>application/models/DbTable/</filename> zusammen mit der Datei
  342. <filename>Guestbook.php</filename> erstellt wurde. Wenn man die Datei öffnet, wird man den
  343. folgenden Inhalt sehen:
  344. </para>
  345. <programlisting language="php"><![CDATA[
  346. // application/models/DbTable/Guestbook.php
  347. /**
  348. * Das ist die DbTable Klasse für die Guestbook Tabelle.
  349. */
  350. class Application_Model_DbTable_Guestbook extends Zend_Db_Table_Abstract
  351. {
  352. /** Tabellenname */
  353. protected $_name = 'guestbook';
  354. }
  355. ]]></programlisting>
  356. <para>
  357. Das Klassenpräfix ist zu beachten: <classname>Application_Model_DbTable</classname>. Das
  358. Klassenpräfix für unser Modul "Application" ist das erste Segment, und dann haben wir die
  359. Komponente "Model_DbTable"; die letztere verweist auf das Verzeichnis
  360. <filename>models/DbTable/</filename> des Moduls.
  361. </para>
  362. <para>
  363. Alles das ist wirklich notwendig, wenn <classname>Zend_Db_Table</classname> erweitert wird
  364. um einen Tabellennamen anzubieten und optional den primären Schlüssel (wenn es nicht die
  365. "id" ist).
  366. </para>
  367. <para>
  368. Jetzt erstellen wir einen <ulink
  369. url="http://martinfowler.com/eaaCatalog/dataMapper.html">Data Mapper</ulink>. Ein
  370. <emphasis>Data Mapper</emphasis> bildet ein Domain Objekt in der Datenbank ab. In unserem
  371. Fall bildet es unser Modell <classname>Application_Model_Guestbook</classname> auf unsere
  372. Datenquelle, <classname>Application_Model_DbTable_Guestbook</classname>, ab. Eine typische
  373. <acronym>API</acronym> für einen Data Mapper ist wie folgt:
  374. </para>
  375. <programlisting language="php"><![CDATA[
  376. // application/models/GuestbookMapper.php
  377. class Application_Model_GuestbookMapper
  378. {
  379. public function save($model);
  380. public function find($id, $model);
  381. public function fetchAll();
  382. }
  383. ]]></programlisting>
  384. <para>
  385. Zusätzlich zu diesen Methoden fügen wir Methoden für das Setzen und Holen des Table Data
  386. Gateways hinzu. Um die initiale Klasse zu erstellen, kann das <command>zf</command> CLI
  387. Tool verwendet werden:
  388. </para>
  389. <programlisting language="shell"><![CDATA[
  390. % zf create model GuestbookMapper
  391. Creating a model at application/models/GuestbookMapper.php
  392. Updating project profile '.zfproject.xml'
  393. ]]></programlisting>
  394. <para>
  395. Jetzt muss die Klasse <classname>Application_Model_GuestbookMapper</classname>, welche in
  396. <filename>application/models/GuestbookMapper.php</filename> zu finden ist, so
  397. geändert werden, dass sie wie folgt aussieht:
  398. </para>
  399. <programlisting language="php"><![CDATA[
  400. // application/models/GuestbookMapper.php
  401. class Application_Model_GuestbookMapper
  402. {
  403. protected $_dbTable;
  404. public function setDbTable($dbTable)
  405. {
  406. if (is_string($dbTable)) {
  407. $dbTable = new $dbTable();
  408. }
  409. if (!$dbTable instanceof Zend_Db_Table_Abstract) {
  410. throw new Exception('Ungültiges Table Data Gateway angegeben');
  411. }
  412. $this->_dbTable = $dbTable;
  413. return $this;
  414. }
  415. public function getDbTable()
  416. {
  417. if (null === $this->_dbTable) {
  418. $this->setDbTable('Application_Model_DbTable_Guestbook');
  419. }
  420. return $this->_dbTable;
  421. }
  422. public function save(Application_Model_Guestbook $guestbook)
  423. {
  424. $data = array(
  425. 'email' => $guestbook->getEmail(),
  426. 'comment' => $guestbook->getComment(),
  427. 'created' => date('Y-m-d H:i:s'),
  428. );
  429. if (null === ($id = $guestbook->getId())) {
  430. unset($data['id']);
  431. $this->getDbTable()->insert($data);
  432. } else {
  433. $this->getDbTable()->update($data, array('id = ?' => $id));
  434. }
  435. }
  436. public function find($id, Application_Model_Guestbook $guestbook)
  437. {
  438. $result = $this->getDbTable()->find($id);
  439. if (0 == count($result)) {
  440. return;
  441. }
  442. $row = $result->current();
  443. $guestbook->setId($row->id)
  444. ->setEmail($row->email)
  445. ->setComment($row->comment)
  446. ->setCreated($row->created);
  447. }
  448. public function fetchAll()
  449. {
  450. $resultSet = $this->getDbTable()->fetchAll();
  451. $entries = array();
  452. foreach ($resultSet as $row) {
  453. $entry = new Application_Model_Guestbook();
  454. $entry->setId($row->id)
  455. ->setEmail($row->email)
  456. ->setComment($row->comment)
  457. ->setCreated($row->created);
  458. $entries[] = $entry;
  459. }
  460. return $entries;
  461. }
  462. }
  463. ]]></programlisting>
  464. <para>
  465. Jetzt ist es Zeit unsere Modellklasse zu erstellen. Wir machen dies, indem wieder das
  466. Kommando <command>zf create model</command> verwendet wird:
  467. </para>
  468. <programlisting language="shell"><![CDATA[
  469. % zf create model Guestbook
  470. Creating a model at application/models/Guestbook.php
  471. Updating project profile '.zfproject.xml'
  472. ]]></programlisting>
  473. <para>
  474. Wir verändern diese leere <acronym>PHP</acronym>-Klasse, um das Model einfach mit Daten
  475. zu füllen, indem ein Array an Daten entweder an den Constructor oder an die
  476. Methode <methodname>setOptions()</methodname> übergeben wird. Das endgültige Modell, welches
  477. in <filename>application/models/Guestbook.php</filename> ist, sollte wie folgt aussehen:
  478. </para>
  479. <programlisting language="php"><![CDATA[
  480. // application/models/Guestbook.php
  481. class Application_Model_Guestbook
  482. {
  483. protected $_comment;
  484. protected $_created;
  485. protected $_email;
  486. protected $_id;
  487. public function __construct(array $options = null)
  488. {
  489. if (is_array($options)) {
  490. $this->setOptions($options);
  491. }
  492. }
  493. public function __set($name, $value)
  494. {
  495. $method = 'set' . $name;
  496. if (('mapper' == $name) || !method_exists($this, $method)) {
  497. throw new Exception('Ungültige Guestbook Eigenschaft');
  498. }
  499. $this->$method($value);
  500. }
  501. public function __get($name)
  502. {
  503. $method = 'get' . $name;
  504. if (('mapper' == $name) || !method_exists($this, $method)) {
  505. throw new Exception('Ungültige Guestbook Eigenschaft');
  506. }
  507. return $this->$method();
  508. }
  509. public function setOptions(array $options)
  510. {
  511. $methods = get_class_methods($this);
  512. foreach ($options as $key => $value) {
  513. $method = 'set' . ucfirst($key);
  514. if (in_array($method, $methods)) {
  515. $this->$method($value);
  516. }
  517. }
  518. return $this;
  519. }
  520. public function setComment($text)
  521. {
  522. $this->_comment = (string) $text;
  523. return $this;
  524. }
  525. public function getComment()
  526. {
  527. return $this->_comment;
  528. }
  529. public function setEmail($email)
  530. {
  531. $this->_email = (string) $email;
  532. return $this;
  533. }
  534. public function getEmail()
  535. {
  536. return $this->_email;
  537. }
  538. public function setCreated($ts)
  539. {
  540. $this->_created = $ts;
  541. return $this;
  542. }
  543. public function getCreated()
  544. {
  545. return $this->_created;
  546. }
  547. public function setId($id)
  548. {
  549. $this->_id = (int) $id;
  550. return $this;
  551. }
  552. public function getId()
  553. {
  554. return $this->_id;
  555. }
  556. }
  557. ]]></programlisting>
  558. <para>
  559. Um diese Elemente alle zusammen zu verbinden, erstellen wir zuletzt einen
  560. Guestbook Controller, der die Einträge auflistet, welche aktuell in der Datenbank sind.
  561. </para>
  562. <para>
  563. Um einen neuen Controller zu erstellen, muss das Kommando
  564. <command>zf create controller</command> verwendet werden:
  565. </para>
  566. <programlisting language="shell"><![CDATA[
  567. % zf create controller Guestbook
  568. Creating a controller at
  569. application/controllers/GuestbookController.php
  570. Creating an index action method in controller Guestbook
  571. Creating a view script for the index action method at
  572. application/views/scripts/guestbook/index.phtml
  573. Creating a controller test file at
  574. tests/application/controllers/GuestbookControllerTest.php
  575. Updating project profile '.zfproject.xml'
  576. ]]></programlisting>
  577. <para>
  578. Das erstellt einen neuen Controller, <classname>GuestbookController</classname>, in
  579. <filename>application/controllers/GuestbookController.php</filename> mit einer einzelnen
  580. Aktions Methode, <methodname>indexAction()</methodname>. Er erstellt auch ein View Skript
  581. Verzeichnis für den Controller, <filename>application/views/scripts/guestbook/</filename>,
  582. mit einem View Skript für die Index Aktion.
  583. </para>
  584. <para>
  585. Wir verwenden die "index" Aktion als Landeseite, um alle Guestbook Einträge anzusehen.
  586. </para>
  587. <para>
  588. Jetzt betrachten wir die grundsätzliche Anwendungslogik. Bei einem Treffer auf
  589. <methodname>indexAction()</methodname> zeigen wir alle Guestbook Einträge an. Das würde wie
  590. folgt aussehen:
  591. </para>
  592. <programlisting language="php"><![CDATA[
  593. // application/controllers/GuestbookController.php
  594. class GuestbookController extends Zend_Controller_Action
  595. {
  596. public function indexAction()
  597. {
  598. $guestbook = new Application_Model_GuestbookMapper();
  599. $this->view->entries = $guestbook->fetchAll();
  600. }
  601. }
  602. ]]></programlisting>
  603. <para>
  604. Und natürlich benötigen wir ein View Skript um damit weiterzumachen.
  605. <filename>application/views/scripts/guestbook/index.phtml</filename> ist zu bearbeiten damit
  606. sie wie folgt aussieht:
  607. </para>
  608. <programlisting language="php"><![CDATA[
  609. <!-- application/views/scripts/guestbook/index.phtml -->
  610. <p><a href="<?php echo $this->url(
  611. array(
  612. 'controller' => 'guestbook',
  613. 'action' => 'sign'
  614. ),
  615. 'default',
  616. true) ?>">Im Guestbook eintragen</a></p>
  617. Guestbook Einträge: <br />
  618. <dl>
  619. <?php foreach ($this->entries as $entry): ?>
  620. <dt><?php echo $this->escape($entry->email) ?></dt>
  621. <dd><?php echo $this->escape($entry->comment) ?></dd>
  622. <?php endforeach ?>
  623. </dl>
  624. ]]></programlisting>
  625. <note>
  626. <title>Checkpoint</title>
  627. <para>
  628. Jetzt gehen wir auf "http://localhost/guestbook". Man sollte das folgende im Browser
  629. sehen:
  630. </para>
  631. <para>
  632. <inlinegraphic width="525" scale="100" align="center" valign="middle"
  633. fileref="figures/learning.quickstart.create-model.png" format="PNG" />
  634. </para>
  635. </note>
  636. <note>
  637. <title>Das Datenlade-Skript verwenden</title>
  638. <para>
  639. Das Datenlade-Skript, welches in diesem Kapitel beschrieben wird
  640. (<filename>scripts/load.sqlite.php</filename>) kann verwendet werden, um die Datenbank
  641. für jede Umgebung zu erstellen, die man definiert hat, sowie sie mit Beispieldaten zu
  642. laden. Intern verwendet es <classname>Zend_Console_Getopt</classname>, was es erlaubt,
  643. eine Anzahl von Kommandozeilenschalter anzugeben. Wenn man den "-h" oder "--help"
  644. Schalter übergibt, werden die folgenden Optionen angegeben:
  645. </para>
  646. <programlisting language="php"><![CDATA[
  647. Usage: load.sqlite.php [ options ]
  648. --withdata|-w Datenbank mit einigen Daten laden
  649. --env|-e [ ] Anwendungsumgebung für welche die Datenbank erstellt wird
  650. (Standard ist Development)
  651. --help|-h Hilfe -- Verwendung)]]
  652. ]]></programlisting>
  653. <para>
  654. Der "-e" Schalter erlaubt es, den Wert anzugeben, der für die Konstante
  655. <constant>APPLICATION_ENV</constant> verwendet wird -- welcher es erlaubt, eine SQLite
  656. Datenbank für jede Umgebung zu erstellen, die man definiert. Man sollte sicherstellen,
  657. dass das Skript für die Umgebung gestartet wird, welche man für die eigene Anwendung
  658. ausgewählt hat, wenn man in Betrieb geht.
  659. </para>
  660. </note>
  661. </sect1>