|
|
@@ -0,0 +1,760 @@
|
|
|
+<?xml version="1.0" encoding="UTF-8"?>
|
|
|
+<!-- Reviewed: no -->
|
|
|
+<sect1 id="learning.quickstart.create-model">
|
|
|
+ <title>Create a Model and Database Table</title>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Before we get started, let's consider something: where will these classes live, and how will
|
|
|
+ we find them? The default project we created instantiates an autoloader. We can attach other
|
|
|
+ autoloaders to it so that it knows where to find different classes. Typically, we want our
|
|
|
+ various MVC classes grouped under the same tree -- in this case,
|
|
|
+ <filename>application/</filename> -- and most often using a common prefix.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ <classname>Zend_Controller_Front</classname> has a notion of "modules", which are individual
|
|
|
+ mini-applications. Modules mimic the directory structure that the <command>zf</command>
|
|
|
+ tool sets up under <filename>application/</filename>, and all classes inside them are
|
|
|
+ assumed to begin with a common prefix, the module name. <filename>application/</filename>
|
|
|
+ is itself a module -- the "default" module. As such, let's setup autoloading for resources
|
|
|
+ within this directory, giving them a prefix of "Default". We can do this by creating another
|
|
|
+ bootstrap resource.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ <classname>Zend_Application_Module_Autoloader</classname> provides the functionality needed
|
|
|
+ to map the various resources under a module to the appropriate directories, and provides a
|
|
|
+ standard naming mechanism as well. In our bootstrap resource, we'll instantiate this, and be
|
|
|
+ done. The method looks like this:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="php"><![CDATA[
|
|
|
+// application/Bootstrap.php
|
|
|
+
|
|
|
+// Add this method to the Bootstrap class:
|
|
|
+
|
|
|
+ protected function _initAutoload()
|
|
|
+ {
|
|
|
+ $autoloader = new Zend_Application_Module_Autoloader(array(
|
|
|
+ 'namespace' => 'Default_',
|
|
|
+ 'basePath' => dirname(__FILE__),
|
|
|
+ ));
|
|
|
+ return $autoloader;
|
|
|
+ }
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ The final bootstrap class will look as follows:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="php"><![CDATA[
|
|
|
+// application/Bootstrap.php
|
|
|
+
|
|
|
+class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
|
|
|
+{
|
|
|
+ protected function _initAutoload()
|
|
|
+ {
|
|
|
+ $autoloader = new Zend_Application_Module_Autoloader(array(
|
|
|
+ 'namespace' => 'Default',
|
|
|
+ 'basePath' => dirname(__FILE__),
|
|
|
+ ));
|
|
|
+ return $autoloader;
|
|
|
+ }
|
|
|
+
|
|
|
+ protected function _initDoctype()
|
|
|
+ {
|
|
|
+ $this->bootstrap('view');
|
|
|
+ $view = $this->getResource('view');
|
|
|
+ $view->doctype('XHTML1_STRICT');
|
|
|
+ }
|
|
|
+}
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Now, let's consider what makes up a guestbook. Typically, they are simply a list of entries
|
|
|
+ with a <emphasis>comment</emphasis>, <emphasis>timestamp</emphasis>, and, often,
|
|
|
+ <emphasis>email address</emphasis>. Assuming we store them in a database, we may also want a
|
|
|
+ <emphasis>unique identifier</emphasis> for each entry. We'll likely want to be able to save
|
|
|
+ an entry, fetch individual entries, and retrieve all entries. As such, a simple guestbook
|
|
|
+ model API might look something like this:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="php"><![CDATA[
|
|
|
+// application/models/Guestbook.php
|
|
|
+
|
|
|
+class Default_Model_Guestbook
|
|
|
+{
|
|
|
+ protected $_comment;
|
|
|
+ protected $_created;
|
|
|
+ protected $_email;
|
|
|
+ protected $_id;
|
|
|
+
|
|
|
+ public function __set($name, $value);
|
|
|
+ public function __get($name);
|
|
|
+
|
|
|
+ public function setComment($text);
|
|
|
+ public function getComment();
|
|
|
+
|
|
|
+ public function setEmail($email);
|
|
|
+ public function getEmail();
|
|
|
+
|
|
|
+ public function setCreated($ts);
|
|
|
+ public function getCreated();
|
|
|
+
|
|
|
+ public function setId($id);
|
|
|
+ public function getId();
|
|
|
+
|
|
|
+ public function save();
|
|
|
+ public function find($id);
|
|
|
+ public function fetchAll();
|
|
|
+}
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ <methodname>__get()</methodname> and <methodname>__set()</methodname> will provide a
|
|
|
+ convenience mechanism for us to access the individual entry properties, and proxy to the
|
|
|
+ other getters and setters. They also will help ensure that only properties we whitelist will
|
|
|
+ be available in the object.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ <methodname>find()</methodname> and <methodname>fetchAll()</methodname> provide the ability
|
|
|
+ to fetch a single entry or all entries.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Now from here, we can start thinking about setting up our database.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ First we need to initialize our <classname>Db</classname> resource. As with the
|
|
|
+ <classname>Layout</classname> and <classname>View</classname> resource, we can provide
|
|
|
+ configuration for the <classname>Db</classname> resource. In your
|
|
|
+ <filename>application/configs/application.ini</filename> file, add the following lines in
|
|
|
+ the appropriate sections.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="ini"><![CDATA[
|
|
|
+; application/configs/application.ini
|
|
|
+
|
|
|
+; Add these lines to the appropriate sections:
|
|
|
+[production]
|
|
|
+resources.db.adapter = "PDO_SQLITE"
|
|
|
+resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook.db"
|
|
|
+
|
|
|
+[testing : production]
|
|
|
+resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-testing.db"
|
|
|
+
|
|
|
+[development : production]
|
|
|
+resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-dev.db"
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Your final configuration file should look like the following:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="ini"><![CDATA[
|
|
|
+; application/configs/application.ini
|
|
|
+
|
|
|
+[production]
|
|
|
+phpSettings.display_startup_errors = 0
|
|
|
+phpSettings.display_errors = 0
|
|
|
+bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
|
|
|
+bootstrap.class = "Bootstrap"
|
|
|
+resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
|
|
|
+resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts"
|
|
|
+resources.view[] =
|
|
|
+resources.db.adapter = "PDO_SQLITE"
|
|
|
+resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook.db"
|
|
|
+
|
|
|
+[staging : production]
|
|
|
+
|
|
|
+[testing : production]
|
|
|
+phpSettings.display_startup_errors = 1
|
|
|
+phpSettings.display_errors = 1
|
|
|
+resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-testing.db"
|
|
|
+
|
|
|
+[development : production]
|
|
|
+phpSettings.display_startup_errors = 1
|
|
|
+phpSettings.display_errors = 1
|
|
|
+resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-dev.db"
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Note that the database(s) will be stored in <filename>data/db/</filename>. Create those
|
|
|
+ directories, and make them world-writeable. On unix-like systems, you can do that as
|
|
|
+ follows:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="shell"><![CDATA[
|
|
|
+% mkdir -p data/db; chmod -R a+rwX data
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ On Windows, you will need to create the directories in Explorer and set the permissions to
|
|
|
+ allow anyone to write to the directory.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ At this point we have a connection to a database; in our case, its a connection to a Sqlite
|
|
|
+ database located inside our <filename>application/data/</filename> directory. So, let's
|
|
|
+ design a simple table that will hold our guestbook entries.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="sql"><![CDATA[
|
|
|
+-- scripts/schema.sqlite.sql
|
|
|
+--
|
|
|
+-- You will need load your database schema with this SQL.
|
|
|
+
|
|
|
+CREATE TABLE guestbook (
|
|
|
+ id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
|
+ email VARCHAR(32) NOT NULL DEFAULT 'noemail@test.com',
|
|
|
+ comment TEXT NULL,
|
|
|
+ created DATETIME NOT NULL
|
|
|
+);
|
|
|
+
|
|
|
+CREATE INDEX "id" ON "guestbook" ("id");
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ And, so that we can have some working data out of the box, lets create a few rows of
|
|
|
+ information to make our application interesting.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="sql"><![CDATA[
|
|
|
+-- scripts/data.sqlite.sql
|
|
|
+--
|
|
|
+-- You can begin populating the database with the following SQL statements.
|
|
|
+
|
|
|
+INSERT INTO guestbook (email, comment, created) VALUES
|
|
|
+ ('ralph.schindler@zend.com',
|
|
|
+ 'Hello! Hope you enjoy this sample zf application!',
|
|
|
+ DATETIME('NOW'));
|
|
|
+INSERT INTO guestbook (email, comment, created) VALUES
|
|
|
+ ('foo@bar.com',
|
|
|
+ 'Baz baz baz, baz baz Baz baz baz - baz baz baz.',
|
|
|
+ DATETIME('NOW'));
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Now that we have both the schema and some data defined. Lets get a script together that we
|
|
|
+ can now execute to build this database. Naturally, this is not needed in production, but
|
|
|
+ this script will help developers build out the database requirements locally so they can
|
|
|
+ have the fully working application. Create the script as
|
|
|
+ <filename>scripts/load.sqlite.php</filename> with the following contents:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="php"><![CDATA[
|
|
|
+// scripts/load.sqlite.php
|
|
|
+
|
|
|
+/**
|
|
|
+ * Script for creating and loading database
|
|
|
+ */
|
|
|
+
|
|
|
+// Initialize the application path and autoloading
|
|
|
+defined('APPLICATION_PATH')
|
|
|
+ || define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));
|
|
|
+set_include_path(implode(PATH_SEPARATOR, array(
|
|
|
+ APPLICATION_PATH . '/../library',
|
|
|
+ get_include_path(),
|
|
|
+)));
|
|
|
+require_once 'Zend/Loader/Autoloader.php';
|
|
|
+Zend_Loader_Autoloader::getInstance();
|
|
|
+
|
|
|
+// Define some CLI options
|
|
|
+$getopt = new Zend_Console_Getopt(array(
|
|
|
+ 'withdata|w' => 'Load database with sample data',
|
|
|
+ 'env|e-s' => 'Application environment for which to create database (defaults to development)',
|
|
|
+ 'help|h' => 'Help -- usage message',
|
|
|
+));
|
|
|
+try {
|
|
|
+ $getopt->parse();
|
|
|
+} catch (Zend_Console_Getopt_Exception $e) {
|
|
|
+ // Bad options passed: report usage
|
|
|
+ echo $e->getUsageMessage();
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+// If help requested, report usage message
|
|
|
+if ($getopt->getOption('h')) {
|
|
|
+ echo $getopt->getUsageMessage();
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+// Initialize values based on presence or absence of CLI options
|
|
|
+$withData = $getopt->getOption('w');
|
|
|
+$env = $getopt->getOption('e');
|
|
|
+defined('APPLICATION_ENV')
|
|
|
+ || define('APPLICATION_ENV', (null === $env) ? 'development' : $env);
|
|
|
+
|
|
|
+// Initialize Zend_Application
|
|
|
+$application = new Zend_Application(
|
|
|
+ APPLICATION_ENV,
|
|
|
+ APPLICATION_PATH . '/configs/application.ini'
|
|
|
+);
|
|
|
+
|
|
|
+// Initialize and retrieve DB resource
|
|
|
+$bootstrap = $application->getBootstrap();
|
|
|
+$bootstrap->bootstrap('db');
|
|
|
+$dbAdapter = $bootstrap->getResource('db');
|
|
|
+
|
|
|
+// let the user know whats going on (we are actually creating a
|
|
|
+// database here)
|
|
|
+if ('testing' != APPLICATION_ENV) {
|
|
|
+ echo 'Writing Database Guestbook in (control-c to cancel): ' . PHP_EOL;
|
|
|
+ for ($x = 5; $x > 0; $x--) {
|
|
|
+ echo $x . "\r"; sleep(1);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Check to see if we have a database file already
|
|
|
+$options = $bootstrap->getOption('resources');
|
|
|
+$dbFile = $options['db']['params']['dbname'];
|
|
|
+if (file_exists($dbFile)) {
|
|
|
+ unlink($dbFile);
|
|
|
+}
|
|
|
+
|
|
|
+// this block executes the actual statements that were loaded from
|
|
|
+// the schema file.
|
|
|
+try {
|
|
|
+ $schemaSql = file_get_contents(dirname(__FILE__) . '/schema.sqlite.sql');
|
|
|
+ // use the connection directly to load sql in batches
|
|
|
+ $dbAdapter->getConnection()->exec($schemaSql);
|
|
|
+ chmod($dbFile, 0666);
|
|
|
+
|
|
|
+ if ('testing' != APPLICATION_ENV) {
|
|
|
+ echo PHP_EOL;
|
|
|
+ echo 'Database Created';
|
|
|
+ echo PHP_EOL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($withData) {
|
|
|
+ $dataSql = file_get_contents(dirname(__FILE__) . '/data.sqlite.sql');
|
|
|
+ // use the connection directly to load sql in batches
|
|
|
+ $dbAdapter->getConnection()->exec($dataSql);
|
|
|
+ if ('testing' != APPLICATION_ENV) {
|
|
|
+ echo 'Data Loaded.';
|
|
|
+ echo PHP_EOL;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+} catch (Exception $e) {
|
|
|
+ echo 'AN ERROR HAS OCCURED:' . PHP_EOL;
|
|
|
+ echo $e->getMessage() . PHP_EOL;
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+// generally speaking, this script will be run from the command line
|
|
|
+return true;
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Now, let's execute this script. From a terminal or the DOS command line, do the following:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="shell"><![CDATA[
|
|
|
+% php scripts/load.sqlite.php --withdata
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ You should see output like the following:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="text"><![CDATA[
|
|
|
+path/to/ZendFrameworkQuickstart/scripts$ php load.sqlite.php --withdata
|
|
|
+Writing Database Guestbook in (control-c to cancel):
|
|
|
+1
|
|
|
+Database Created
|
|
|
+Data Loaded.
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Now we have a fully working database and table for our guestbook application. Our next few
|
|
|
+ steps are to build out our application code. This includes building a data source (in our
|
|
|
+ case, we will use <classname>Zend_Db_Table</classname>), and a data mapper to connect that
|
|
|
+ data source to our domain model. Finally we'll also create the controller that will interact
|
|
|
+ with this model to both display existing entries and process new entries.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ We'll use a <ulink url="http://martinfowler.com/eaaCatalog/tableDataGateway.html">Table Data
|
|
|
+ Gateway</ulink> to connect to our data source; <classname>Zend_Db_Table</classname>
|
|
|
+ provides this functionality. To get started, lets create a
|
|
|
+ <classname>Zend_Db_Table</classname>-based table class. First, create the directory
|
|
|
+ <filename>application/models/DbTable/</filename>. Then create and edit a file
|
|
|
+ <filename>Guestbook.php</filename> within it, and add the following contents:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="php"><![CDATA[
|
|
|
+// application/models/DbTable/Guestbook.php
|
|
|
+
|
|
|
+/**
|
|
|
+ * This is the DbTable class for the guestbook table.
|
|
|
+ */
|
|
|
+class Default_Model_DbTable_Guestbook extends Zend_Db_Table_Abstract
|
|
|
+{
|
|
|
+ /** Table name */
|
|
|
+ protected $_name = 'guestbook';
|
|
|
+}
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Note the class prefix: <classname>Default_Model_DbTable</classname>. The class prefix
|
|
|
+ "Default" from our autoloader is the first segment, and then we have the component,
|
|
|
+ "Model_DbTable"; the latter is mapped to the <filename>models/DbTable/</filename> directory
|
|
|
+ of the module.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ All that is truly necessary when extending <classname>Zend_Db_Table</classname> is to
|
|
|
+ provide a table name and optionally the primary key (if it is not "id").
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Now let's create a <ulink url="http://martinfowler.com/eaaCatalog/dataMapper.html">Data
|
|
|
+ Mapper</ulink>. A <emphasis>Data Mapper</emphasis> maps a domain object to the database.
|
|
|
+ In our case, it will map our model, <classname>Default_Model_Guestbook</classname>, to our
|
|
|
+ data source, <classname>Default_Model_DbTable_Guestbook</classname>. A typical API for a
|
|
|
+ data mapper is as follows:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="php"><![CDATA[
|
|
|
+// application/models/GuestbookMapper.php
|
|
|
+
|
|
|
+class Default_Model_GuestbookMapper
|
|
|
+{
|
|
|
+ public function save($model);
|
|
|
+ public function find($id, $model);
|
|
|
+ public function fetchAll();
|
|
|
+}
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ In addition to these methods, we'll add methods for setting and retrieving the Table Data
|
|
|
+ Gateway. The final class, located in
|
|
|
+ <filename>application/models/GuestbookMapper.php</filename>, looks like this:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="php"><![CDATA[
|
|
|
+// application/models/GuestbookMapper.php
|
|
|
+
|
|
|
+class Default_Model_GuestbookMapper
|
|
|
+{
|
|
|
+ protected $_dbTable;
|
|
|
+
|
|
|
+ public function setDbTable($dbTable)
|
|
|
+ {
|
|
|
+ if (is_string($dbTable)) {
|
|
|
+ $dbTable = new $dbTable();
|
|
|
+ }
|
|
|
+ if (!$dbTable instanceof Zend_Db_Table_Abstract) {
|
|
|
+ throw new Exception('Invalid table data gateway provided');
|
|
|
+ }
|
|
|
+ $this->_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;
|
|
|
+ }
|
|
|
+}
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Now it's time to update our model class slightly, to accomodate the data mapper. Just like
|
|
|
+ the data mapper contains a reference to the data source, the model contains a reference to
|
|
|
+ the data mapper. Additionally, we'll make it easy to populate the model by passing an array
|
|
|
+ of data either to the constructor or a <methodname>setOptions()</methodname> method. The
|
|
|
+ final model class, located in <filename>application/models/Guestbook.php</filename>, looks
|
|
|
+ like this:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="php"><![CDATA[
|
|
|
+// application/models/Guestbook.php
|
|
|
+
|
|
|
+class Default_Model_Guestbook
|
|
|
+{
|
|
|
+ protected $_comment;
|
|
|
+ protected $_created;
|
|
|
+ protected $_email;
|
|
|
+ protected $_id;
|
|
|
+ protected $_mapper;
|
|
|
+
|
|
|
+ public function __construct(array $options = null)
|
|
|
+ {
|
|
|
+ if (is_array($options)) {
|
|
|
+ $this->setOptions($options);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public function __set($name, $value)
|
|
|
+ {
|
|
|
+ $method = 'set' . $name;
|
|
|
+ if (('mapper' == $name) || !method_exists($this, $method)) {
|
|
|
+ throw new Exception('Invalid guestbook property');
|
|
|
+ }
|
|
|
+ $this->$method($value);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function __get($name)
|
|
|
+ {
|
|
|
+ $method = 'get' . $name;
|
|
|
+ if (('mapper' == $name) || !method_exists($this, $method)) {
|
|
|
+ throw new Exception('Invalid guestbook property');
|
|
|
+ }
|
|
|
+ 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();
|
|
|
+ }
|
|
|
+}
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Lastly, to connect these elements all together, lets create a guestbook controller that will
|
|
|
+ both list the entries that are currently inside the database.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ To create a new controller, open a terminal or DOS console, navigate to your project
|
|
|
+ directory, and enter the following:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="shell"><![CDATA[
|
|
|
+# Unix-like systems:
|
|
|
+% zf.sh create controller guestbook
|
|
|
+
|
|
|
+# DOS/Windows:
|
|
|
+C:> zf.bat create controller guestbook
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ This will create a new controller, <classname>GuestbookController</classname>, in
|
|
|
+ <filename>application/controllers/GuestbookController.php</filename>, with a single action
|
|
|
+ method, <methodname>indexAction()</methodname>. It will also create a view script directory
|
|
|
+ for the controller, <filename>application/views/scripts/guestbook/</filename>, with a view
|
|
|
+ script for the index action.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ We'll use the "index" action as a landing page to view all guestbook entries.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Now, let's flesh out the basic application logic. On a hit to
|
|
|
+ <methodname>indexAction()</methodname>, we'll display all guestbook entries. This would look
|
|
|
+ like the following:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="php"><![CDATA[
|
|
|
+// application/controllers/GuestbookController.php
|
|
|
+
|
|
|
+class GuestbookController extends Zend_Controller_Action
|
|
|
+{
|
|
|
+ public function indexAction()
|
|
|
+ {
|
|
|
+ $guestbook = new Default_Model_Guestbook();
|
|
|
+ $this->view->entries = $guestbook->fetchAll();
|
|
|
+ }
|
|
|
+}
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ And, of course, we need a view script to go along with that. Edit
|
|
|
+ <filename>application/views/scripts/guestbook/index.phtml</filename> to read as follows:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="php"><![CDATA[
|
|
|
+<!-- application/views/scripts/guestbook/index.phtml -->
|
|
|
+
|
|
|
+<p><a href="<?php echo $this->url(
|
|
|
+ array(
|
|
|
+ 'controller' => 'guestbook',
|
|
|
+ 'action' => 'sign'
|
|
|
+ ),
|
|
|
+ 'default',
|
|
|
+ true) ?>">Sign Our Guestbook</a></p>
|
|
|
+
|
|
|
+Guestbook Entries: <br />
|
|
|
+<dl>
|
|
|
+ <?php foreach ($this->entries as $entry): ?>
|
|
|
+ <dt><?php echo $this->escape($entry->email) ?></dt>
|
|
|
+ <dd><?php echo $this->escape($entry->comment) ?></dd>
|
|
|
+ <?php endforeach ?>
|
|
|
+</dl>
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <note>
|
|
|
+ <title>Checkpoint</title>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Now browse to "http://localhost/guestbook". You should see the following in your
|
|
|
+ browser:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ <inlinegraphic width="525" scale="100" align="center" valign="middle"
|
|
|
+ fileref="figures/learning.quickstart.create-model.png" format="PNG" />
|
|
|
+ </para>
|
|
|
+ </note>
|
|
|
+
|
|
|
+ <note>
|
|
|
+ <title>Using the data loader script</title>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ The data loader script introduced in this section
|
|
|
+ (<filename>scripts/load.sqlite.php</filename>) can be used to create the database for
|
|
|
+ each environment you have defined, as well as to load it with sample data. Internally,
|
|
|
+ it utilizes <classname>Zend_Console_Getopt</classname>, which allows it to provide a
|
|
|
+ number of command line switches. If you pass the "-h" or "--help" switch, it will give
|
|
|
+ you the available options:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="php"><![CDATA[
|
|
|
+Usage: load.sqlite.php [ options ]
|
|
|
+--withdata|-w Load database with sample data
|
|
|
+--env|-e [ ] Application environment for which to create database
|
|
|
+ (defaults to development)
|
|
|
+--help|-h Help -- usage message)]]
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ The "-e" switch allows you to specify the value to use for the constant
|
|
|
+ <constant>APPLICATION_ENV</constant> -- which in turn allows you to create a SQLite
|
|
|
+ database for each environment you define. Be sure to run the script for the environment
|
|
|
+ you choose for your application when deploying.
|
|
|
+ </para>
|
|
|
+ </note>
|
|
|
+</sect1>
|