2010-05-21 3 views
3

J'utilise un modèle de domaine riche dans mon application. Les idées de base ont été prises there. Par exemple, j'ai des entités Utilisateur et Commentaire. Ils sont définis comme suit:Pagination dans un modèle de domaine riche

<?php 
class Model_User extends Model_Abstract { 

    public function getComments() { 
     /** 
     * @var Model_Mapper_Db_Comment 
     */ 
     $mapper = $this->getMapper(); 
     $commentsBlob = $mapper->getUserComments($this->getId()); 
     return new Model_Collection_Comments($commentsBlob); 
    } 

} 

class Model_Mapper_Db_Comment extends Model_Mapper_Db_Abstract { 

    const TABLE_NAME = 'comments'; 

    protected $_mapperTableName = self::TABLE_NAME; 

    public function getUserComments($user_id) { 
     $commentsBlob = $this->_getTable()->fetchAllByUserId((int)$user_id); 
     return $commentsBlob->toArray(); 
    } 
} 

class Model_Comment extends Model_Abstract { 

} 
?> 

fonction de getUserComments Mapper retourne simplement quelque chose comme:

return $this->getTable->fetchAllByUserId($user_id) 

qui est un tableau. fetchAllByUserId accepte les paramètres $ count et $ offset, mais je ne sais pas les passer de mon contrôleur à cette fonction via le modèle sans réécrire tout le code du modèle. Donc, la question est comment puis-je organiser la pagination à travers les données du modèle (getComments). Existe-t-il une "belle" méthode pour obtenir des commentaires de 5 à 10, pas tous, comme le renvoie getComments par défaut.

+0

Pourriez-vous également afficher le code du mappeur, en particulier la fonction 'getUserComments'? – gnarf

+0

Terminé. Vous pouvez jeter un coup d'oeil. – Ololo

Répondre

0

Voici ma solution:

class Model_Mapper_Db_Comment extends Model_Mapper_Db_Abstract { 

    public function getUserCommentsPaginator($user_id) { 
     $select = $this->_getTable()->select()->where('user_id = ?', (int)$user_id); 
     $paginator = Zend_Paginator::factory($select, 'DbSelect'); 
     return $paginator; 
    } 
} 

class Model_User extends Model_Abstract implements Zend_Auth_Adapter_Interface { 

    public function getCommentsPaginator() { 
     $paginator = $this->getMapper(null, 'Comment')->getUserCommentsPaginator($this->id); 
     $paginator->setFilter(new App_Filter_Array_Collection('Model_Collection_Comments')); 
     return $paginator; 
    } 
} 

demande Modèle objet Zend_Paginate avec requête préparée sur la base user_id fournie par modèle. Ensuite, Model a ajouté un filtre à l'objet Zend_Paginate pour le rendre compatible avec les autres méthodes Model (retourner les classes Model_Collection, pas les tableaux). Tous les autres paramètres de pagination sont définis dans le contrôleur (page, nombre d'éléments par page, etc.).

Voilà comment j'ai séparé la logique de stockage, de gestion et de contrôle dans mon application.

2

Zend_Paginator Peut être la solution simple que vous cherchez. Il peut prendre toute array() ou instance d'un Iterator (qui a Zend_Db_Table_Rowset est)

$paginator = Zend_Paginator::factory($model->getComments()); 
$paginator->setItemCountPerPage(5); 
$paginator->setCurrentPageNumber($this->getRequest()->getParam('page',1)); 
$this->view->comments = $paginator; 

De l'avis:

<?php foreach($this->comments as $comment): ?> 
    Render your HTML for the comment 
<?php endforeach; ?> 
<?php echo $this->paginationControl($this->comments, 'Sliding', '_pagination.phtml'); ?> 

Et un (très) simple, paginationControl() partielle (extrait de this blog post):

<?php if ($this->pageCount): ?> 
<div class="paginationControl"> 
    <?php if (isset($this->previous)): ?> 
    <a href="<?= $this->url(array(’page’ => $this->previous)); ?>">&lt; Previous</a> | 
    <?php else: ?> 
    <span class="disabled">&lt; Previous</span> | 
    <?php endif; ?> 

    <?php foreach ($this->pagesInRange as $page): ?> 
    <?php if ($page != $this->current): ?> 
     <a href="<?= $this->url(array(’page’ => $page)); ?>"><?= $page; ?></a> | 
    <?php else: ?> 
     <?= $page; ?> | 
    <?php endif; ?> 
    <?php endforeach; ?> 

    <?php if (isset($this->next)): ?> 
    <a href="<?= $this->url(array(’page’ => $this->next)); ?>">Next &gt;</a> 
    <?php else: ?> 
    <span class="disabled">Next &gt;</span> 
    <?php endif; ?> 
</div> 
<?php endif; ?> 

More examples of Zend_Paginator sont disponibles via une recherche google.

+0

Cela signifie que je devrais obtenir tous les commentaires et paginer à travers eux comme tableau. Ce n'est pas la solution appropriée. – Ololo

+0

Si vous vous inquiétez trop de l'interrogation du SQL - 'Zend_Paginate' peut aussi prendre un' Zend_Db_Select' et il ajoutera automatiquement les bonnes clauses 'LIMIT'. Vous pouvez également écrire des adaptateurs personnalisés pour 'Zend_Paginate' qui peut gérer n'importe quelle classe arbitraire. L'interface est assez puissante en réalité. – gnarf

+0

Je connais les adaptateurs Zend_Paginate. Et dans le cas courant c'est juste un instrument pour faire la pagination. Mais je parle de comment faire pour obtenir une section de données sans retourner les instructions SQL du modèle au contrôleur, ce qui n'est pas bon. Si je pouvais créer un mécanisme transparent pour obtenir des données partielles de getComments, je créerais mon propre adaptateur Zend_Paginate. – Ololo

2

Si vous ne souhaitez que paginer les résultats que l'utilisateur voit et ne vous souciez pas d'améliorer vos performances, vous pouvez probablement éviter d'intégrer la pagination dans votre infrastructure de modèle.

En supposant que vous ayez une sorte de classe Model_Collection_Abstract dont dérivent toutes vos classes de collection, vous pourriez probablement pirater la pagination.

Alors vous auriez du code qui ressemble à:

<?PHP 
    //$comments is a subclass of Model_Collection_Abstract, which implements the paging stuff 
    $comments = $user->getComments(); 
    $comments->setStart(10); 
    $comments->setPageLength(10); 

    $numPages = $comments->numPages(); //can be derived from the pagelength and the collection's internal record store. 

    $currentPage = $comments->currentPage(); //can be derived from start and page length 

    foreach($comments as $comment){ 
     //this code runs up to ten times, starting at the tenth element in the collection. 
    } 

L'inconvénient est que vous êtes toujours saisir tous les commentaires, même si vous voulez voir seulement dix d'entre eux. Mais cela pourrait être une solution acceptable pour vous. Si vous voulez seulement N enregistrements retirés de la base de données (pour N = nombre à afficher), alors bien sûr, vous aurez besoin de mettre en œuvre un moyen de passer les paramètres start/limit, ou équivalents, tout au long de votre modèle.

EDIT: L'autre réponse à propos de Zend_Paginator vaut également la peine d'être lue. Si votre classe de collection est implements Iterator, il est tout à fait possible que vous puissiez le brancher sur Zend_Paginator et ainsi sauver de sérieux maux de tête. Je n'ai pas encore fait ça, mais ça vaut le coup d'y jeter un oeil!

2

Penser à ce sujet aussi maintenant. Avez-vous trouvé une solution intéressante? Mes pensées jusqu'à présent sont 1) évitez d'utiliser quelque chose comme Zend_Paginator et faites tout simplement sur votre propre. je.E vous params de l'environnement (objet de requête ou d'un fichier de configuration, etc.) comme itemCountPerPage, currentPageNumbe, vous passez ceux dans votre méthode de couche de service (ou dans votre cas, il est Mapper juste) comme

$comments = $this->getCommentsMapper()->getPage($itemCountPerPage, $currentPage);

vous demander le montant de l'article totale de votre mappeur (il est entièrement à vous si vous devez faire une demande séparée ou non) comme

$totalComments = $this->getCommentsMapper()->getTotal();

Alors maintenant, vous avez toutes les données pour faire une « vue paginateur ». En parlant "Zendy" vous passez toutes ces variables à la vue et dessinez le contrôle de pagination par vous-même.

2) Développer un adaptateur pour Zend_Paginator qui serait LIE sur son état. C'est à dire. Au lieu d'avoir tous les éléments, il en aura juste une partie dont vous avez besoin, mais vous devrez définir manuellement la valeur "total count" et d'autres. Je n'aime pas vraiment cette approche moi-même.

3) Ajouter un objet de domaine spécial appelé "Page" qui serait renvoyé par les mappeurs ou les couches de service. Un tel objet encapsulerait toutes les variables dont nous avons besoin pour construire une présentation de contrôle de pagination. Avec ceci, nous serions en mesure de faire ce qui suit:

` $ commentsService = $ this-> getCommentsService(); $ commentsService-> setItemCountPerPage (10); // disons que 10 est extrait de certains fichiers config.ini

$ commentairesPage = $ this-> getCommentsService() -> getPage (12); $ this-> view-> commentairesPage = $ commentairesPage;

`

Quelque part dans la vue que nous pouvons obtenir ces ` getItems $ commentsPage->(); // collection de commentaires

$ commentairesPage-> getCurrentPageNumber();

$ commentairesPage-> getTotalItemsCount();

`

qui est suffisant pour construire une paginateur.

C'est tout.

Je choisis actuellement entre la première et la troisième approche, probablement ira avec le troisième.

+0

J'ai trouvé la solution de compromis. Je pense que le mieux est de retourner l'objet Paginator du mappeur. Pourquoi? Parce que les mappeurs travaillent avec un type de stockage concret (c'est-à-dire une base de données), il n'y a rien de mal si le mappeur DB crée un objet Zend_Paginate brut. – Ololo

+0

Voici le lien vers ma solution: http://stackoverflow.com/questions/2883900/pagination-in-a-rich-domain-model/3091917#3091917 – Ololo

Questions connexes