Load Model Action Helper

Por zsamer en Agosto 13, 2008

Hoy veremos una implementación de un Action Helper que permite cargar modelos en nuestros controladores u otra parte de nuestro sistema, la ventaja es que nos evita tener que hacer includes/require de éstos cada vez que los necesitemos y además nos da la posibilidad de guardar los modelos en un contenedor como singleton, si estas de acuerdo entonces sigue leyendo.

Este Helper nació de la idea de evitar el tener que hacer los típicos require y la necesidad de hacerlos singletons ya que en una petición usaba el mismo modelo en diversas partes del sistema, no solo en el controlador y tenia que estar creando las instancias a diestra y siniestra del modelo en cada lugar, lo que en realidad no tenia mucho sentido, un ejemplo típico es el modelo de usuario, post, etc, que no sólo se utiliza en el controlador/acción actual sino también en algún bloque de nuestro sitio (últimos post o usuarios online) mediante el helper view action o login, chequeos etc.

Vamos al grano, veamos como se utiliza:

PHP:
  1. $userModel = $this->_loadModel->getSingleton('admin/user');

Sencillo ¿no?, bueno expliquemos un poco, pasa por argumento un string que contiene el modulo y el modelo, separados por el "/", es decir que es el módulo admin y el modelo user.

Ahora veamos como tiene que ser nuestra clase modelo, siguiendo un poco con el estándar de Zend:

application\modules\admin\model\User.php

PHP:
  1. <?php
  2. class Admin_Model_User extends Zend_Db_Table_Abstract
  3. {
  4.     protected $_primary = 'user_id';
  5.  
  6.     protected $_name = 'admin_user';
  7.  
  8.     public function findAll()
  9.     {
  10.         return $this->fetchAll();
  11.     }
  12.  
  13.     public function findById($userId)
  14.     {
  15.         return $this->find($userId)->current();
  16.     }
  17. }

Se fijan, nuestra clase modelo User es Admin_Model_User donde Admin es el módulo, Model nos indica que es un modelo (namespace model dentro del módulo) y finalmente el nombre del modelo User. Nos damos cuenta que sigue el estándar de Zend nombrando nuestras clases según el namespace en que se encuentra (application\modules\admin\model\User.php).

Veamos un controlador en acción:

PHP:
  1. class Permissions_Admin_UserController extends Zend_Controller_Action
  2. {
  3.  
  4.     public function init()
  5.     {
  6.         $this->_flashMessenger = $this->_helper->getHelper('ZsamerFlashMessenger');
  7.         $this->_loadModel = $this->_helper->getHelper('LoadModel');
  8.     }
  9.  
  10.     public function indexAction()
  11.     {
  12.        $this->_forward('list');
  13.         }
  14.  
  15.     public function listAction()
  16.     {
  17.        $this->view->list = $this->_loadModel->getSingleton('admin/user')->findAll();
  18.         $this->view->title = $this->__('User List');
  19.         }
  20.  
  21.     public function editAction()
  22.     {
  23.         $id = $this->getRequest()->getParam('id');
  24.  
  25.         $user = null;
  26.  
  27.         if(null !== $id && is_numeric($id)){
  28.             $user = $this->_loadModel->getSingleton('admin/user')->findById($id);
  29.  
  30.             if (null === $user) {
  31.                 $this->_flashMessenger->addError($this->__('This user no longer exists'));
  32.                 $this->_redirect('*/*/');
  33.                 return;
  34.             }
  35.             $breadCrumbTitle = $this->__('Edit User');
  36.         } else {
  37.             $user = new stdClass();
  38.             $user->user_id = null;
  39.             $breadCrumbTitle = $this->__('Add new User');
  40.         }
  41.  
  42.         $this->view->form = $this->getForm($user);
  43.         $this->view->title = $breadCrumbTitle;
  44.     }
  45. }

Podemos observar lo simple que resulta obtener al usuario desde nuestro modelo User del modulo Admin, no necesitamos ningún require, ni crear instancias new User(), hay muchos pasos que nos simplificamos para dedicarnos de lleno a nuestra lógica de negocio.

Ahora toca ver lo más importante que es nuestro famoso y del que hemos hablado tanto Action Helper "Load Model":

PHP:
  1. <?php
  2. class Zsamer_Controller_Action_Helper_LoadModel extends Zend_Controller_Action_Helper_Abstract
  3. {
  4.     protected $_classNameCache = array();
  5.  
  6.     /**
  7.      * Retrieve class name by class group
  8.      *
  9.      * @param   string $groupType currently supported model.
  10.      * @param   string $classId slash separated class identifier, ex. group/class
  11.      * @param   string $groupRootNode optional config path for group config
  12.      * @return  string
  13.      */
  14.     public function getGroupedClassName($groupType, $classId, $groupRootNode=null)
  15.     {
  16.         if (empty($groupRootNode)) {
  17.             $groupRootNode = 'global/'.$groupType.'s';
  18.         }
  19.  
  20.         $classArr = explode('/', $classId);
  21.         $group = $classArr[0];
  22.         $class = !empty($classArr[1]) ? $classArr[1] : null;
  23.  
  24.         if (isset($this->_classNameCache[$groupRootNode][$group][$class])) {
  25.             return $this->_classNameCache[$groupRootNode][$group][$class];
  26.         }
  27.  
  28.         $includesFile = Core::getRoot() . '/modules/'; // Para Core::getRoot() ver Post Bootstrap Class
  29.  
  30.         $className = ucwords($group) . '_Model';
  31.         $moduleName = strtolower($group);
  32.         $includesFile .= $moduleName . '/model/';
  33.  
  34.         if (!empty($class)) {
  35.             if (strpos($class, '_') !== false) {
  36.                 $classNamesArr = explode('_', $class);
  37.                 $classNamesArrUcw = array_map('ucwords', $classNamesArr);
  38.                 $className .= '_'.join('_', $classNamesArrUcw);
  39.  
  40.                 $modelName = ucwords(array_pop($classNamesArr));
  41.                 $modelPath = join('/', $classNamesArr);
  42.                 $modelPath = empty($modelPath)? '': $modelPath . '/';
  43.                    
  44.             } else {
  45.                 $className .= '_'.ucwords($class);
  46.                 $modelName = ucwords($class);
  47.                 $modelPath = '';
  48.             }
  49.         }
  50.  
  51.         $includesFile .= $modelPath . $modelName . '.php';
  52.            
  53.         if (Zend_Loader::isReadable($includesFile)) {
  54.             require_once $includesFile;
  55.         }
  56.  
  57.         $this->_classNameCache[$groupRootNode][$group][$class] = $className;
  58.  
  59.         return $className;
  60.     }
  61.  
  62.     /**
  63.      * Retrieve modele class name
  64.      *
  65.      * @param   sting $modelClass
  66.      * @return  string
  67.      */
  68.     public function getModelClassName($modelClass)
  69.     {
  70.         if (strpos($modelClass, '/') === false) {
  71.             return $modelClass;
  72.         }
  73.  
  74.         return $this->getGroupedClassName('model', $modelClass);
  75.     }
  76.  
  77.     /**
  78.      * Get model class instance.
  79.      *
  80.      * Example in Action:
  81.      * $this->_loadModel->getModelInstance('catalog/product')
  82.      *
  83.      * Will instantiate Zend_Db_Table_Abstract
  84.      *
  85.      * @param string $modelClass
  86.      * @param array|object $constructArguments
  87.      * @return Zend_Db_Table_Abstract
  88.      */
  89.     public function getModelInstance($modelClass='', $constructArguments=array())
  90.     {
  91.         $className = $this->getModelClassName($modelClass);
  92.  
  93.         if (class_exists($className)) {
  94.             if(empty($constructArguments)){
  95.                 $model = new $className();
  96.             } else {
  97.                 $model = new $className($constructArguments);
  98.             }
  99.         } else {
  100.             //return false;
  101.             throw new Exception('Model class does not exist: ' . $modelClass . ' (' . $className . ')');
  102.         }
  103.         return $model;
  104.     }
  105.  
  106.     /**
  107.      * Get model class Singleton instance.
  108.      *
  109.      * Example in Action:
  110.      * $this->_loadModel->getSingletonModelInstance('catalog/product')
  111.      *
  112.      * Will instantiate Zend_Db_Table_Abstract
  113.      *
  114.      * @param string $modelClass
  115.      * @param array|object $constructArguments
  116.      * @return Zend_Db_Table_Abstract
  117.      */
  118.     public function getSingletonModelInstance($modelClass='', $constructArguments=array())
  119.     {
  120.         $registryKey = '_singleton/' . $modelClass;
  121.  
  122.         if (!Zend_Registry::isRegistered($registryKey)) {
  123.             Zend_Registry::set($registryKey, $this->getModelInstance($modelClass, $constructArguments));
  124.         }
  125.  
  126.         return Zend_Registry::get($registryKey);
  127.     }
  128.  
  129.     /**
  130.      * Get model class instance.
  131.      *
  132.      * Example in Action:
  133.      * $this->_loadModel->getModel('catalog/product')
  134.      *
  135.      * Will instantiate Zend_Db_Table_Abstract
  136.      *
  137.      * @param string $modelClass
  138.      * @param array|object $constructArguments
  139.      * @return Zend_Db_Table_Abstract
  140.      */
  141.     public function getModel($modelClass='', $constructArguments=array())
  142.     {
  143.         return $this->getModelInstance($modelClass, $constructArguments);
  144.     }
  145.  
  146.     /**
  147.      * Get model class Singleton instance.
  148.      *
  149.      * Example in Action:
  150.      * $this->_loadModel->getSingleton('catalog/product')
  151.      *
  152.      * Will instantiate Zend_Db_Table_Abstract
  153.      *
  154.      * @param string $modelClass
  155.      * @param array|object $constructArguments
  156.      * @return Zend_Db_Table_Abstract
  157.      */
  158.     public function getSingleton($modelClass='', $constructArguments=array())
  159.     {
  160.         return $this->getSingletonModelInstance($modelClass, $constructArguments);
  161.     }
  162. }

Como advertencia Core::getRoot() necesitan primero ver el Post Bootstrap Class, sino pueden remplazar a su gusto.

Ahora la gran pregunta, ¿cómo puedo obtener mis modelos desde un lugar de nuestro sistema que no sea el controlador?

Muy fácil, como ya saben Zend_Controller_Action_HelperBroker nos permite obtener nuestros Helper Action de manera estática, veamos como:

PHP:
  1. $userList = Zend_Controller_Action_HelperBroker::getStaticHelper('LoadModel')->getSingleton('admin/user')->findAll();
  2.  
  3. // OR
  4.  
  5. $loadModel = Zend_Controller_Action_HelperBroker::getStaticHelper('LoadModel');
  6.  
  7. $userList = $loadModel->getSingleton('admin/user')->findAll();

Sin embargo tengo una mejor manera de hacer esto y para eso necesitamos ir a un viejo articulo Bootstrap Class y agregar un par de métodos:

PHP:
  1. final class Core
  2. {
  3. /*...........*/
  4.  
  5.  
  6.     public static function getModel($modelClass, $arguments = array())
  7.     {
  8.         return self::getHelperAction('LoadModel')->getModelInstance($modelClass, $arguments);
  9.     }
  10.  
  11.     public static function getSingleton($modelClass = '', $arguments = array())
  12.     {
  13.         return self::getHelperAction('LoadModel')->getSingletonModelInstance($modelClass, $arguments);
  14.     }
  15.  
  16.     public static function getHelperAction($helper)
  17.     {
  18.         return Zend_Controller_Action_HelperBroker::getStaticHelper($helper);
  19.     }
  20. }

Con esto lo anterior se simplifica a:

PHP:
  1. $userList = Core::getSingleton('admin/user')->findAll();

Con esto logro tener a mis modelos singleton y accesible de donde yo quiera en mi sistema, impecable.

Eso es todo por ahora, espero que les sea útil.

Comentarios

6 Responses to “Load Model Action Helper”

  1. Pablo Morales on Agosto 13th, 2008 6:24 pm

    La solucin me parece demasiado extensa, no seria algo mas simple llamar a un metodo de un modelo asi ?
    Zsamer_Models::get(’nombre_del_modelo’, ‘nombre_del_metodo’, array(”parametros”));

    ?

    Saludos

  2. zsamer on Agosto 13th, 2008 8:27 pm

    También es una buena solución.

    Tu propuesta es super válida, la idea es que lo acomoden a su gusto, a mi me acomoda más un Helper Action.

    ;-)

  3. Pablo Morales on Agosto 14th, 2008 4:57 pm

    Me parece un buen argumento :P

    Por cierto, te falta hacer el Core que prometiste en el primer post del blog!!

    Suerte, y siempre esperando un post tuyo.

  4. zsamer on Agosto 14th, 2008 5:14 pm

    Hola Pablo, la clase Core la vimos en un post antiguo Post Bootstrap Class.

    class Core = class Bootstrap

    Se me olvido mencionar eso.

    es decir :
    $userList = Core::getSingleton(’admin/user’)->findAll();

    Igual a

    $userList = Bootstrap::getSingleton(’admin/user’)->findAll();

    Core::getRoot() = Bootstrap::getRoot()

    saludos.

  5. marcus on Febrero 3rd, 2009 11:15 pm

    VhySNf sk6skN2aP6Vvq18MdGcl

  6. ronal on Julio 8th, 2009 9:18 pm

    todo super
    muy bueno !!!!

Deja tu comentario




XHTML: puedes usar estas etiquetas: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>