2
0

quickstart-create-model.xml 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- EN-Revision: 19766 -->
  3. <!-- Reviewed: no -->
  4. <sect1 id="learning.quickstart.create-model">
  5. <title>Create a Model and Database Table</title>
  6. <para>
  7. Before we get started, let's consider something: where will these classes live, and how will
  8. we find them? The default project we created instantiates an autoloader. We can attach other
  9. autoloaders to it so that it knows where to find different classes. Typically, we want our
  10. various MVC classes grouped under the same tree -- in this case,
  11. <filename>application/</filename> -- and most often using a common prefix.
  12. </para>
  13. <para>
  14. <classname>Zend_Controller_Front</classname> has a notion of "modules", which are individual
  15. mini-applications. Modules mimic the directory structure that the <command>zf</command>
  16. tool sets up under <filename>application/</filename>, and all classes inside them are
  17. assumed to begin with a common prefix, the module name. <filename>application/</filename>
  18. is itself a module -- the "default" module. As such, let's setup autoloading for resources
  19. within this directory, giving them a prefix of "Default". We can do this by creating another
  20. bootstrap resource.
  21. </para>
  22. <para>
  23. <classname>Zend_Application_Module_Autoloader</classname> provides the functionality needed
  24. to map the various resources under a module to the appropriate directories, and provides a
  25. standard naming mechanism as well. In our bootstrap resource, we'll instantiate this, and be
  26. done. The method looks like this:
  27. </para>
  28. <programlisting language="php"><![CDATA[
  29. // application/Bootstrap.php
  30. // Add this method to the Bootstrap class:
  31. protected function _initAutoload()
  32. {
  33. $autoloader = new Zend_Application_Module_Autoloader(array(
  34. 'namespace' => 'Default_',
  35. 'basePath' => dirname(__FILE__),
  36. ));
  37. return $autoloader;
  38. }
  39. ]]></programlisting>
  40. <para>
  41. The final bootstrap class will look as follows:
  42. </para>
  43. <programlisting language="php"><![CDATA[
  44. // application/Bootstrap.php
  45. class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
  46. {
  47. protected function _initAutoload()
  48. {
  49. $autoloader = new Zend_Application_Module_Autoloader(array(
  50. 'namespace' => 'Default',
  51. 'basePath' => dirname(__FILE__),
  52. ));
  53. return $autoloader;
  54. }
  55. protected function _initDoctype()
  56. {
  57. $this->bootstrap('view');
  58. $view = $this->getResource('view');
  59. $view->doctype('XHTML1_STRICT');
  60. }
  61. }
  62. ]]></programlisting>
  63. <para>
  64. Now, let's consider what makes up a guestbook. Typically, they are simply a list of entries
  65. with a <emphasis>comment</emphasis>, <emphasis>timestamp</emphasis>, and, often,
  66. <emphasis>email address</emphasis>. Assuming we store them in a database, we may also want a
  67. <emphasis>unique identifier</emphasis> for each entry. We'll likely want to be able to save
  68. an entry, fetch individual entries, and retrieve all entries. As such, a simple guestbook
  69. model API might look something like this:
  70. </para>
  71. <programlisting language="php"><![CDATA[
  72. // application/models/Guestbook.php
  73. class Default_Model_Guestbook
  74. {
  75. protected $_comment;
  76. protected $_created;
  77. protected $_email;
  78. protected $_id;
  79. public function __set($name, $value);
  80. public function __get($name);
  81. public function setComment($text);
  82. public function getComment();
  83. public function setEmail($email);
  84. public function getEmail();
  85. public function setCreated($ts);
  86. public function getCreated();
  87. public function setId($id);
  88. public function getId();
  89. public function save();
  90. public function find($id);
  91. public function fetchAll();
  92. }
  93. ]]></programlisting>
  94. <para>
  95. <methodname>__get()</methodname> and <methodname>__set()</methodname> will provide a
  96. convenience mechanism for us to access the individual entry properties, and proxy to the
  97. other getters and setters. They also will help ensure that only properties we whitelist will
  98. be available in the object.
  99. </para>
  100. <para>
  101. <methodname>find()</methodname> and <methodname>fetchAll()</methodname> provide the ability
  102. to fetch a single entry or all entries.
  103. </para>
  104. <para>
  105. Now from here, we can start thinking about setting up our database.
  106. </para>
  107. <para>
  108. First we need to initialize our <classname>Db</classname> resource. As with the
  109. <classname>Layout</classname> and <classname>View</classname> resource, we can provide
  110. configuration for the <classname>Db</classname> resource. In your
  111. <filename>application/configs/application.ini</filename> file, add the following lines in
  112. the appropriate sections.
  113. </para>
  114. <programlisting language="ini"><![CDATA[
  115. ; application/configs/application.ini
  116. ; Add these lines to the appropriate sections:
  117. [production]
  118. resources.db.adapter = "PDO_SQLITE"
  119. resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook.db"
  120. [testing : production]
  121. resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-testing.db"
  122. [development : production]
  123. resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-dev.db"
  124. ]]></programlisting>
  125. <para>
  126. Your final configuration file should look like the following:
  127. </para>
  128. <programlisting language="ini"><![CDATA[
  129. ; application/configs/application.ini
  130. [production]
  131. phpSettings.display_startup_errors = 0
  132. phpSettings.display_errors = 0
  133. bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
  134. bootstrap.class = "Bootstrap"
  135. resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
  136. resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts"
  137. resources.view[] =
  138. resources.db.adapter = "PDO_SQLITE"
  139. resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook.db"
  140. [staging : production]
  141. [testing : production]
  142. phpSettings.display_startup_errors = 1
  143. phpSettings.display_errors = 1
  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.params.dbname = APPLICATION_PATH "/../data/db/guestbook-dev.db"
  149. ]]></programlisting>
  150. <para>
  151. Note that the database(s) will be stored in <filename>data/db/</filename>. Create those
  152. directories, and make them world-writeable. On unix-like systems, you can do that as
  153. follows:
  154. </para>
  155. <programlisting language="shell"><![CDATA[
  156. % mkdir -p data/db; chmod -R a+rwX data
  157. ]]></programlisting>
  158. <para>
  159. On Windows, you will need to create the directories in Explorer and set the permissions to
  160. allow anyone to write to the directory.
  161. </para>
  162. <para>
  163. At this point we have a connection to a database; in our case, its a connection to a Sqlite
  164. database located inside our <filename>application/data/</filename> directory. So, let's
  165. design a simple table that will hold our guestbook entries.
  166. </para>
  167. <programlisting language="sql"><![CDATA[
  168. -- scripts/schema.sqlite.sql
  169. --
  170. -- You will need load your database schema with this SQL.
  171. CREATE TABLE guestbook (
  172. id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
  173. email VARCHAR(32) NOT NULL DEFAULT 'noemail@test.com',
  174. comment TEXT NULL,
  175. created DATETIME NOT NULL
  176. );
  177. CREATE INDEX "id" ON "guestbook" ("id");
  178. ]]></programlisting>
  179. <para>
  180. And, so that we can have some working data out of the box, lets create a few rows of
  181. information to make our application interesting.
  182. </para>
  183. <programlisting language="sql"><![CDATA[
  184. -- scripts/data.sqlite.sql
  185. --
  186. -- You can begin populating the database with the following SQL statements.
  187. INSERT INTO guestbook (email, comment, created) VALUES
  188. ('ralph.schindler@zend.com',
  189. 'Hello! Hope you enjoy this sample zf application!',
  190. DATETIME('NOW'));
  191. INSERT INTO guestbook (email, comment, created) VALUES
  192. ('foo@bar.com',
  193. 'Baz baz baz, baz baz Baz baz baz - baz baz baz.',
  194. DATETIME('NOW'));
  195. ]]></programlisting>
  196. <para>
  197. Now that we have both the schema and some data defined. Lets get a script together that we
  198. can now execute to build this database. Naturally, this is not needed in production, but
  199. this script will help developers build out the database requirements locally so they can
  200. have the fully working application. Create the script as
  201. <filename>scripts/load.sqlite.php</filename> with the following contents:
  202. </para>
  203. <programlisting language="php"><![CDATA[
  204. // scripts/load.sqlite.php
  205. /**
  206. * Script for creating and loading database
  207. */
  208. // Initialize the application path and autoloading
  209. defined('APPLICATION_PATH')
  210. || define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));
  211. set_include_path(implode(PATH_SEPARATOR, array(
  212. APPLICATION_PATH . '/../library',
  213. get_include_path(),
  214. )));
  215. require_once 'Zend/Loader/Autoloader.php';
  216. Zend_Loader_Autoloader::getInstance();
  217. // Define some CLI options
  218. $getopt = new Zend_Console_Getopt(array(
  219. 'withdata|w' => 'Load database with sample data',
  220. 'env|e-s' => 'Application environment for which to create database (defaults to development)',
  221. 'help|h' => 'Help -- usage message',
  222. ));
  223. try {
  224. $getopt->parse();
  225. } catch (Zend_Console_Getopt_Exception $e) {
  226. // Bad options passed: report usage
  227. echo $e->getUsageMessage();
  228. return false;
  229. }
  230. // If help requested, report usage message
  231. if ($getopt->getOption('h')) {
  232. echo $getopt->getUsageMessage();
  233. return true;
  234. }
  235. // Initialize values based on presence or absence of CLI options
  236. $withData = $getopt->getOption('w');
  237. $env = $getopt->getOption('e');
  238. defined('APPLICATION_ENV')
  239. || define('APPLICATION_ENV', (null === $env) ? 'development' : $env);
  240. // Initialize Zend_Application
  241. $application = new Zend_Application(
  242. APPLICATION_ENV,
  243. APPLICATION_PATH . '/configs/application.ini'
  244. );
  245. // Initialize and retrieve DB resource
  246. $bootstrap = $application->getBootstrap();
  247. $bootstrap->bootstrap('db');
  248. $dbAdapter = $bootstrap->getResource('db');
  249. // let the user know whats going on (we are actually creating a
  250. // database here)
  251. if ('testing' != APPLICATION_ENV) {
  252. echo 'Writing Database Guestbook in (control-c to cancel): ' . PHP_EOL;
  253. for ($x = 5; $x > 0; $x--) {
  254. echo $x . "\r"; sleep(1);
  255. }
  256. }
  257. // Check to see if we have a database file already
  258. $options = $bootstrap->getOption('resources');
  259. $dbFile = $options['db']['params']['dbname'];
  260. if (file_exists($dbFile)) {
  261. unlink($dbFile);
  262. }
  263. // this block executes the actual statements that were loaded from
  264. // the schema file.
  265. try {
  266. $schemaSql = file_get_contents(dirname(__FILE__) . '/schema.sqlite.sql');
  267. // use the connection directly to load sql in batches
  268. $dbAdapter->getConnection()->exec($schemaSql);
  269. chmod($dbFile, 0666);
  270. if ('testing' != APPLICATION_ENV) {
  271. echo PHP_EOL;
  272. echo 'Database Created';
  273. echo PHP_EOL;
  274. }
  275. if ($withData) {
  276. $dataSql = file_get_contents(dirname(__FILE__) . '/data.sqlite.sql');
  277. // use the connection directly to load sql in batches
  278. $dbAdapter->getConnection()->exec($dataSql);
  279. if ('testing' != APPLICATION_ENV) {
  280. echo 'Data Loaded.';
  281. echo PHP_EOL;
  282. }
  283. }
  284. } catch (Exception $e) {
  285. echo 'AN ERROR HAS OCCURED:' . PHP_EOL;
  286. echo $e->getMessage() . PHP_EOL;
  287. return false;
  288. }
  289. // generally speaking, this script will be run from the command line
  290. return true;
  291. ]]></programlisting>
  292. <para>
  293. Now, let's execute this script. From a terminal or the DOS command line, do the following:
  294. </para>
  295. <programlisting language="shell"><![CDATA[
  296. % php scripts/load.sqlite.php --withdata
  297. ]]></programlisting>
  298. <para>
  299. You should see output like the following:
  300. </para>
  301. <programlisting language="text"><![CDATA[
  302. path/to/ZendFrameworkQuickstart/scripts$ php load.sqlite.php --withdata
  303. Writing Database Guestbook in (control-c to cancel):
  304. 1
  305. Database Created
  306. Data Loaded.
  307. ]]></programlisting>
  308. <para>
  309. Now we have a fully working database and table for our guestbook application. Our next few
  310. steps are to build out our application code. This includes building a data source (in our
  311. case, we will use <classname>Zend_Db_Table</classname>), and a data mapper to connect that
  312. data source to our domain model. Finally we'll also create the controller that will interact
  313. with this model to both display existing entries and process new entries.
  314. </para>
  315. <para>
  316. We'll use a <ulink url="http://martinfowler.com/eaaCatalog/tableDataGateway.html">Table Data
  317. Gateway</ulink> to connect to our data source; <classname>Zend_Db_Table</classname>
  318. provides this functionality. To get started, lets create a
  319. <classname>Zend_Db_Table</classname>-based table class. First, create the directory
  320. <filename>application/models/DbTable/</filename>. Then create and edit a file
  321. <filename>Guestbook.php</filename> within it, and add the following contents:
  322. </para>
  323. <programlisting language="php"><![CDATA[
  324. // application/models/DbTable/Guestbook.php
  325. /**
  326. * This is the DbTable class for the guestbook table.
  327. */
  328. class Default_Model_DbTable_Guestbook extends Zend_Db_Table_Abstract
  329. {
  330. /** Table name */
  331. protected $_name = 'guestbook';
  332. }
  333. ]]></programlisting>
  334. <para>
  335. Note the class prefix: <classname>Default_Model_DbTable</classname>. The class prefix
  336. "Default" from our autoloader is the first segment, and then we have the component,
  337. "Model_DbTable"; the latter is mapped to the <filename>models/DbTable/</filename> directory
  338. of the module.
  339. </para>
  340. <para>
  341. All that is truly necessary when extending <classname>Zend_Db_Table</classname> is to
  342. provide a table name and optionally the primary key (if it is not "id").
  343. </para>
  344. <para>
  345. Now let's create a <ulink url="http://martinfowler.com/eaaCatalog/dataMapper.html">Data
  346. Mapper</ulink>. A <emphasis>Data Mapper</emphasis> maps a domain object to the database.
  347. In our case, it will map our model, <classname>Default_Model_Guestbook</classname>, to our
  348. data source, <classname>Default_Model_DbTable_Guestbook</classname>. A typical API for a
  349. data mapper is as follows:
  350. </para>
  351. <programlisting language="php"><![CDATA[
  352. // application/models/GuestbookMapper.php
  353. class Default_Model_GuestbookMapper
  354. {
  355. public function save($model);
  356. public function find($id, $model);
  357. public function fetchAll();
  358. }
  359. ]]></programlisting>
  360. <para>
  361. In addition to these methods, we'll add methods for setting and retrieving the Table Data
  362. Gateway. The final class, located in
  363. <filename>application/models/GuestbookMapper.php</filename>, looks like this:
  364. </para>
  365. <programlisting language="php"><![CDATA[
  366. // application/models/GuestbookMapper.php
  367. class Default_Model_GuestbookMapper
  368. {
  369. protected $_dbTable;
  370. public function setDbTable($dbTable)
  371. {
  372. if (is_string($dbTable)) {
  373. $dbTable = new $dbTable();
  374. }
  375. if (!$dbTable instanceof Zend_Db_Table_Abstract) {
  376. throw new Exception('Invalid table data gateway provided');
  377. }
  378. $this->_dbTable = $dbTable;
  379. return $this;
  380. }
  381. public function getDbTable()
  382. {
  383. if (null === $this->_dbTable) {
  384. $this->setDbTable('Default_Model_DbTable_Guestbook');
  385. }
  386. return $this->_dbTable;
  387. }
  388. public function save(Default_Model_Guestbook $guestbook)
  389. {
  390. $data = array(
  391. 'email' => $guestbook->getEmail(),
  392. 'comment' => $guestbook->getComment(),
  393. 'created' => date('Y-m-d H:i:s'),
  394. );
  395. if (null === ($id = $guestbook->getId())) {
  396. unset($data['id']);
  397. $this->getDbTable()->insert($data);
  398. } else {
  399. $this->getDbTable()->update($data, array('id = ?' => $id));
  400. }
  401. }
  402. public function find($id, Default_Model_Guestbook $guestbook)
  403. {
  404. $result = $this->getDbTable()->find($id);
  405. if (0 == count($result)) {
  406. return;
  407. }
  408. $row = $result->current();
  409. $guestbook->setId($row->id)
  410. ->setEmail($row->email)
  411. ->setComment($row->comment)
  412. ->setCreated($row->created);
  413. }
  414. public function fetchAll()
  415. {
  416. $resultSet = $this->getDbTable()->fetchAll();
  417. $entries = array();
  418. foreach ($resultSet as $row) {
  419. $entry = new Default_Model_Guestbook();
  420. $entry->setId($row->id)
  421. ->setEmail($row->email)
  422. ->setComment($row->comment)
  423. ->setCreated($row->created)
  424. ->setMapper($this);
  425. $entries[] = $entry;
  426. }
  427. return $entries;
  428. }
  429. }
  430. ]]></programlisting>
  431. <para>
  432. Now it's time to update our model class slightly, to accomodate the data mapper. Just like
  433. the data mapper contains a reference to the data source, the model contains a reference to
  434. the data mapper. Additionally, we'll make it easy to populate the model by passing an array
  435. of data either to the constructor or a <methodname>setOptions()</methodname> method. The
  436. final model class, located in <filename>application/models/Guestbook.php</filename>, looks
  437. like this:
  438. </para>
  439. <programlisting language="php"><![CDATA[
  440. // application/models/Guestbook.php
  441. class Default_Model_Guestbook
  442. {
  443. protected $_comment;
  444. protected $_created;
  445. protected $_email;
  446. protected $_id;
  447. protected $_mapper;
  448. public function __construct(array $options = null)
  449. {
  450. if (is_array($options)) {
  451. $this->setOptions($options);
  452. }
  453. }
  454. public function __set($name, $value)
  455. {
  456. $method = 'set' . $name;
  457. if (('mapper' == $name) || !method_exists($this, $method)) {
  458. throw new Exception('Invalid guestbook property');
  459. }
  460. $this->$method($value);
  461. }
  462. public function __get($name)
  463. {
  464. $method = 'get' . $name;
  465. if (('mapper' == $name) || !method_exists($this, $method)) {
  466. throw new Exception('Invalid guestbook property');
  467. }
  468. return $this->$method();
  469. }
  470. public function setOptions(array $options)
  471. {
  472. $methods = get_class_methods($this);
  473. foreach ($options as $key => $value) {
  474. $method = 'set' . ucfirst($key);
  475. if (in_array($method, $methods)) {
  476. $this->$method($value);
  477. }
  478. }
  479. return $this;
  480. }
  481. public function setComment($text)
  482. {
  483. $this->_comment = (string) $text;
  484. return $this;
  485. }
  486. public function getComment()
  487. {
  488. return $this->_comment;
  489. }
  490. public function setEmail($email)
  491. {
  492. $this->_email = (string) $email;
  493. return $this;
  494. }
  495. public function getEmail()
  496. {
  497. return $this->_email;
  498. }
  499. public function setCreated($ts)
  500. {
  501. $this->_created = $ts;
  502. return $this;
  503. }
  504. public function getCreated()
  505. {
  506. return $this->_created;
  507. }
  508. public function setId($id)
  509. {
  510. $this->_id = (int) $id;
  511. return $this;
  512. }
  513. public function getId()
  514. {
  515. return $this->_id;
  516. }
  517. public function setMapper($mapper)
  518. {
  519. $this->_mapper = $mapper;
  520. return $this;
  521. }
  522. public function getMapper()
  523. {
  524. if (null === $this->_mapper) {
  525. $this->setMapper(new Default_Model_GuestbookMapper());
  526. }
  527. return $this->_mapper;
  528. }
  529. public function save()
  530. {
  531. $this->getMapper()->save($this);
  532. }
  533. public function find($id)
  534. {
  535. $this->getMapper()->find($id, $this);
  536. return $this;
  537. }
  538. public function fetchAll()
  539. {
  540. return $this->getMapper()->fetchAll();
  541. }
  542. }
  543. ]]></programlisting>
  544. <para>
  545. Lastly, to connect these elements all together, lets create a guestbook controller that will
  546. both list the entries that are currently inside the database.
  547. </para>
  548. <para>
  549. To create a new controller, open a terminal or DOS console, navigate to your project
  550. directory, and enter the following:
  551. </para>
  552. <programlisting language="shell"><![CDATA[
  553. # Unix-like systems:
  554. % zf.sh create controller guestbook
  555. # DOS/Windows:
  556. C:> zf.bat create controller guestbook
  557. ]]></programlisting>
  558. <para>
  559. This will create a new controller, <classname>GuestbookController</classname>, in
  560. <filename>application/controllers/GuestbookController.php</filename>, with a single action
  561. method, <methodname>indexAction()</methodname>. It will also create a view script directory
  562. for the controller, <filename>application/views/scripts/guestbook/</filename>, with a view
  563. script for the index action.
  564. </para>
  565. <para>
  566. We'll use the "index" action as a landing page to view all guestbook entries.
  567. </para>
  568. <para>
  569. Now, let's flesh out the basic application logic. On a hit to
  570. <methodname>indexAction()</methodname>, we'll display all guestbook entries. This would look
  571. like the following:
  572. </para>
  573. <programlisting language="php"><![CDATA[
  574. // application/controllers/GuestbookController.php
  575. class GuestbookController extends Zend_Controller_Action
  576. {
  577. public function indexAction()
  578. {
  579. $guestbook = new Default_Model_Guestbook();
  580. $this->view->entries = $guestbook->fetchAll();
  581. }
  582. }
  583. ]]></programlisting>
  584. <para>
  585. And, of course, we need a view script to go along with that. Edit
  586. <filename>application/views/scripts/guestbook/index.phtml</filename> to read as follows:
  587. </para>
  588. <programlisting language="php"><![CDATA[
  589. <!-- application/views/scripts/guestbook/index.phtml -->
  590. <p><a href="<?php echo $this->url(
  591. array(
  592. 'controller' => 'guestbook',
  593. 'action' => 'sign'
  594. ),
  595. 'default',
  596. true) ?>">Sign Our Guestbook</a></p>
  597. Guestbook Entries: <br />
  598. <dl>
  599. <?php foreach ($this->entries as $entry): ?>
  600. <dt><?php echo $this->escape($entry->email) ?></dt>
  601. <dd><?php echo $this->escape($entry->comment) ?></dd>
  602. <?php endforeach ?>
  603. </dl>
  604. ]]></programlisting>
  605. <note>
  606. <title>Checkpoint</title>
  607. <para>
  608. Now browse to "http://localhost/guestbook". You should see the following in your
  609. browser:
  610. </para>
  611. <para>
  612. <inlinegraphic width="525" scale="100" align="center" valign="middle"
  613. fileref="figures/learning.quickstart.create-model.png" format="PNG" />
  614. </para>
  615. </note>
  616. <note>
  617. <title>Using the data loader script</title>
  618. <para>
  619. The data loader script introduced in this section
  620. (<filename>scripts/load.sqlite.php</filename>) can be used to create the database for
  621. each environment you have defined, as well as to load it with sample data. Internally,
  622. it utilizes <classname>Zend_Console_Getopt</classname>, which allows it to provide a
  623. number of command line switches. If you pass the "-h" or "--help" switch, it will give
  624. you the available options:
  625. </para>
  626. <programlisting language="php"><![CDATA[
  627. Usage: load.sqlite.php [ options ]
  628. --withdata|-w Load database with sample data
  629. --env|-e [ ] Application environment for which to create database
  630. (defaults to development)
  631. --help|-h Help -- usage message)]]
  632. ]]></programlisting>
  633. <para>
  634. The "-e" switch allows you to specify the value to use for the constant
  635. <constant>APPLICATION_ENV</constant> -- which in turn allows you to create a SQLite
  636. database for each environment you define. Be sure to run the script for the environment
  637. you choose for your application when deploying.
  638. </para>
  639. </note>
  640. </sect1>