Zend Framework desde cero - Modelos e Integración de Doctrina ORM
Spanish (Español) translation by Steven (you can also view the original English article)
¿Listo para llevar tus habilidades de PHP al siguiente nivel? En esta nueva serie "desde Cero", nos centraremos exclusivamente en Zend Framework, un framework PHP de pila completa creado por Zend Technologies. Este segundo tutorial de nuestra serie se titula "Modelos e Integración de Doctrine ORM".
Comentario
¡Bienvenido de nuevo a nuestra serie Zend Framework desde cero! En nuestro último tutorial, aprendimos algunas cosas básicas acerca de Zend Framework, tales como:
- Dónde descargar los últimos archivos de Zend Framework
- ¿Dónde y cómo configurarlo localmente?
- Crear tu primer proyecto de Zend Framework y configurar un VirtualHost en tu servidor web
- Cómo exactamente Zend Framework implementa el patrón MVC y su enrutamiento de aplicación predeterminado
- Pasando datos desde un controlador a su vista
- Creación del diseño de un sitio para su aplicación Zend Framework
- Creando nuevos controladores y acciones.
Si aún no lo has hecho, deberías leer el tutorial anterior. Realmente te facilitará comprender algunos conceptos básicos de Zend Framework y te ayudará a comprender algunas de las cosas que discutimos en este tutorial.
En esta segunda parte de la serie, hablaremos de una parte crucial de cualquier aplicación web: los MODELOS. También veremos cómo integrar el muy popular ORD de Doctrine con nuestro proyecto Zend Framework, y descubriremos por qué es mucho mejor usarlo el Zend_Db nativo de Zend Framework. Así que, sin más preámbulos, ¡comencemos!
¿Qué son exactamente los "modelos"?
Cuando comencé a tratar de comprender el concepto de MVC, leí varias analogías, que intentaron explicar exactamente qué representa cada uno de estos componentes. Una de las mejores analogías que he leído hasta ahora fue la de este artículo, Otra forma de pensar acerca de MVC. Es algo parecido a esto:
Imaginemos un banco.
La caja de seguridad es la base de datos: aquí es donde se almacenan todos los bienes más importantes y están bien protegidos del mundo exterior.
Luego tenemos a los banqueros o en términos de programación, los Modelos. Los banqueros son los únicos que tienen acceso a la caja fuerte (DB). Generalmente son gordos, viejos y perezosos, que siguen bastante bien con una de las reglas de MVC: *Modelos gordos, controladores flacos*. Veremos por qué y cómo. Esta analogía se aplica un poco más tarde.
Ahora tenemos nuestros trabajadores bancarios promedio, los geófilos, los corredores, los controladores. Los controladores o gofers hacen todo el recorrido, por eso tienen que estar en forma y ser delgados. Toman el botín o la información de los banqueros (los Modelos) y la llevan a los clientes del Banco las Vistas.
Los banqueros (modelos) han estado en el trabajo por un tiempo, por lo tanto, toman todas las decisiones importantes. Lo que nos lleva a otra regla: *mantener la mayor lógica de negocios en el Modelo como sea posible*. Los controladores, nuestros trabajadores promedio, no deben tomar tales decisiones, le piden detalles al banquero, obtienen la información y se la transmiten al cliente (la Vista). Por lo tanto, continuamos siguiendo la regla de los *modelos gordos, controladores flacos*. Los gophers no toman decisiones importantes, pero no pueden ser simples tontos (por lo tanto, un poco de lógica de negocios en el controlador está bien). Sin embargo, tan pronto como el Gopher comienza a pensar demasiado, el banquero se enoja y su banco (o su aplicación) se cierra. Así que, una vez más, siempre recuerda descargar tanta lógica de negocios (o toma de decisiones) al modelo.
Ahora, los banqueros no van a hablar directamente con los clientes (la Vista), son demasiado importantes en sus cómodos sillones para eso. Por lo tanto, se sigue otra regla: Los modelos no deben hablar con vistas*. Esta comunicación entre el banquero y el cliente (el Modelo y la Vista) siempre es manejada por el Gopher (el Controlador). (Sí, hay alguna excepción a esta regla para los clientes súper VIP, pero sigamos con lo básico por el momento).
También sucede que un solo trabajador (Controlador) tiene que obtener información de más de un banquero, y eso es perfectamente aceptable. Sin embargo, si los banqueros están relacionados (de lo contrario, ¿de qué otra manera obtendrían tan buenos trabajos?)... los banqueros (Modelos) se comunicarán entre sí primero, y luego pasarán la información acumulada a su Gopher, quien con gusto la entregará al cliente ( Ver). Así que aquí hay otra regla: *Los Modelos relacionados proporcionan información al controlador a través de su asociación (relación)*.
Entonces, ¿qué pasa con nuestro cliente (la vista)? Bueno, los bancos cometen errores y el cliente debe ser lo suficientemente inteligente como para equilibrar su propia cuenta y tomar algunas decisiones. En términos de MVC obtenemos otra regla simple: *es bastante correcto que las vistas contengan cierta lógica, que trata con la vista o presentación*. Siguiendo nuestra analogía, el cliente se asegurará de no olvidar usar pantalones mientras van al banco, pero no les dirán a los banqueros cómo procesar las transacciones.
En una palabra:
- Los modelos son representantes de la base de datos y deben estar donde reside toda la lógica empresarial de una aplicación
- Los controladores se comunican con los modelos y les piden que recuperen la información que necesitan
- Esta información es luego pasada por un Controlador a la Vista y es renderizada
- Es muy raro que un modelo interactúe directamente con una vista, pero a veces puede suceder cuando sea necesario.
- Los modelos pueden hablar con otros modelos y no son independientes. Tienen relaciones que se entrelazan entre sí.
- Estas relaciones hacen que sea más fácil y rápido para un Controlador obtener información, ya que no tiene que interactuar con diferentes Modelos; los Modelos pueden hacerlo ellos mismos
Podemos ver cuán importantes son los modelos en cualquier aplicación, ya que es responsable de cualquier acción dinámica que suceda en una aplicación. Ahora que tenemos una comprensión bastante clara de las responsabilidades del Modelo, así como de la Vista y el Controlador, vamos a sumergirnos en la implementación de los Modelos en nuestra aplicación.
Paso 1: Configuración de la aplicación Zend para conectarse a una base de datos
Lo primero que tendremos que hacer es hacer que nuestra aplicación Zend se conecte a una base de datos. Afortunadamente, el comando zf puede encargarse de eso. Abre tu símbolo del sistema (o terminal), cd en tu carpeta thenextsocial, y escribe lo siguiente:
1 |
zf configure db-adapter "adapter=PDO_MYSQL&dbname=thenextsocial&host=localhost&username=[your local database username]&password=[your local database password]" -s development |
Si es correcto, deberías obtener una salida similar a:
1 |
A db configuration for the development section has been written to the application config file. |
Además, deberías ver dos nuevas líneas dentro de tu archivo application.ini:
1 |
resources.db.adapter = "PDO_MYSQL" |
2 |
resources.db.params.dbname = "thenextsocial" |
3 |
resources.db.params.host = "localhost" |
4 |
resources.db.params.username = "[your local database username]" |
5 |
resources.db.params.password = "[your local database password]" |
6 |
</p></code> |
7 |
|
8 |
<h3>The application.ini explained</h3> |
9 |
<p>The <code>application.ini</code> is a configuration file which should contain all of the configuration we have for an application. This includes, for example, what kind of database we're using, what the database name is, the username and password we'll be using with the database, even custom PHP settings like error display and include paths.</p> |
10 |
|
11 |
<div class="tutorial_image"> |
12 |
<img src="http://nettuts.s3.amazonaws.com/1122_zend2/images/application_ini.png" alt="The application.ini file" title="The application.ini file" /> |
13 |
|
14 |
<small>The <code>application.ini</code> file</small> |
15 |
</div> |
16 |
|
17 |
<p>I'm sure you've noticed that the <code>application.ini</code> file has sections enclosed in [square brackets]. One of the great things about the <code>application.ini</code> is that you can define different settings depending on what environment your code is in. For example, the database parameters we created earlier falls under the <code>[development : production]</code> section, which means that the set of settings under this section will be used when the application is being run on the <code>development</code> environment.</p> |
18 |
|
19 |
<p>To add to that, you can “inherit” settings from another section. For example, the <code>[development : production]</code> section is the configuration for the <code>development</code> environmnent, but inherits all the settings from the <code>production</code> environment as well. This means that any setting which you haven't explicitly overwritten in <code>development</code> will use the setting from <code>production</code>. This allows you to configure settings that are the same in all environments in one place, and just override the ones that you need. Pretty nifty huh?</p> |
20 |
|
21 |
<p>To configure our project to use the <code>development</code> configuration settings, open or create an <strong>.htaccess</strong> file inside the <strong>public_html</strong> folder, and make sure that it looks like this:</p> |
22 |
|
23 |
[php] |
24 |
SetEnv APPLICATION_ENV development |
25 |
RewriteEngine On |
26 |
RewriteCond %{REQUEST_FILENAME} -s [OR] |
27 |
RewriteCond %{REQUEST_FILENAME} -l [OR] |
28 |
RewriteCond %{REQUEST_FILENAME} -d |
29 |
RewriteRule ^.*$ - [NC,L] |
30 |
RewriteRule ^.*$ index.php [NC,L] |
Podemos ver claramente que la directiva SetEnv APPLICATION_ENV establece el entorno de nuestra aplicación. Si y cuando movemos la aplicación a otro entorno, esto debería ser lo único que debemos cambiar. Esto garantiza que todo en lo que se basa nuestra aplicación para trabajar está definido en application.ini, lo que garantiza que nuestra aplicación no se base en ninguna configuración externa. Esto ayuda a eliminar el problema "funciona en mi máquina de desarrollo, ¿por qué no funciona en el servidor de producción?".
Paso 2 - Crear la base de datos y algunas tablas
Antes de crear tu primer modelo para la aplicación, necesitaremos una base de datos que el modelo representará primero. Comencemos con algo simple: una tabla User, donde guardaremos todos los usuarios registrados para TheNextSocial.
Inicia sesión en tu base de datos MySQL y crea una base de datos llamada thenextsocial. Una vez que se haya creado, ejecuta la siguiente consulta para crear una tabla User y una tabla adjunta de configuración de usuario llamada User Settings:
1 |
CREATE TABLE `thenextsocial`.`user` ( |
2 |
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, |
3 |
`email` VARCHAR(100) NOT NULL, |
4 |
`password` TEXT NOT NULL, |
5 |
`salt` TEXT NOT NULL, |
6 |
`date_created` DATETIME NOT NULL, |
7 |
PRIMARY KEY (`id`), |
8 |
UNIQUE INDEX `Index_email`(`email`) |
9 |
)
|
10 |
ENGINE = InnoDB |
11 |
CHARACTER SET utf8 COLLATE utf8_general_ci; |
12 |
|
13 |
CREATE TABLE `thenextsocial`.`user_settings` ( |
14 |
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, |
15 |
`user_id` INTEGER UNSIGNED NOT NULL, |
16 |
`name` VARCHAR(100) NOT NULL, |
17 |
`value` TEXT NOT NULL, |
18 |
PRIMARY KEY (`id`), |
19 |
CONSTRAINT `FK_user_settings_user_id` FOREIGN KEY `FK_user_settings_user_id` (`user_id`) |
20 |
REFERENCES `user` (`id`) |
21 |
ON DELETE CASCADE |
22 |
ON UPDATE CASCADE |
23 |
)
|
24 |
ENGINE = InnoDB |
25 |
CHARACTER SET utf8 COLLATE utf8_general_ci; |
Estas consultas SQL deben crear dos tablas. Una tabla User con las siguientes columnas:
- id - una ID única para cada usuario
- email - la dirección de correo electrónico del usuario, también único.
- password - la contraseña del usuario, que tendremos encriptada.
- salt - un sal aleatorio, que utilizaremos para marcar la contraseña del usuario
- date_created - la fecha y hora en que se creó el registro de usuario
Y una tabla user_settings, donde almacenaremos cualquier configuración relacionada con el usuario, con las columnas:
- id - una ID única para cada configuración
- user_id - el
user_idque es una clave externa parauser.id - name - una cadena de texto que representa la configuración
- value -el valor de la configuración
Vale la pena tener en cuenta que la tabla User Settings y User comparte una relación de uno a varios, lo que significa que un solo registro de usuario puede relacionarse con varios registros de configuración de usuario. Esto facilitará el almacenamiento de cualquier tipo de información relacionada con un usuario, por ejemplo, tu nombre o foto de perfil.
Ahora que tenemos algunas tablas para jugar, aprendamos cómo crear nuestro primer modelo: el modelo User.
Paso 3 - Creando tu primer modelo
El patrón de diseño DAO
Al igual que con muchas aplicaciones, la forma habitual de utilizar los modelos make en Zend Framework es hacer uso de un patrón de diseño popular denominado patrón "DAO". En este patrón tenemos los siguientes componentes:
- Table Data Gateway (DataSource) - Es la puerta de enlace de datos de la tabla (fuente de datos) que conecta tu aplicación con la fuente de datos, la tabla MySQL
- Data Mapper (DataAccessObject) que mapea los datos recuperados de la base de datos y los asigna.
- Objeto de datos (datos) - Data Object (Data) que representa una fila de nuestra base de datos, después de que el DataMapper le asigna los datos
Comencemos por crear una puerta de enlace de datos de tabla (Table Data Gateway) para la tabla de usuario utilizando la herramienta CLI de zf:
1 |
zf create db-table User user |
2 |
Creating a DbTable at thenextsocial/application/models/DbTable/User.php |
3 |
Updating project profile 'thenextsocial/.zfproject.xml' |
La zf create db-table toma en dos parámetros:
- ClassName - el nombre de la clase
- database_table - el nombre de la tabla
Abre el archivo User.php que se encuentra en la carpeta application/models/DbTable y debería verse así:
1 |
<?php
|
2 |
|
3 |
class Application_Model_DbTable_User extends Zend_Db_Table_Abstract |
4 |
{
|
5 |
|
6 |
protected $_name = 'user'; |
7 |
|
8 |
|
9 |
}
|
Ahora vamos a crear una clase Data Mapper. De nuevo, usando la herramienta CLI de zf:
1 |
zf create model UserMapper |
2 |
Creating a model at thenextsocial/application/models/UserMapper.php |
3 |
Updating project profile 'thenextsocial/.zfproject.xml' |
El archivo UserMapper.php estará vacío en este momento, pero luego ingresaremos un código. Por ahora, necesitamos crear el objeto de datos, que es el modelo User:
1 |
zf create model User |
2 |
Creating a model at thenextsocial/application/models/User.php |
3 |
Updating project profile 'thenextsocial/.zfproject.xml' |
4 |
</p></code>
|
5 |
|
6 |
<p>Now that we have all three components of the DAO pattern, we create the code for the files. Open the <code>UserMapper.php</code> file and put in the following code:</p> |
7 |
|
8 |
[php] |
9 |
<?php
|
10 |
class Application_Model_UserMapper |
11 |
{
|
12 |
protected $_db_table; |
13 |
|
14 |
public function __construct() |
15 |
{
|
16 |
//Instantiate the Table Data Gateway for the User table
|
17 |
$this->_db_table = new Application_Model_DbTable_User(); |
18 |
}
|
19 |
|
20 |
public function save(Application_Model_User $user_object) |
21 |
{
|
22 |
//Create an associative array
|
23 |
//of the data you want to update
|
24 |
$data = array( |
25 |
'email' => $user_object->email, |
26 |
'password' => $user_object->password, |
27 |
);
|
28 |
|
29 |
//Check if the user object has an ID
|
30 |
//if no, it means the user is a new user
|
31 |
//if yes, then it means you're updating an old user
|
32 |
if( is_null($user_object->id) ) { |
33 |
$data['salt'] = $user_object->salt; |
34 |
$data['date_created'] = date('Y-m-d H:i:s'); |
35 |
$this->_db_table->insert($data); |
36 |
} else { |
37 |
$this->_db_table->update($data, array('id = ?' => $user_object->id)); |
38 |
}
|
39 |
}
|
40 |
|
41 |
public function getUserById($id) |
42 |
{
|
43 |
//use the Table Gateway to find the row that
|
44 |
//the id represents
|
45 |
$result = $this->_db_table->find($id); |
46 |
|
47 |
//if not found, throw an exsception
|
48 |
if( count($result) == 0 ) { |
49 |
throw new Exception('User not found'); |
50 |
}
|
51 |
|
52 |
//if found, get the result, and map it to the
|
53 |
//corresponding Data Object
|
54 |
$row = $result->current(); |
55 |
$user_object = new Application_Model_User($row); |
56 |
|
57 |
//return the user object
|
58 |
return $user_object; |
59 |
}
|
60 |
}
|
Aquí tenemos tres métodos:
- __construct() - Es el constructor para la clase. Una vez que se crea una instancia, crea una instancia de Table Data Gateway y la almacena
- save(Application_Model_User $user_object): toma a
Application_Model_Usery guarda los datos del objeto en la base de datos - getUserById($id): toma un entero
$idque representa una sola fila de la tabla de base de datos, lo recupera, luego devuelve un Application_Model_User con los datos asignados
Abre User.php y coloca el siguiente código en:
1 |
<?php
|
2 |
class Application_Model_User |
3 |
{
|
4 |
//declare the user's attributes
|
5 |
private $id; |
6 |
private $email; |
7 |
private $password; |
8 |
private $salt; |
9 |
private $date_created; |
10 |
|
11 |
//upon construction, map the values
|
12 |
//from the $user_row if available
|
13 |
public function __construct($user_row = null) |
14 |
{
|
15 |
if( !is_null($user_row) && $user_row instanceof Zend_Db_Table_Row ) { |
16 |
$this->id = $user_row->id; |
17 |
$this->email = $user_row->email; |
18 |
$this->password = $user_row->password; |
19 |
$this->salt = $user_row->salt; |
20 |
$this->date_created = $user_row->date_created; |
21 |
}
|
22 |
}
|
23 |
|
24 |
//magic function __set to set the
|
25 |
//attributes of the User model
|
26 |
public function __set($name, $value) |
27 |
{
|
28 |
switch($name) { |
29 |
case 'id': |
30 |
//if the id isn't null, you shouldn't update it!
|
31 |
if( !is_null($this->id) ) { |
32 |
throw new Exception('Cannot update User\'s id!'); |
33 |
}
|
34 |
break; |
35 |
case 'date_created': |
36 |
//same goes for date_created
|
37 |
if( !is_null($this->date_created) ) { |
38 |
throw new Exception('Cannot update User\'s date_created'); |
39 |
}
|
40 |
break; |
41 |
case 'password': |
42 |
//if you're updating the password, hash it first with the salt
|
43 |
$value = sha1($value.$this->salt); |
44 |
break; |
45 |
}
|
46 |
|
47 |
//set the attribute with the value
|
48 |
$this->$name = $value; |
49 |
}
|
50 |
|
51 |
public function __get($name) |
52 |
{
|
53 |
return $this->$name; |
54 |
}
|
55 |
}
|
- __construct($user_row): toma en un objeto
Zend_Db_Table_Rowopcional, que representa una fila de la base de datos, y asigna los datos a sí mismo - __set($name, $value): una función mágica que se encarga de configurar todos los atributos para el modelo.
- __get($name): una función mágica que se encarga de obtener un atributo del modelo.
¡Vamos a probarlo! Si seguiste el tutorial anterior, debes tener un archivo IndexController.php. Ábrelo y pon este código que crea un nuevo usuario:
1 |
public function indexAction() |
2 |
{
|
3 |
// action body
|
4 |
$this->view->current_date_and_time = date('M d, Y - H:i:s'); |
5 |
|
6 |
$user = new Application_Model_User(); |
7 |
$user->email = 'nikko@test.local'; |
8 |
$user->salt = sha1(time()); |
9 |
$user->password = 'test'; |
10 |
$user->date_created = date('Y-m-d H:i:s'); |
11 |
|
12 |
$user_mapper = new Application_Model_UserMapper(); |
13 |
$user_mapper->save($user); |
14 |
}
|
Ahora ve a http://thenextsocial.local/. Una vez que se cargue, verifica la tabla thenextsocial.user en MySQL y si todo funcionó, ¡deberías tener un nuevo registro de User!



¡Un nuevo registro de User!
Ahora, intentemos actualizar este registro. Vuelve a IndexController.php y actualiza el código para que coincida con lo siguiente:
1 |
public function indexAction() |
2 |
{
|
3 |
// action body
|
4 |
$this->view->current_date_and_time = date('M d, Y - H:i:s'); |
5 |
|
6 |
$user_mapper = new Application_Model_UserMapper(); |
7 |
$user = $user_mapper->getUserById(1); |
8 |
$user->email = 'new_email@test.local'; |
9 |
$user_mapper->save($user); |
10 |
}
|
Verifica la tabla MySQL nuevamente, ¡y deberías ver que el correo electrónico para el registro se ha actualizado!



Registro de User actualizado
¡Felicidades! ¡Has creado con éxito tu primer modelo en Zend Framework!
La Doctrina ORM
Introducción
Desde el sitio web de Doctrine ORM, http://doctrine-project.org/projects/orm:
Asignador relacional de objetos (ORM) para PHP que se encuentra sobre una capa de abstracción de base de datos potente (DBAL). Una de sus características clave es la opción de escribir consultas de base de datos en un dialecto SQL orientado a objetos patentado llamado Doctrine Query Language (DQL), inspirado en Hibernates HQL. Esto proporciona a los desarrolladores una poderosa alternativa a SQL que mantiene la flexibilidad sin requerir duplicación de código innecesaria.
Básicamente, la biblioteca ORM de Doctrine abstrae la mayoría, si no toda la implementación del Modelo para una aplicación. Algunas de las increíbles ventajas que descubrí al usar Doctrine con Zend Framework son:
- Facilidad de usar, garantizado para reducir tu tiempo de desarrollo a la mitad
- Funciona igual de bien con diferentes tipos de DB's con muy pocos ajustes necesarios. Por ejemplo, Doctrine me hizo muy fácil portar una aplicación en la que trabajé antes de usar MySQL para MSSQL
- Una herramienta de scaffolding, llamada
Doctrine_Cli, que crea modelos a partir de la base de datos muy rápidamente.
Para comenzar, debes descargar la biblioteca ORD de Doctrine primero desde su sitio. Estaré usando la versión 1.2.4. Ve a http://www.doctrine-project.org/projects/orm/1.2/download/1.2.4 y haz clic en el enlace Descargar paquete 1.2.4.



Descargando Doctrine ORM
Una vez descargado, ábrelo y extrae el contenido. Dentro, deberías ver la carpeta Doctrine-1.2.4 y un archivo package.xml. Ve dentro de la carpeta y deberías ver una carpeta Doctrine, un archivo Doctrine.php y un archivo LICENSE.

Descargar contenidos
Copia la carpeta Doctrine y el archivo Doctrine.php y colócalo dentro de la ruta de inclusión de tu instalación de PHP. Si recuerdas cómo configuramos Zend Framework en el último tutorial, es muy probable que sea la misma carpeta en la que colocaste los archivos de la biblioteca Zend.

Librería Doctrine con la biblioteca de Zend en include_path de PHP
¡Ahora estamos listos para integrarlo con nuestra aplicación Zend! Comienza por abrir application.ini nuevamente y agrega la siguiente configuración dentro del bloque [development : production]:
1 |
;Doctrine settings |
2 |
resources.doctrine.connection_string = "mysql://[replace with db username]:[replace with db password]@localhost/thenextsocial" |
3 |
resources.doctrine.models_path = APPLICATION_PATH "/models" |
4 |
resources.doctrine.generate_models_options.pearStyle = true |
5 |
resources.doctrine.generate_models_options.generateTableClasses = true |
6 |
resources.doctrine.generate_models_options.generateBaseClasses = true |
7 |
resources.doctrine.generate_models_options.classPrefix = "Model_" |
8 |
resources.doctrine.generate_models_options.baseClassPrefix = "Base_" |
9 |
resources.doctrine.generate_models_options.baseClassesDirectory = |
10 |
resources.doctrine.generate_models_options.classPrefixFiles = false |
11 |
resources.doctrine.generate_models_options.generateAccessors = false |
Ahora que lo hemos configurado, abre el archivo Bootstrap.php de la aplicación. Encontrarás esto dentro de la carpeta thenextsocial/application.
Bootstrap.php
Bootstrap.php nos permite inicializar los recursos que podríamos usar en nuestra aplicación. Básicamente, todos los recursos que necesitamos para crear instancias deben ubicarse aquí. Nos adentraremos en esto con más detalle más adelante en la serie, pero por ahora, todo lo que necesitas saber es que el formato de los métodos aquí es el siguiente:
1 |
protected function _initYourResource() |
2 |
{
|
3 |
//Do your resource setup here
|
4 |
}
|
Dentro del archivo Bootstrap.php, agrega el siguiente código para inicializar Doctrine con la aplicación:
1 |
<?php
|
2 |
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap |
3 |
{
|
4 |
public function _initDoctrine() |
5 |
{
|
6 |
//require the Doctrine.php file
|
7 |
require_once 'Doctrine.php'; |
8 |
|
9 |
//Get a Zend Autoloader instance
|
10 |
$loader = Zend_Loader_Autoloader::getInstance(); |
11 |
|
12 |
//Autoload all the Doctrine files
|
13 |
$loader->pushAutoloader(array('Doctrine', 'autoload')); |
14 |
|
15 |
//Get the Doctrine settings from application.ini
|
16 |
$doctrineConfig = $this->getOption('doctrine'); |
17 |
|
18 |
//Get a Doctrine Manager instance so we can set some settings
|
19 |
$manager = Doctrine_Manager::getInstance(); |
20 |
|
21 |
//set models to be autoloaded and not included (Doctrine_Core::MODEL_LOADING_AGGRESSIVE)
|
22 |
$manager->setAttribute( |
23 |
Doctrine::ATTR_MODEL_LOADING, |
24 |
Doctrine::MODEL_LOADING_CONSERVATIVE); |
25 |
|
26 |
//enable ModelTable classes to be loaded automatically
|
27 |
$manager->setAttribute( |
28 |
Doctrine_Core::ATTR_AUTOLOAD_TABLE_CLASSES, |
29 |
true
|
30 |
);
|
31 |
|
32 |
//enable validation on save()
|
33 |
$manager->setAttribute( |
34 |
Doctrine_Core::ATTR_VALIDATE, |
35 |
Doctrine_Core::VALIDATE_ALL |
36 |
);
|
37 |
|
38 |
//enable sql callbacks to make SoftDelete and other behaviours work transparently
|
39 |
$manager->setAttribute( |
40 |
Doctrine_Core::ATTR_USE_DQL_CALLBACKS, |
41 |
true
|
42 |
);
|
43 |
|
44 |
//not entirely sure what this does :)
|
45 |
$manager->setAttribute( |
46 |
Doctrine_Core::ATTR_AUTO_ACCESSOR_OVERRIDE, |
47 |
true
|
48 |
);
|
49 |
|
50 |
//enable automatic queries resource freeing
|
51 |
$manager->setAttribute( |
52 |
Doctrine_Core::ATTR_AUTO_FREE_QUERY_OBJECTS, |
53 |
true
|
54 |
);
|
55 |
|
56 |
//connect to database
|
57 |
$manager->openConnection($doctrineConfig['connection_string']); |
58 |
|
59 |
//set to utf8
|
60 |
$manager->connection()->setCharset('utf8'); |
61 |
|
62 |
return $manager; |
63 |
}
|
64 |
|
65 |
protected function _initAutoload() |
66 |
{
|
67 |
// Add autoloader empty namespace
|
68 |
$autoLoader = Zend_Loader_Autoloader::getInstance(); |
69 |
$resourceLoader = new Zend_Loader_Autoloader_Resource(array( |
70 |
'basePath' => APPLICATION_PATH, |
71 |
'namespace' => '', |
72 |
'resourceTypes' => array( |
73 |
'model' => array( |
74 |
'path' => 'models/', |
75 |
'namespace' => 'Model_' |
76 |
)
|
77 |
),
|
78 |
));
|
79 |
// Return it so that it can be stored by the bootstrap
|
80 |
return $autoLoader; |
81 |
}
|
82 |
}
|
La configuración que he hecho aquí se basa en un Script que encontré en el pasado en http://dev.juokaz.com, que fue mantenido por Juozas Kaziukenas, uno de los miembros del equipo en el proyecto Doctrine. Lamentablemente, el blog ya se ha cerrado, por lo que no podré enlazarlo más. Además, ten en cuenta que tenemos otro método llamado _initAutoload(). Básicamente, esto inicializa el autocargador Zend, que cargará automáticamente todos los modelos generados dentro de la carpeta models. Esto nos ahorra la molestia de tener que incluir estos archivos uno por uno.
A continuación, debemos configurar el script CLI de Doctrine que usaremos para generar modelos automáticamente desde la base de datos. Vuelve a la carpeta thenextsocial y crea una carpeta llamada scripts. Dentro, crea un archivo llamado doctrine-cli.php y coloca lo siguiente dentro:
1 |
<?php
|
2 |
/**
|
3 |
* Doctrine CLI script
|
4 |
*
|
5 |
* @author Juozas Kaziukenas (juozas@juokaz.com)
|
6 |
*/
|
7 |
|
8 |
define('APPLICATION_ENV', 'development'); |
9 |
define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application')); |
10 |
|
11 |
set_include_path(implode(PATH_SEPARATOR, array( |
12 |
realpath(APPLICATION_PATH . '/../library'), |
13 |
'./', |
14 |
get_include_path(), |
15 |
)));
|
16 |
|
17 |
require_once 'Zend/Application.php'; |
18 |
|
19 |
// Create application, bootstrap, and run
|
20 |
$application = new Zend_Application( |
21 |
APPLICATION_ENV, |
22 |
APPLICATION_PATH . '/configs/application.ini' |
23 |
);
|
24 |
|
25 |
$application->getBootstrap()->bootstrap('doctrine'); |
26 |
|
27 |
// set aggressive loading to make sure migrations are working
|
28 |
Doctrine_Manager::getInstance()->setAttribute( |
29 |
Doctrine::ATTR_MODEL_LOADING, |
30 |
Doctrine_Core::MODEL_LOADING_AGGRESSIVE |
31 |
);
|
32 |
|
33 |
$options = $application->getBootstrap()->getOptions(); |
34 |
|
35 |
$cli = new Doctrine_Cli($options['doctrine']); |
36 |
|
37 |
$cli->run($_SERVER['argv']); |
Vuelve a la carpeta models y elimina los archivos del modelo User que tenemos allí (si lo deseas, puedes moverlo a otra ubicación primero, pero no debería estar dentro de la carpeta). A continuación, abre tu símbolo del sistema (o terminal), cd a la carpeta scripts y escribe el siguiente comando:
1 |
php doctrine-cli.php |
Deberías ver algo como esto:



Salida esperada de Doctrine CLI
Si todo salió bien, ¡comencemos a crear algunos modelos! Escribe lo siguiente:
1 |
php doctrine-cli.php generate-models-db |
Ahora deberías ver el siguiente resultado:



Generando modelos usando Doctrine CLI
Si lo hiciste, revisa nuevamente la carpeta de tus modelos y deberías ver algunos modelos nuevos como User y UserSettings que han sido generados por Doctrine.

¡Modelos Generados!
Si abres los archivos, no verás mucho dentro. La mayoría de los códigos para los modelos están resumidos en la biblioteca Doctrine. Al extender la clase Doctrine_Record, tenemos disponibles para nosotros muchos métodos precompilados de la biblioteca. Abre IndexController.php de nuevo y reemplaza el código de prueba anterior con lo siguiente:
1 |
public function indexAction() |
2 |
{
|
3 |
// action body
|
4 |
$this->view->current_date_and_time = date('M d, Y - H:i:s'); |
5 |
|
6 |
$user = new Model_User(); |
7 |
$user->email = 'new_user_2@test.local'; |
8 |
$user->password = 'test'; |
9 |
$user->salt = sha1(time()); |
10 |
$user->date_created = date('Y-m-d H:i:s'); |
11 |
$user->save(); |
12 |
}
|
Una vez hecho esto, vuelve a http://thenextsocial.local. Si la página se carga, comprueba tu tabla MySQL y deberías ver que se ha insertado un nuevo registro de Usuario en User.



Registro de usuario a través de Doctrine ORM
Ahora, probemos cosas más complicadas: recuperar un usuario existente a través de métodos Doctrine precompilados y actualizarlo. Actualiza el código para que se vea así:
1 |
public function indexAction() |
2 |
{
|
3 |
// action body
|
4 |
$this->view->current_date_and_time = date('M d, Y - H:i:s'); |
5 |
|
6 |
$user = Doctrine_Core::getTable('Model_User')->findOneByEmailAndPassword('new_user_2@test.local', 'test'); |
7 |
$user->password = 'new_password'; |
8 |
$user->save(); |
9 |
}
|
El método findOneByEmailAndPassword() es un método conveniente creado previamente por Doctrine para facilitar la selección de una fila de la base de datos. Lo mejor de esto es que puedes combinar columnas de tablas en el método. Por ejemplo, puedes llamar a algo como findOneByIdAndNameAndPasswordAndSalt() y ¡seguirá funcionando!



Actualización de un registro de usuario a través de Doctrine ORM
Hay mucho más que podemos hacer ahora que usamos Doctrine ORM para la implementación del Modelo de la aplicación. Cosas como el lenguaje de consulta de Doctrine (DQL), aprovechando las relaciones del modelo y generando modelos desde YAML. Para el resto de la serie, usaremos Doctrine ORM para la implementación del Modelo de la aplicación, ¡así que en realidad aprenderás dos cosas de la serie en lugar de solo una! ¡Puntuación!
Conclusión
Por ahora, deberías poder saber lo siguiente:
- Qué son los “modelos”
- Conectando tu aplicación Zend a una base de datos
- Cómo funciona
application.ini - El patrón de diseño DAO
- Creación de modelos utilizando la herramienta ZF CLI
- Dónde descargar Doctrine ORM y cómo instalarlo
- Integración de Doctrine ORM con tu aplicación Zend
- El
Bootstrap.php - Generando modelos usando la herramienta CLI de Doctrine
- Uso básico de los modelos generados con Doctrine.
Ahora que sabes cómo implementar los Modelos en una aplicación potenciada de Zend Framework, tienes el conocimiento para crear sitios web dinámicos. Intenta jugar con la aplicación, crea algunos Controladores y modelos nuevos que lean, actualicen, guarden y eliminen de la base de datos.
En nuestro próximo tutorial, aprenderemos sobre algunos componentes de uso frecuente de la biblioteca de Zend Framework, los componentes Zend_Auth y Zend_Acl y compilaremos el sistema de autenticación de TheNextSocial.
Hasta entonces, permanece atento y recuerda que todo el código utilizado aquí está disponible en el repositorio GitHub de TheNextSocial.






