Antes de comenzar, consideremos algo: ¿dónde vivirán estas clases, y cómo
las encontraremos? El proyecto por defecto que creamos instancia un autoloader. Podemos adjuntarle otros
autoloaders para que sepa dónde encontrar diferentes clases. Normalmente, queremos que nuestras
distintas clases MVC estén agrupadas bajo el mismo árbol -- en este caso,
application/ -- y casi siempre usando un prefijo común.
Zend_Controller_Front tiene una noción de "módulos", que son
mini-aplicaciones individuales. Los módulos imitan la estructura de directorios que la herramienta
zf configura bajo application/, y se asume que todas las
clases dentro de ellos comienzan con un prefijo común, el nombre del módulo. application/
es en sí mismo un módulo -- el módulo "default" o "application". Como tal, querremos configurar
el autoloading para los recursos dentro de este directorio.
Zend_Application_Module_Autoloader proporciona la funcionalidad necesaria
para mapear los distintos recursos bajo un módulo a los directorios apropiados, y también proporciona un
mecanismo de nomenclatura estándar. Se crea una instancia de la clase por defecto durante la
inicialización del objeto bootstrap; el bootstrap de su aplicación usará por defecto el
prefijo de módulo "Application". Como tal, nuestros modelos, formularios y clases de tabla comenzarán todos
con el prefijo de clase "Application_".
Ahora, consideremos qué compone un libro de visitas. Normalmente, son simplemente una lista de entradas con un comentario, una marca de tiempo, y, a menudo, una dirección de correo electrónico. Suponiendo que las almacenamos en una base de datos, también podríamos querer un identificador único para cada entrada. Probablemente querremos poder guardar una entrada, obtener entradas individuales y recuperar todas las entradas. Como tal, una API simple de modelo de libro de visitas podría verse algo así:
// application/models/Guestbook.php
class Application_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();
}
class Application_Model_GuestbookMapper
{
public function save(Application_Model_Guestbook $guestbook);
public function find($id);
public function fetchAll();
}
__get() y __set() nos proporcionarán un
mecanismo de conveniencia para acceder a las propiedades individuales de la entrada, y delegarán a
los demás getters y setters. También ayudarán a asegurar que solo las propiedades que incluyamos en la lista blanca
estarán disponibles en el objeto.
find() y fetchAll() proporcionan la capacidad
de obtener una única entrada o todas las entradas, mientras que save() se encarga de
guardar una entrada en el almacén de datos.
Ahora, desde aquí, podemos empezar a pensar en configurar nuestra base de datos.
Primero necesitamos inicializar nuestro recurso Db. Al igual que con los recursos
Layout y View, podemos proporcionar
configuración para el recurso Db. Podemos hacer esto con el
comando zf configure db-adapter:
% zf configure db-adapter \ > 'adapter=PDO_SQLITE&dbname=APPLICATION_PATH "/../data/db/guestbook.db"' \ > production A db configuration for the production has been written to the application config file. % zf configure db-adapter \ > 'adapter=PDO_SQLITE&dbname=APPLICATION_PATH "/../data/db/guestbook-testing.db"' \ > testing A db configuration for the production has been written to the application config file. % zf configure db-adapter \ > 'adapter=PDO_SQLITE&dbname=APPLICATION_PATH "/../data/db/guestbook-dev.db"' \ > development A db configuration for the production has been written to the application config file.
Ahora edite su archivo application/configs/application.ini, donde verá
que se agregaron las siguientes líneas en las secciones apropiadas.
; application/configs/application.ini [production] ; ... resources.db.adapter = "PDO_SQLITE" resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook.db" [testing : production] ; ... resources.db.adapter = "PDO_SQLITE" resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-testing.db" [development : production] ; ... resources.db.adapter = "PDO_SQLITE" resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-dev.db"
Su archivo de configuración final debería verse como el siguiente:
; application/configs/application.ini [production] phpSettings.display_startup_errors = 0 phpSettings.display_errors = 0 bootstrap.path = APPLICATION_PATH "/Bootstrap.php" bootstrap.class = "Bootstrap" appnamespace = "Application" resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers" resources.frontController.params.displayExceptions = 0 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.adapter = "PDO_SQLITE" resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-testing.db" [development : production] phpSettings.display_startup_errors = 1 phpSettings.display_errors = 1 resources.db.adapter = "PDO_SQLITE" resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-dev.db"
Tenga en cuenta que la(s) base(s) de datos se almacenarán en data/db/. Cree esos
directorios, y hágalos escribibles por todos. En sistemas tipo unix, puede hacerlo de la
siguiente manera:
% mkdir -p data/db; chmod -R a+rwX data
En Windows, necesitará crear los directorios en el Explorador y establecer los permisos para permitir que cualquiera escriba en el directorio.
Llegados a este punto tenemos una conexión a una base de datos; en nuestro caso, es una conexión a una base de datos
Sqlite ubicada dentro de nuestro directorio application/data/. Así que, diseñemos
una tabla simple que contendrá las entradas de nuestro libro de visitas.
-- 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");
Y, para que tengamos algunos datos de trabajo desde el principio, creemos algunas filas de información para hacer que nuestra aplicación sea interesante.
-- 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'));
Ahora que tenemos definidos tanto el esquema como algunos datos. Preparemos un script que podamos
ahora ejecutar para construir esta base de datos. Naturalmente, esto no es necesario en producción, pero
este script ayudará a los desarrolladores a construir los requisitos de la base de datos localmente para que puedan
tener la aplicación completamente funcional. Cree el script como
scripts/load.sqlite.php con el siguiente contenido:
// 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;
Ahora, ejecutemos este script. Desde una terminal o la línea de comandos de DOS, haga lo siguiente:
% php scripts/load.sqlite.php --withdata
Debería ver una salida como la siguiente:
path/to/ZendFrameworkQuickstart/scripts$ php load.sqlite.php --withdata Writing Database Guestbook in (control-c to cancel): 1 Database Created Data Loaded.
Ahora tenemos una base de datos y tabla completamente funcionales para nuestra aplicación de libro de visitas. Nuestros próximos
pasos son construir el código de nuestra aplicación. Esto incluye construir una fuente de datos (en nuestro
caso, usaremos Zend_Db_Table), y un data mapper para conectar esa
fuente de datos a nuestro modelo de dominio. Finalmente también crearemos el controlador que interactuará
con este modelo tanto para mostrar las entradas existentes como para procesar entradas nuevas.
Usaremos un Table Data
Gateway para conectarnos a nuestra fuente de datos; Zend_Db_Table
proporciona esta funcionalidad. Para empezar, creemos una clase de tabla basada en
Zend_Db_Table. Al igual que hicimos para los layouts y
el adaptador de base de datos, podemos usar la herramienta zf para ayudarnos, usando el comando
create db-table. Esto toma como mínimo dos argumentos, el nombre por el cual
desea referirse a la clase, y la tabla de base de datos a la que se mapea.
% zf create db-table Guestbook guestbook Creating a DbTable at application/models/DbTable/Guestbook.php Updating project profile 'zfproject.xml'
Al mirar su árbol de directorios, ahora verá que se creó un nuevo directorio,
application/models/DbTable/, con el archivo
Guestbook.php. Si abre ese archivo, verá el siguiente
contenido:
// application/models/DbTable/Guestbook.php
/**
* This is the DbTable class for the guestbook table.
*/
class Application_Model_DbTable_Guestbook extends Zend_Db_Table_Abstract
{
/** Table name */
protected $_name = 'guestbook';
}
Observe el prefijo de la clase: Application_Model_DbTable. El prefijo de clase
para nuestro módulo, "Application", es el primer segmento, y luego tenemos el componente,
"Model_DbTable"; este último se mapea al directorio models/DbTable/
del módulo.
Todo lo que es realmente necesario al extender Zend_Db_Table es
proporcionar un nombre de tabla y opcionalmente la clave primaria (si no es "id").
Ahora creemos un Data
Mapper. Un Data Mapper mapea un objeto de dominio a la base de datos.
En nuestro caso, mapeará nuestro modelo, Application_Model_Guestbook, a
nuestra fuente de datos, Application_Model_DbTable_Guestbook. Una
API típica para un data mapper es la siguiente:
// application/models/GuestbookMapper.php
class Application_Model_GuestbookMapper
{
public function save($model);
public function find($id, $model);
public function fetchAll();
}
Además de estos métodos, agregaremos métodos para establecer y recuperar el Table Data Gateway. Para crear la clase inicial, use la herramienta CLI zf:
% zf create model GuestbookMapper Creating a model at application/models/GuestbookMapper.php Updating project profile '.zfproject.xml'
Ahora, edite la clase Application_Model_GuestbookMapper que se encuentra en
application/models/GuestbookMapper.php para que quede de la siguiente manera:
// application/models/GuestbookMapper.php
class Application_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('Application_Model_DbTable_Guestbook');
}
return $this->_dbTable;
}
public function save(Application_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, Application_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 Application_Model_Guestbook();
$entry->setId($row->id)
->setEmail($row->email)
->setComment($row->comment)
->setCreated($row->created);
$entries[] = $entry;
}
return $entries;
}
}
Ahora es momento de crear nuestra clase de modelo. Lo haremos, una vez más, usando el comando zf create model:
% zf create model Guestbook Creating a model at application/models/Guestbook.php Updating project profile '.zfproject.xml'
Modificaremos esta clase PHP vacía para facilitar la población del modelo
pasando un array de datos ya sea al constructor o a un método
setOptions(). La clase de modelo final, ubicada en
application/models/Guestbook.php, debería verse así:
// application/models/Guestbook.php
class Application_Model_Guestbook
{
protected $_comment;
protected $_created;
protected $_email;
protected $_id;
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;
}
}
Por último, para conectar todos estos elementos, creemos un controlador de libro de visitas que liste las entradas que actualmente están dentro de la base de datos.
Para crear un nuevo controlador, use el comando zf create controller:
% zf create controller Guestbook
Creating a controller at
application/controllers/GuestbookController.php
Creating an index action method in controller Guestbook
Creating a view script for the index action method at
application/views/scripts/guestbook/index.phtml
Creating a controller test file at
tests/application/controllers/GuestbookControllerTest.php
Updating project profile '.zfproject.xml'
Esto creará un nuevo controlador, GuestbookController, en
application/controllers/GuestbookController.php, con un único método de
acción, indexAction(). También creará un directorio de scripts de vista
para el controlador, application/views/scripts/guestbook/, con un script
de vista para la acción index.
Usaremos la acción "index" como página de aterrizaje para ver todas las entradas del libro de visitas.
Ahora, desarrollemos la lógica básica de la aplicación. Al acceder a
indexAction(), mostraremos todas las entradas del libro de visitas. Esto se vería
de la siguiente manera:
// application/controllers/GuestbookController.php
class GuestbookController extends Zend_Controller_Action
{
public function indexAction()
{
$guestbook = new Application_Model_GuestbookMapper();
$this->view->entries = $guestbook->fetchAll();
}
}
Y, por supuesto, necesitamos un script de vista que lo acompañe. Edite
application/views/scripts/guestbook/index.phtml para que quede de la siguiente manera:
<!-- 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>
![]() |
Punto de control |
|---|---|
|
Ahora navegue a "http://localhost/guestbook". Debería ver lo siguiente en su navegador:
|
![]() |
Uso del script de carga de datos |
|---|---|
|
El script de carga de datos introducido en esta sección
(
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)]]
El modificador "-e" le permite especificar el valor a usar para la constante
|
![[Note]](images/note.png)