2008-12-05 23 views
6

Estoy trabajando en un proyecto Zend Framework (1.7) con una estructura basada en la estructura de la aplicación de inicio rápido - controlador frontal, controladores de acción, modelos & que usan Zend_Db_Table para acceder a la base de datos. Uno de mis modelos principales depende de algunas uniones costosas para obtener su listado principal, así que estoy buscando usar Zend_Paginator para reducir el número de filas traídas de la base de datos. Mi problema es que Zend_Paginator solo viene con 4 adaptadores, ninguno de los cuales realmente parece ser adecuado para mí.Zend_Paginator borrando líneas MVC

  • matriz: La construcción de la matriz para alimentar a ZP implicaría ir a buscar todos los registros que es lo que estoy tratando de evitar
  • iterador: Un iterador tonto presentaría los mismos problemas como una matriz y uno inteligente siente que sería un ajuste pobre para el Modelo
  • DbSelect: Obtener un objeto DbSelect hasta el Controlador incómodamente uniría el Controlador al funcionamiento interno de mi DB (sin mencionar producir filas de resultados sin procesar) en lugar de objetos encapsulados)
  • DbTableSelect: lo mismo que DbSelect
  • adaptador Null: pasar todos los detalles de ida y vuelta de forma manual.

Pasar el paginador en el modelo parece que, también, violaría la separación de MVC. ¿El problema es que he construido incorrectamente mi modelo, que estoy siendo dogmático sobre el mantenimiento de la separación de MVC o me falta una forma limpia y elegante de unir todas las partes móviles?

Respuesta

2

Puede proporcionar una interfaz en sus modelos que acepta $current_page y $per_page parámetros y devuelve los datos de la página actual, así como establecer un objeto paginador.

De esta forma, todo el código de paginación se incluye en el modelo y puede usar los adaptadores Db sin sentir que ha roto el concepto.

Además, el controlador realmente no debe configurar el buscapersonas de todos modos ya que está en lo correcto al estar vinculado a los datos (y los modelos son para datos, no solo conexiones de base de datos).

class Model 
{ 
    //... 
    /** 
    * @return array Array in the form: array('paginator' => obj, 'resultset' => obj) 
    */ 
    public function getAll($where = array(), $current_page = null, $per_page = null); 
    //... 
} 
0

Bueno, no puedo darle una respuesta a sus inquietudes con el uso de DbSelect, pero encontré este fragmento de código (en los comentarios del blog de ibuildings) relacionado con la cuestión de reducir el número de filas extraídas. Puede ser útil para algunos lectores.

$select = $db->from('users')->order('name');  
$paginator = new Zend_Paginator(new Zend_Paginator_Adapter_DbSelect($select)); 
$paginator->setCurrentPageNumber($this->_getParam('page', 1)); 
$paginator->setItemCountPerPage(25); 
$this->view->paginator = $paginator; 
0

Una consideración importante a tener en cuenta cuando se trabaja con MVC es que el modelo es para toda la lógica de dominio, mientras que el controlador es para la lógica de negocio. Una regla general es que los modelos no deberían tener conocimiento de la interfaz (controladores o vistas), pero no necesitan ser simples accesadores de DB. Para ser tan portátil como sea posible, tampoco deberían saber nada sobre el formato o las propiedades de visualización (a menos que sea parte de la lógica del dominio).

De hecho, toda la lógica que manipula la lógica de dominio debe estar en el modelo y no en los controladores. El controlador debe pasar información de la interfaz, transformándola según sea necesario, al modelo y seleccionar qué vista mostrar/actualizar.Si no tiene nada que ver con la interfaz, podría representarse mejor en un modelo que en un controlador, ya que permitiría una mayor reutilización del código si decide cambiar el emparejamiento entre el controlador y la vista más tarde.

Lo ideal es que su modelo proporcione una interfaz para acceder a la información que necesita. Cómo se implementa eso detrás de esa interfaz no es una preocupación de MVC, siempre y cuando el modelo no tenga conocimiento de la parte de VC de MVC. Si eso significa pasar alrededor de un objeto de paginador, eso no es una violación directa de los principios de MVC, aunque si el paginador tiene algo que ver con la renderización (lo siento, no conozco a Zend), podría ser mejor pasar una interfaz de ello (que le faltan los métodos de renderizado), haga que el modelo lo manipule/complete, y luego vuelva a pasarlo. De esta forma, no se generará un código de representación desde el modelo, y podría reemplazar la implementación del paginador si decidiera convertir su aplicación en una aplicación de consola más tarde (o agregue una interfaz de API de algún tipo).

0

Si usa el adaptador DbSelect simplemente puede pasar el resultado y esto ayuda a mantener la separación. Así que en su controlador:

$items = new Items();//setup model as usual in controller 
$this->view->paginator = Zend_Paginator::factory($items->getAll()); //initialize the pagination in the view NB getAll is just a custom function to encapsulate my query in the model that returns a Zend_Db_Table_Rowset 
$this->view->paginator->setCurrentPageNumber($page); //$page is just the page number that could be passed in as a param in the request 
$this->view->paginator->setView($this->view); 

En la vista que se puede acceder a los datos a través del paginador

<?php foreach($this->paginator as $item):?> 
<?=$item->someProperty?> 
<?php endforeach;?> 

Este es un ejemplo simplificado (I también configurar al estilo de desplazamiento predeterminado y vista por defecto parcial en el arranque), pero creo que configurarlo en el controlador no está mal porque los datos recuperados del modelo están colocados en la vista por el controlador de todos modos y esta implementación hace uso del conjunto de resultados NO del modelo.

+1

Pero este no parece un desperdicio? Una llamada a getAll() - una llamada costosa que puede devolver miles o millones de registros - y luego ignorar todos menos los 20 o menos que pueda necesitar para una página de datos mostrados? –

2

En la actualidad existe un método setFilter para Zend_Paginator que le permite cargar los datos del objeto de fila a cualquier objeto modelo que desea:

class Model_UserDataMapper { 
    public function getUsers($select, $page) { 
     $pager = Zend_Paginator::factory($select); 
     $pager->setItemCountPerPage(10) 
        >setCurrentPageNumber($page) 
        ->setFilter(new Zend_Filter_Callback(array($this,'getUserObjects'))); 
    } 

    public function getUserObjects($rows) { 
     $users = array(); 

     foreach($rows as $row) { 
      $user = new Model_User($row->toArray()); 

      $users[] = $user; 
     } 

     return $users; 
    } 
} 
0

También se podría implementar el Zend_Paginator_Adapter_Interface directamente o extender Zend_Paginator_Adapter_DbSelect en cualquier modelo que necesita para apoyar la paginación.

De esta manera, el modelo no sabe nada directamente sobre View, Controller o incluso Zend_Paginator, pero se puede usar directamente con Zend_Paginator donde sea que tenga más sentido.

class ModelSet extends Zend_Paginator_Adapter_DbSelect 
{ 
    public function __construct(...) 
    { 
     // Create a new Zend_Db_Select ($select) representing the desired 
     // data set using incoming criteria 
     parent::__construct($select); 
    } 
    ... 
} 

con algo como esto, se puede crear una instancia directamente un buscapersonas utilizando una instancia de esta clase dondequiera que tiene más sentido:

$modelSet = new ModelSet(...); 
... 
$pager = new Zend_Paginator($modelSet); 
$pager->setItemCountPerPage(...); 
$pager->setCurrentPageNumber(...); 
... 
// The first time the record set is actually retrieved will be at the beginning 
// of the first traversal 
foreach ($pager as $record) 
{ 
    // ... do stuff with the record ... 
} 

Ahora, puede utilizar esta clase como la clase base para cualquier "Modelo" que sea un conjunto.

1

I realmente necesitaba una solución en la que podría utilizar un método de clase Zend_Db_Table como el recurso para mi adaptador paginador, en lugar de una matriz o un objeto Zend_Db_Select.

Este tipo de modelado avanzado no es compatible con los adaptadores estándar para Zend_Paginator. Seguí adelante y arreglé esto para todos los que están desesperados por una respuesta, como yo.

<?php 

    /* /zend/Paginator/Adapter/DbTableMethod.php */  
    class Zend_Paginator_Adapter_DbTableMethod implements Zend_Paginator_Adapter_Interface { 

     protected $_class; 
     protected $_method; 
     protected $_parameters; 
     protected $_rowCount = null; 

     public function __construct($class, $method, array $parameters = array()){ 

     $reflectionClass = new ReflectionClass($class); 
     $reflectionMethod = $reflectionClass->getMethod($method); 
     $reflectionParameters = $reflectionMethod->getParameters(); 

     $_parameters = array(); 

     foreach ($reflectionParameters as $reflectionParameter){ 

      $_parameters[$reflectionParameter->name] = ($reflectionParameter->isDefaultValueAvailable()) ? $reflectionParameter->getDefaultValue() : null; 

     }  

     foreach ($parameters as $parameterName => $parameterValue){ 

      if (array_key_exists($parameterName, $_parameters)) $_parameters[$parameterName] = $parameterValue; 

     } 

     $this->_class = $class; 
     $this->_method = $method; 
     $this->_parameters = $_parameters; 

     } 

     public function count(){ 

      if (is_null($this->_rowCount)){ 

       $parameters = $this->_parameters; 
       $parameters['count'] = true; 

       $this->_rowCount = call_user_func_array(array($this->_class, $this->_method), $parameters); 

      }  

      return $this->_rowCount; 

     } 

     public function getItems($offset, $itemCountPerPage){ 

      $parameters = $this->_parameters; 
      $parameters['limit'] = $itemCountPerPage; 
      $parameters['offset'] = $offset; 

      $items = call_user_func_array(array($this->_class, $this->_method), $parameters); 

      return $items; 
     } 

    } 

?> 

Esto es cómo funciona en su controlador:

<?php 

    class StoreController extends Zend_Controller_Action { 

     public function storeCustomersAction(){ 

      $model = new Default_Model_Store(); 
      $method = 'getStoreCustomers'; 
      $parameters = array('storeId' => 1); 

      $paginator = new Zend_Paginator(new Site_Paginator_Adapter_DbTableMethod($model, $method, $parameters)); 
      $paginator->setCurrentPageNumber($this->_request->getParam('page', 1)); 
      $paginator->setItemCountPerPage(20); 

      $this->view->paginator = $paginator; 

     } 

    } 

?> 

Los únicos requisitos para este adaptador para el trabajo es para enumerar los siguientes parámetros en el modelo de su lista de métodos argumentos (en cualquier orden [el adaptador detectará la firma del método mediante la reflexión):

$ límite = 0, offset = 0, $ $ = false recuento

El paginador llamará a su método con los valores apropiados para los argumentos $ limit, $ offset y $ count. ¡Eso es!

Ejemplo:

 <?php 

     class Default_Model_Store extends Zend_Db_Table { 

     public function getStoreCustomers($storeId, $includeCustomerOrders = false, $limit = 0, $offset = 0, $count = false){ 

if ($count) /* return SELECT COUNT(*) ... */ 

       /* ... run primary query, get result */ 
       $select = $this->_db->select(...)->limit($limit, $offset); 


       $rows = $this->_db->fetchAll($select); 

       if ($includeCustomerOrders){ 

        foreach ($rows as &$row){ 

         $customerId = $row['id']; 
         $row['orders'] = $this->getCustomerOrders($customerId); 

        } 

       } 

       return $rows;  

      } 

     } 

    ?> 
Cuestiones relacionadas