Cuando se manejan tablas en Zend Framework 1, muchas veces nos complicamos la vida al no querer o no saber usar las funcionalidades que nos presta el propio framework.
Con Zend Framework 1, podemos definir la dependencias de las tablas al crear un modelo de base de datos.
Zend framework Zend_Db_Table_Abstract implementa los métodos necesarios para conseguir un manejo de relaciones entre filas de forma sencilla y, sobre todo rápida.
Los métodos que usaremos serán:
  • "findParentRow": nos permite obtener el objeto padre en una relación directa.
  • "findDependentRowset": nos permite obtener los objetos hijos de una relación directa.
  • "findManyToManyRowset": nos permite obtener tantos los objetos padres como hijos a través de una tabla intermedia de relación.
A partir de un objeto, Zend Framework se encargará de obtener los relacionados que indiquemos en su definición. Esto resulta muy útil, para estructura de datos relacionales (lógico). Si eres de los que toman sus datos relacionales usando complicadas consultas con INNER JOINS o sub consultas, este es tu post.

REQUISITOS PREVIOS

Antes de comenzar, procedemos a instalar o incluir el esqueleto de la aplicación. Recomiendo bajar la versión de 1.12.11 de Zend Framework, y crear el esqueleto como un nuevo proyecto desde Zend Studio desde File > New > Project...

En Examples elegimos Zend Framework > Zend Framework Example Project. Una vez creado, nos movemos a la carpeta de Library y copiamos el contenido de zip bajado anteriormente en esta misma carpeta, dado que el ejemplo no incluye la librería, sólo el esqueleto.

Para este ejemplo, uso XAMPP para Windows y directamente he creado el ejemplo en el htdocs para poder ejecutarlo directamente. Recordar correr tanto el PHP como el MYSQL.

Una vez creado el projecto, procedemos a configurarlo un poco (mínimamente).

Para la base de datos, supongamos algo similar a 

Para ello, creamos un directorio dentro de application llamado "data" y añadimos un fichero llamado schema.sql con el siguiente contenido:



USE `usuarios`;::n::SET NAMES 'utf8';::n::DROP TABLE IF EXISTS `usuarios_has_profesion`;::n::DROP TABLE IF EXISTS `usuarios`;::n::CREATE TABLE IF NOT EXISTS `usuarios` (::n::::t::`idUsuarios` int(10) unsigned NOT NULL AUTO_INCREMENT,::n::::t::`nombre` varchar(45) NOT NULL,::n::::t::`apellidos` varchar(45) NOT NULL,::n::::t::PRIMARY KEY (`idUsuarios`)::n::) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;::n::INSERT INTO `usuarios` (`idUsuarios`, `nombre`, `apellidos`) VALUES::n::(1, 'Usuarios 1', 'Apellidos 1'),::n::(2, 'Usuarios 2', 'Apellidos 2'),::n::(3, 'Usuarios 3', 'Apellidos 3');::n::::n::DROP TABLE IF EXISTS `direcciones`;::n::CREATE TABLE IF NOT EXISTS `direcciones` (::n::::t::`idDirecciones` int(10) unsigned NOT NULL AUTO_INCREMENT,::n::::t::`idUsuarios` int(10) unsigned NOT NULL,::n::::t::`calle` varchar(45) NOT NULL,::n::::t::`direccion` varchar(45) NOT NULL,::n::::t::`numero` varchar(45) NOT NULL,::n::::t::`puerta` varchar(45) NOT NULL,::n::::t::`escalera` varchar(45) NOT NULL,::n::::t::`planta` varchar(45) NOT NULL,::n::::t::`portal` varchar(45) NOT NULL,::n::::t::`cp` varchar(45) NOT NULL,::n::::t::`provincia` varchar(45) NOT NULL,::n::::t::`localidad` varchar(45) NOT NULL,::n::::t::PRIMARY KEY (`idDirecciones`),::n::::t::KEY `fk_direc_idUser` (`idUsuarios`)::n::) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ;::n::INSERT INTO `direcciones` (`idDirecciones`, `idUsuarios`, `calle`, `direccion`, `numero`, `puerta`, `escalera`, `planta`, `portal`, `cp`, `provincia`, `localidad`) VALUES::n::(1, 1, 'C/', 'Cita', '1', '1', '', '1', 'C', '28001', 'Madrid', 'Madrid'),::n::(2, 1, 'C/', 'Jero', '1', '1', '', '', '', '28002', 'Madrid', 'Madrid'),::n::(3, 2, 'C/', 'Cita', '1', '1', '', '1', 'C', '28001', 'Madrid', 'Madrid');::n::::n::DROP TABLE IF EXISTS `profesion`;::n::CREATE TABLE IF NOT EXISTS `profesion` (::n::::t::`idProfesion` int(10) unsigned NOT NULL AUTO_INCREMENT,::n::::t::`nombre` varchar(255) NOT NULL,::n::::t::PRIMARY KEY (`idProfesion`)::n::) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;::n::INSERT INTO `profesion` (`idProfesion`, `nombre`) VALUES::n::(1, 'Informático'),::n::(2, 'Administrativo'),::n::(3, 'Transportista');::n::::n::DROP TABLE IF EXISTS `usuarios_has_profesion`;::n::CREATE TABLE IF NOT EXISTS `usuarios_has_profesion` (::n::::t::`idUsuarios` INT(10) UNSIGNED NOT NULL,::n::::t::`idProfesion` INT(10) UNSIGNED NOT NULL,::n::::t::PRIMARY KEY (`idUsuarios`, `idProfesion`),::n::::t::INDEX `fk_Usuarios_has_Profesion_Profesion1_idx` (`idProfesion` ASC),::n::::t::INDEX `fk_Usuarios_has_Profesion_Usuarios1_idx` (`idUsuarios` ASC),::n::::t::CONSTRAINT `fk_Usuarios_has_Profesion_Usuarios1`::n::::t::::t::FOREIGN KEY (`idUsuarios`)::n::::t::::t::REFERENCES `usuarios` (`idUsuarios`)::n::::t::::t::ON DELETE NO ACTION::n::::t::::t::ON UPDATE NO ACTION,::n::::t::CONSTRAINT `fk_Usuarios_has_Profesion_Profesion1`::n::::t::::t::FOREIGN KEY (`idProfesion`)::n::::t::::t::REFERENCES `profesion` (`idProfesion`)::n::::t::::t::ON DELETE NO ACTION::n::::t::::t::ON UPDATE NO ACTION) ENGINE = InnoDB;::n::INSERT INTO `usuarios_has_profesion` (`idUsuarios`, `idProfesion`) VALUES ::n::(1, 1),::n::(1, 3),::n::(2, 1),::n::(2, 2),::n::(2, 3);
Vemos, cuando definamos la estructura de la clase Bootstrap, que directamente la aplicación controlará el schema mediante la modificación de este fichero, sin embargo es necesario tener creado el schema con anterioridad (el schema lo llamaremos "usuarios").

Configuración

Supongamos la siguiente estructura de ficheros:

tocamos mínimamente los siguientes ficheros:

  • application/configs/application.ini
  • Bootstrap.php

application.ini

de aquí únicamente configuramos la base de datos (directamente en production si se quiere):


resources.db.adapter::t::::t::::t::=::t::"PDO_MYSQL"::n::resources.db.params.dbname::t::::t::=::t::"usuarios"::n::resources.db.params.host::t::::t::=::t::"localhost"::n::resources.db.params.username::t::::t::=::t::"nombre de usuario de la base de datos"::n::resources.db.params.password::t::::t::=::t::"password para la base de datos"::n::::n::resources.db.isDefaultTableAdapter::t::::t::=::t::true

Bootstrap.php

Añadimos y/o modificamos lo siguiente:

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap::n::{::n::::t::protected function _initDoctype()::n::::t::{::n::::t::::t::header('Content-Type: text/html; charset=UTF-8');::n::::t::::t::$this-::gt::bootstrap('view');::n::::t::::t::$view = $this-::gt::getResource('view');::n::::t::::t::$view-::gt::doctype('XHTML1_STRICT');::n::::t::}::n::::t::protected function _initViewSettings()::n::::t::{  ::n::::t::::t::$this-::gt::bootstrap('view');::n::            ::n::::t::::t::$this-::gt::_view = $this-::gt::getResource('view');::n::::t::::t:://Encoding Doctype::n::::t::::t::$this-::gt::_view-::gt::setEncoding('UTF-8');::n::::t::::t::$this-::gt::_view-::gt::doctype('XHTML1_STRICT');::n::            ::n::::t::::t::$this-::gt::_view-::gt::keywords = '';::n::::t::::t::$this-::gt::_view-::gt::descripcion = '';::n::::t::::t::$this-::gt::_view::n::::t::::t::-::gt::headMeta()::n::::t::::t::-::gt::appendHttpEquiv('Content-Type', 'text/html; charset=UTF-8');::n::::t::::t:://Título::n::::t::::t::$this-::gt::_view-::gt::headTitle('Usuarios');::n::::t::::t::$this-::gt::_view-::gt::headTitle()-::gt::setSeparator(' - ');::n::            ::n::::t::}::n::::t::protected function _initView()::n::::t::{::n::::t::::t:://texto de la home::n::::t::::t::date_default_timezone_set('Europe/Madrid');::n::::t::::t::setlocale(LC_TIME, "es_ES");::n::::t::::t::// Inicializamos la vista::n::::t::::t::$view = new Zend_View();::n::::t::::t::$view-::gt::headTitle('Usuarios');::n::    ::n::::t::::t::$view-::gt::headMeta()-::gt::appendHttpEquiv('Content-Type',::n::::t::::t::'text/html; charset=utf-8');::n::    ::n::::t::::t::// Lo añadimos al view render::n::::t::::t::$viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper(::n::::t::::t::::t::'ViewRenderer'::n::::t::::t::);::n::::t::::t::$viewRenderer-::gt::setView($view);::n::::n::::t::::t::return $view;::n::::t::}::n::::t::protected function _initDb()::n::::t::{::n::::t::::t::$resource  =   $this-::gt::getPluginResource('db');::n::::t::::t::$db        =   $resource-::gt::getDbAdapter();        ::n::::t::::t:://Hago debug de las querys::n::::t::::t::if ('production' !== $this-::gt::getEnvironment()) {::n::    ::n::::t::::t::::t:://$this-::gt::bootstrap('db');::n::    ::n::::t::::t::::t::$profiler = new Zend_Db_Profiler_Firebug(::n::::t::::t::::t::::t::'All DB Queries'::n::::t::::t::::t::);::n::    ::n::::t::::t::::t::$profiler-::gt::setEnabled(true);::n::    ::n::::t::::t::::t::$db-::gt::setProfiler($profiler);::n::    ::n::::t::::t::}::n::::t::::t::if( file_exists( APPLICATION_PATH . "/data/schema.sql" ) ){::n::::t::::t::::t::$schema =   file_get_contents( APPLICATION_PATH . "/data/schema.sql" );                 ::n::::t::::t::::t::$db-::gt::query( $schema );::n::::t::::t::::t::rename( APPLICATION_PATH . "/data/schema.sql",APPLICATION_PATH . "/data/_schema.sql" );::n::::t::::t::::t:://IMPORTANTE, si es necesario modificar el SCHEMA, a partir de este punto, modificar "_schema.sql" y renombrarlo a "schema.sql"            ::n::::t::::t::}::n::::t::::t::$db-::gt::query( "SET NAMES 'utf8'" );::n::        ::n::::t::::t::Zend_Db_Table::setDefaultAdapter( $db );::n::::t::::t::Zend_Registry::set( 'db', $db );::n::::t::}::n::}

No entraré en detalles para explicar que hace cada cosa, sólo indicar que  lo importante para este ejercicio es lo que contiene "_initDb()". 

Modelos

Creamos los modelos para el manejo de usuarios y direcciones:

Usuarios.php


class Application_Model_DbTable_Usuarios extends Zend_Db_Table_Abstract::n::{::n::::t::protected $_name    =   'usuarios';    ::n::::t::protected $_index   =   'idUsuarios';::n::::t::protected $_alias   =   'u';::n::::t::// HIJOS::n::::t::protected $_dependentTables =   array(::n::::t::::t::'Application_Model_DbTable_Direcciones'::n::::t:::.t::);::n::    ::n::::t::public function fetchMe( $idUsuarios=null )::n::::t::{::n::::t::::t::// por ejemplo, trabajamos con un objeto de tipo "stdClass" que luego nos resulte más sencillo, incluso::n::::t::::t::// se podría realizar un cast a object la propia $fila, convirtiendo los datos en un objeto y añadiendo::n::::t::::t::// atributos, pero perdiendo las capacidades del Zend_Db_Table_Abstract::n::::t::::t::$retorno                =   new stdClass();::n::::t::::t::$retorno-::gt::fila          =   null;::n::::t::::t::$retorno-::gt::direcciones   =   array();::n::::t::::t::if( isset( $idUsuarios ) )::n::::t::::t::{::n::::t::::t::::t::$fila   =   $this-::gt::find( $idUsuarios )-::gt::current();::n::::t::::t::::t::if( $fila )::n::::t::::t::::t::{::n::::t::::t::::t::::t::$retorno-::gt::fila          =   $fila;                ::n::::t::::t::::t::::t::$retorno-::gt::direcciones   =   $fila-::gt::findDependentRowset( 'Application_Model_DbTable_Direcciones' );::n::::t::::t::::t::}::n::::t::::t::}::n::::t::::t::return $retorno;::n::::t::}::n::}


Direcciones.php


class Application_Model_DbTable_Direcciones extends Zend_Db_Table_Abstract::n::{::n::::t::protected $_name    =   'direcciones';    ::n::::t::protected $_index   =   'idDirecciones';::n::::t::protected $_alias   =   'd';::n::::t::// HIJOS::n::::t::protected $_dependentTables =   array(          ::n::::t::);::n::::t:://PADRES::n::::t::protected $_referenceMap    =   array(::n::::t::::t::"socio" =::gt::  array(::n::::t::::t::::t::'columns'       =::gt::  array('idUsuarios'),::n::::t::::t::::t::'refTableClass' =::gt::  'Application_Model_DbTable_Usuarios',::n::::t::::t::::t::'refColumns'    =::gt::  array('idUsuarios')::n::::t::::t::)::n::::t::);::n::    ::n::::t::public function fetchMe( $idDirecciones=null )::n::::t::{::n::::t::::t::// por ejemplo, trabajamos con un objeto de tipo "stdClass" que luego nos resulte más sencillo, incluso::n::::t::::t::// se podría realizar un cast a object la propia $fila, convirtiendo los datos en un objeto y añadiendo::n::::t::::t::// atributos, pero perdiendo las capacidades del Zend_Db_Table_Abstract::n::::t::::t::$retorno                =   new stdClass();::n::::t::::t::$retorno-::gt::fila          =   null;::n::::t::::t::$retorno-::gt::usuario      =    array();::n::::t::::t::if( isset( $idDirecciones ) )::n::::t::::t::{::n::::t::::t::::t::$fila   =   $this-::gt::find( $idDirecciones )-::gt::current();::n::::t::::t::::t::if( $fila )::n::::t::::t::::t::{::n::::t::::t::::t::::t::$retorno-::gt::fila       =   $fila;::n::::t::::t::::t::::t::$retorno-::gt::usuario    =   $fila-::gt::findParentRow( 'Application_Model_DbTable_Usuarios' );::n::::t::::t::::t::}::n::::t::::t::}::n::::t::::t::return $retorno;::n::::t::}::n::}

El manejo de dependencias se realiza con los atributos "$_dependentTables" y "$_referenceMap".

$_dependentTables hace referencia a los hijos del modelo y $_referenceMap a los padres.

Casos de uso

Pensemos en unos cuantos casos para ver cómo tratarlos:

  1. Supongamos que queremos obtener los datos de un registro de usuarios con la siguiente estructura en BBDD

    En este caso  todo registro de "Usuarios" podrá tener ninguna, una o varias direcciones, dado que la tabla "direcciones" se relaciona con "usuarios" a través del campo "idUsuarios".

    Con Zend Framework la tarea de recoger las direcciones de un registro de "usuarios" es muy sencilla.
    $retorno-::gt::direcciones::t::=::t::$fila-::gt::findDependentRowset( 'Application_Model_DbTable_Direcciones' );

    Esta línea se encarga de obtener la direcciones de un usuario, así de simple

    Para probarlo:
    En indexController, action index, añadimos lo siguiente:
    $usuarios::t::=::t::new Application_Model_DbTable_Usuarios();::n::print_r( $usuarios-::gt::fetchMe( 1 ) );die();

    al ejecutar la aplicación, obtendremos algo similar a esto:
    stdClass Object::n::(::n::::t::[fila] =::gt:: Zend_Db_Table_Row Object::n::::t::(::n::::t::::t::[_data:protected] =::gt:: Array::n::::t::::t::(::n::::t::::t::::t::[idUsuarios] =::gt:: 1::n::::t::::t::::t::[nombre] =::gt:: Usuarios 1::n::::t::::t::::t::[apellidos] =::gt:: Apellidos 1::n::::t::::t::)::n::::n::::t::::t::...::n::::n::::t::[direcciones] =::gt:: Zend_Db_Table_Rowset Object::n::::t::(::n::::t::::t::[_data:protected] =::gt:: Array::n::::t::::t::(::n::::t::::t::::t::[0] =::gt:: Array::n::::t::::t::::t::(::n::::t::::t::::t::::t::[idDirecciones] =::gt:: 1::n::::t::::t::::t::::t::[idUsuarios] =::gt:: 1::n::::t::::t::::t::::t::[calle] =::gt:: C/::n::::t::::t::::t::::t::[direccion] =::gt:: Cita::n::::t::::t::::t::::t::[numero] =::gt:: 1::n::::t::::t::::t::::t::[puerta] =::gt:: 1::n::::t::::t::::t::::t::[escalera] =::gt:: ::n::::t::::t::::t::::t::[planta] =::gt:: 1::n::::t::::t::::t::::t::[portal] =::gt:: C::n::::t::::t::::t::::t::[cp] =::gt:: 28001::n::::t::::t::::t::::t::[provincia] =::gt:: Madrid::n::::t::::t::::t::::t::[localidad] =::gt:: Madrid::n::::t::::t::::t::)::n::::n::::t::::t::::t::[1] =::gt:: Array::n::::t::::t::::t::(::n::::t::::t::::t::::t::[idDirecciones] =::gt:: 2::n::::t::::t::::t::::t::[idUsuarios] =::gt:: 1::n::::t::::t::::t::::t::[calle] =::gt:: C/::n::::t::::t::::t::::t::[direccion] =::gt:: Jero::n::::t::::t::::t::::t::[numero] =::gt:: 1::n::::t::::t::::t::::t::[puerta] =::gt:: 1::n::::t::::t::::t::::t::[escalera] =::gt:: ::n::::t::::t::::t::::t::[planta] =::gt:: ::n::::t::::t::::t::::t::[portal] =::gt:: ::n::::t::::t::::t::::t::[cp] =::gt:: 28002::n::::t::::t::::t::::t::[provincia] =::gt:: Madrid::n::::t::::t::::t::::t::[localidad] =::gt:: Madrid::n::::t::::t::::t::)::n::::n::::t::::t::)::n::::n::::t::::t::...::n::::n::)

    Como vemos, obtenemos las dos direcciones del usuario 1 (el identificador 1).
    Podéis probar que ocurre si cambiamos el identificador a 2 o 3, etc.

  2. Partiendo de (1), supongamos que queremos obtener los datos de un registro de usuarios del caso anterior, pero que cumplan que el código postal sea 28002.
    Para ello, modificamos el modelo de "usuarios" y le añadimos al método "fetchMe() lo siguiente:
    $oModelDirecciones::t::=::t::new Application_Model_DbTable_Direcciones();::n::$retorno-::gt::fila::t::::t::=::t::$fila;::n::$select::t::::t::::t::=::t::$oModelDirecciones-::gt::select()-::gt::where( 'cp LIKE ?','28002' );::n::$retorno-::gt::direcciones::t::=::t::$fila-::gt::findDependentRowset( 'Application_Model_DbTable_Direcciones',null,$select );

    Usuarios.php


    class Application_Model_DbTable_Usuarios extends Zend_Db_Table_Abstract::n::{::n::::t::protected $_name    =   'usuarios';    ::n::::t::protected $_index	=	'idUsuarios';::n::::t::protected $_alias	=	'u';::n::::t::// HIJOS::n::::t::protected $_dependentTables	=	array(::n::::t::::t::'Application_Model_DbTable_Direcciones'::n::::t::);::n::    ::n::::t::public function fetchMe( $idUsuarios=null )::n::::t::{::n::::t::::t::// por ejemplo, trabajamos con un objeto de tipo "stdClass" que luego nos resulte más sencillo, incluso::n::::t::::t::// se podría realizar un cast a object la propia $fila, convirtiendo los datos en un objeto y añadiendo::n::::t::::t::// atributos, pero perdiendo las capacidades del Zend_Db_Table_Abstract::n::::t::::t::$retorno				=	new stdClass();::n::::t::::t::$retorno-::gt::fila			=	null;::n::::t::::t::$retorno-::gt::direcciones	=	array();::n::::t::::t::if( isset( $idUsuarios ) )::n::::t::::t::{::n::::t::::t::::t::$fila	=	$this-::gt::find( $idUsuarios )-::gt::current();::n::::t::::t::::t::if( $fila )::n::::t::::t::::t::{::n::::t::::t::::t::::t::$oModelDirecciones      =    new Application_Model_DbTable_Direcciones();::n::::t::::t::::t::::t::$retorno-::gt::fila			=	$fila;::n::::t::::t::::t::::t::$select                 =   $oModelDirecciones-::gt::select()-::gt::where( 'cp LIKE ?','28002' );::n::::t::::t::::t::::t::$retorno-::gt::direcciones	=	$fila-::gt::findDependentRowset( 'Application_Model_DbTable_Direcciones',null,$select );::n::::t::::t::::t::}::n::::t::::t::}::n::::t::::t::return $retorno;::n::::t::}::n::}
  3. Supongamos que queremos obtener los datos de un registro de usuarios con la siguiente estructura en BBDD

    En este caso todo registro de "Usuarios" podrá tener ninguna, una o varias profesiones y una profesión puede ser representada por uno o varios usuarios, dado que la tabla "profesion" se relaciona con "usuarios" a través de la tabla usuarios_has_profesion.

    Como vemos, aparecen dos entidades nuevas que debemos tener en cuenta "profesion" y "usuarios_has_profesion":

    Usuarioshasprofesion.php


    class Application_Model_DbTable_Usuarioshasprofesion extends Zend_Db_Table_Abstract::n::{::n::::t::protected $_name    =   'usuarios_has_profesion';    ::n::::t::protected $_alias   =   'uhp';::n::::t::// HIJOS::n::::t::protected $_dependentTables =   array();::n::::t::// PADRES::n::::t::protected $_referenceMap    =   array(::n::::t::::t::'Usuarios'          =::gt::  array(::n::::t::::t::::t::'columns'       =::gt::  array('idUsuarios'),::n::::t::::t::::t::'refTableClass' =::gt::  'Application_Model_DbTable_Usuarios',::n::::t::::t::::t::'refColumns'    =::gt::  array('idUsuarios')::n::::t::::t::),::n::::t::::t::'Profesiones'       =::gt::  array(::n::::t::::t::::t::'columns'       =::gt::  array('idProfesion'),::n::::t::::t::::t::'refTableClass' =::gt::  'Application_Model_DbTable_Profesion',::n::::t::::t::::t::'refColumns'    =::gt::  array('idProfesion')::n::::t::::t::)::n::::t::);::n::}

    Como vemos, al definir "$_referenceMap", le añadimos dos índices que serán los que usaremos como reglas posteriormente al hacer uso del método "findManyToManyRowset". Es importante es definir bien las columnas y las referencias; en el caso de "Usuarios", la columna en la tabla de relación es "idUsuario" (pero podría haberse llamado "Usuarios_idUsuario" para diferenciarlo del campo de la tabla de "usuarios"), mientras que el campo al que hace referencia es "idUsuarios" de la tabla de "usuarios".

    Creamos el modelo de profesiones:

    Profesion.php


    class Application_Model_DbTable_Profesion extends Zend_Db_Table_Abstract::n::{::n::::t::protected $_name    =   'profesion';    ::n::::t::protected $_index   =   'idProfesion';::n::::t::protected $_alias   =   'p';::n::::t::// HIJOS::n::::t::protected $_dependentTables =   array(::n::::t::::t::"Application_Model_DbTable_Usuarioshasprofesion"::n::::t::);::n::    ::n::::t::public function fetchMe( $idProfesion=null )::n::::t::{::n::::t::::t::// por ejemplo, trabajamos con un objeto de tipo "stdClass" que luego nos resulte más sencillo, incluso::n::::t::::t::// se podría realizar un cast a object la propia $fila, convirtiendo los datos en un objeto y añadiendo::n::::t::::t::// atributos, pero perdiendo las capacidades del Zend_Db_Table_Abstract::n::::t::::t::$retorno                =   new stdClass();::n::::t::::t::$retorno-::gt::fila          =   null;::n::::t::::t::$retorno-::gt::usuarios      =    array();::n::::t::::t::if( isset( $idProfesion ) )::n::::t::::t::{::n::::t::::t::::t::$fila   =   $this-::gt::find( $idProfesion )-::gt::current();::n::::t::::t::::t::if( $fila )::n::::t::::t::::t::{::n::::t::::t::::t::::t::$retorno-::gt::fila      =   $fila;::n::::t::::t::::t::::t::$retorno-::gt::usuarios  =   $fila-::gt::findManyToManyRowset( 'Application_Model_DbTable_Usuarios','Application_Model_DbTable_Usuarioshasprofesion',null,'Usuarios' );::n::::t::::t::::t::}::n::::t::::t::}::n::::t::::t::return $retorno;::n::::t::}::n::}

    Vemos que para obtener los usuarios usamos el método "findManyToManyRowset":
    ::t::Zend_Db_Table_Row_Abstract::findManyToManyRowset(::n::::t::::t::string|Zend_Db_Table_Abstract $matchTable, ::n::::t::::t::string|Zend_Db_Table_Abstract $intersectionTable, ::n::::t::::t::$callerRefRule, ::n::::t::::t::$matchRefRule, ::n::::t::::t::Zend_Db_Table_Select $select::n::::t::) 

    retorna un resultado de Zend_Db_Table_Rowset_Abstract Query:
    • El primer parámetro es el nombre de la clase o un objeto de la tabla que queremos obtener datos.
    • El segundo parámetro es el nombre de la clase o un objeto de la tabla intermedia o de relación.
    • El tercer parámetro es la regla de relación desde la interseción hasta la tabla origen (parámetro opcional).
    • El cuarto parámetro es la regla de relación desde la intersección hasta la tabla destino (parámetro opcional).
    • El quito parámetro es un objeto Zend_Db_Table_Select que filtra la búsqueda en la relación (dónde el alias de la intersección es "i" (de intersection) y el alias de la tabla de destino es "m" (de match)) (parámetro opcional).

    Para finalizar, es necesario indicar el nuevo hijo desde el modelo de usuario:
    ::t::protected $_dependentTables =   array(::n::::t::::t::'Application_Model_DbTable_Direcciones','Application_Model_DbTable_Usuarioshasprofesion'::n::::t::);

    y modificamos el método "fetchMe" de usuarios para añadir la búsqueda de profesiones:
    ::t::public function fetchMe( $idUsuarios=null )::n::::t::{::n::::t::::t::// por ejemplo, trabajamos con un objeto de tipo "stdClass" que luego nos resulte más sencillo, incluso::n::::t::::t::// se podría realizar un cast a object la propia $fila, convirtiendo los datos en un objeto y añadiendo::n::::t::::t::// atributos, pero perdiendo las capacidades del Zend_Db_Table_Abstract::n::::t::::t::$retorno                =   new stdClass();::n::::t::::t::$retorno-::gt::fila          =   null;::n::::t::::t::$retorno-::gt::direcciones   =   array();::n::::t::::t::$retorno-::gt::profesiones   =   array();::n::::t::::t::if( isset( $idUsuarios ) )::n::::t::::t::{::n::::t::::t::::t::$fila   =   $this-::gt::find( $idUsuarios )-::gt::current();::n::::t::::t::::t::if( $fila )::n::::t::::t::::t::{::n::::t::::t::::t::::t::$oModelDirecciones       =    new Application_Model_DbTable_Direcciones();::n::::t::::t::::t::::t::$retorno-::gt::fila          =   $fila;::n::::t::::t::::t::::t::$select                 =   $oModelDirecciones-::gt::select()-::gt::where( 'cp LIKE ?','28022' );::n::                ::n::::t::::t::::t::::t::$retorno-::gt::direcciones   =   $fila-::gt::findDependentRowset( 'Application_Model_DbTable_Direcciones' );::n::                ::n::::t::::t::::t::::t::$retorno-::gt::profesiones   =   $fila-::gt::findManyToManyRowset( 'Application_Model_DbTable_Profesion','Application_Model_DbTable_Usuarioshasprofesion',null,'Profesiones' );                ::n::::t::::t::::t::}::n::::t::::t::}::n::::t::::t::return $retorno;::n::::t::}

    Como se puede ver, en esta llamada, al buscar la profesión, se toma la regla de cuarto parámetro "Profesiones" para describir que queremos que nos marque las profesiones que contenga nuestro registro.

    Al igual que anteriormente, se puede realizar un filtro, ahora con el quinto parámetro de findManyToManyRowset:

    ::t::$oModelProfesiones      =   new Application_Model_DbTable_Profesion();::n::::t::$selecProfesiones       =   $oModelProfesiones-::gt::select()-::gt::order( 'm.nombre ASC' );::n::::t::$retorno-::gt::profesiones   =   $fila-::gt::findManyToManyRowset( 'Application_Model_DbTable_Profesion','Application_Model_DbTable_Usuarioshasprofesion',null,'Profesiones',$selecProfesiones );

    Esto nos ordenará las profesiones de un usuario por nombre.

Resumen

Con los métodos "findParentRow", "findDependentRowset" y "findManyToManyRowset", podemos relacionar un objeto con todas las dependencias que tengamos definidos sobre nuestros modelos.

Espero que sirva de ayuda y ya sabéis que si queréis aportar o comentar algo, dejad un comentario que es gratis.
Saludos!

PD. Dejo los ficheros aquí

El comentario será validado antes de mostrarse.