Zend_Paginator MVC hatları bulanıklık

7 Cevap php

Ön denetleyicisi, aksiyon kontrolörleri, izleme ve - gevşek quickstart uygulamanın yapısına dayalı bir yapıya sahip bir Zend Framework (1.7) proje üzerinde çalışıyorum veritabanına almak için Zend_Db_Table kullanın modelleri. Benim ana modellerinden biri, bazı pahalı birincil listesini yukarı çekin katıldı dayanır, bu yüzden satır sayısını azaltmak için Zend_Paginator kullanarak içine arıyorum veritabanından geri getirdi. Benim sorunum Zend_Paginator sadece gerçekten benim için iyi bir uyum gibi görünüyor hiçbiri 4 adaptörleri ile birlikte geliyor olması.

  • Array: ZP beslemek için diziden bina kaçınmaya çalışıyorum budur tüm kayıtları getiriliyor içerecektir
  • Iterator: Bu Model için kötü uygun olurdu gibi bir aptal yineleyici bir dizi ve akıllı biri hisseder gibi aynı sorunları sunacak
  • DbSelect: Bir DbSelect Kontrolörü kadar itiraz Başlarken rahatsız (yerine kapsüllü nesnelerden daha ham sonuç satırlarını üreten saymıyorum) benim DB iç işleyişini Kontrol kravat
  • DbTableSelect, DbSelect aynı
  • Null Adapter: ileri ve geri elle tüm ayrıntıları geçmek.

Modeli sayfa numaralarını geçen bu gibi hissediyor, çok MVC ayrımı ihlal eder. Ben MVC ayrımı bakımı hakkında dogmatik için açıyorum ya da ben birlikte tüm hareketli parçaları yapışmasını temiz, zarif bir şekilde eksik olduğunu, yanlış benim modeli inşa ettik sorun mudur?

7 Cevap

Sen $current_page ve $per_page parametreleri kabul eder ve geçerli sayfanın veri seti yanı sıra paginator nesnesi döndürür modellerin üzerinde bir arayüz sağlayabilir.

Bu şekilde tüm Sayfalandırması kod modeli içinde yer alan ve size kavramını kırdım gibi hissetmeden Db adaptörleri kullanmak için ücretsiz olmasıdır.

Bu verilere bağlı (ve modelleri sadece veritabanı bağlantıları, veri olan) olan size doğru Plus beri, denetleyici gerçekten her çağrı kurma edilmemelidir.

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

Istediğiniz herhangi bir model nesnesine satır nesneden veri yüklemek için olanak Zend_Paginator için bir setFilter bir yöntem şimdi var:

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;
    }
}

I really yerine bir dizi veya bir Zend_Db_Select nesne, benim paginator adaptörü için kaynak olarak bir Zend_Db_Table sınıf yöntemi kullanabilirsiniz hangi bir çözüme ihtiyacı vardı.

Gelişmiş modelleme Bu tür Zend_Paginator için standart adaptörleri ile uyumlu değildir. Ben önde gitti ve ben gibi, bir cevap için umutsuz herkes için bu sabit.

<?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;
        }

    }

?>

Bu sizin denetleyicisi nasıl işliyor:

    <?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;

        }

    }

?>

Çalışmak için bu adaptör için sadece gereksinimleri, model yöntemleri argümanlar listesinde aşağıdaki parametreleri (herhangi bir sırayla [adaptör yansıma yoluyla yöntem imzası algılayacaktır) listelemek için:

$limit = 0, $offset = 0, $count = false

Paginator, $ sınırı için uygun değerleri ile yöntemini çağırın $ ofset, ve $ argümanlar sayacaktır. İşte bu!

Örnek:

        <?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;    

            }

        }

    ?>

Eh, ben DbSelect kullanarak endişelerinizi size bir cevap veremem ama ben çekti satır sayısını azaltmak konuyla ilgili (ibuildings blogun görüşleri) bu kod bit rastlamak yoktu. Bazı okuyucular için yararlı olabilir.

$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;

MVC ile çalışırken dikkate alınması gereken önemli bir husustur kontrolör iş mantığı için ise modeli, tüm etki alanı mantık için olmasıdır. Başparmak genel bir kural modeli (ler) arabirimi (kontrolörleri veya görünümler) hiçbir bilgiye sahip olmalıdır, ama bunlar basit DB erişiciler olması gerekmez ki. Mümkün olduğunca taşınabilir olması, onlar biçimlendirme veya görüntü özellikleri ya (bu etki mantığının parçası olmadığı sürece) hiçbir şey bilmeli.

Aslında, alan mantık işleyen tüm mantık modeldeki olup kontrol olmalıdır. Kontrolör modeli, gerektiği şekilde dönüştürerek, arayüzü bilgi aktarmak ve / güncelleme görüntülemek için görüntülemek hangi seçmelisiniz. Bu arayüzü ile ilgisi yoksa, bu daha iyi daha sonra denetleyici / görünüm eşleştirmeleri takas için karar verirseniz daha kodu tekrar kullanmak için izin verecek gibi, bir kontrolör daha bir modelde temsil edilebilir.

İdeal olarak, modeli ihtiyacınız bilgilerine erişmek için bir arayüz sağlamak gerekir. O arayüzü arkasında nasıl uygulandığını çok uzun modeli MVC VC kısmının habersiz kalır gibi, MVC bir endişe değildir. Bir paginator nesnenin etrafında geçen demektir paginator (üzgünüm, ben Zend bilmiyorum) kendisini render ile ilgisi olmadığını, bir arayüzü geçmek iyi olabilir ama, o, MVC ilkeler doğrudan ihlali değil (Bu işleme yöntemlerini eksik) bunun, model arka dışarı geçmek, sonra onu doldurmak / manipüle var. Bu şekilde modelden üretilen kod render yok, ve bir konsol uygulaması daha sonra uygulama yapmak (ya da bazı tür bir API arayüzü eklemek) karar eğer paginator uygulanmasını yerini alabilir.

Eğer DbSelect adaptör kullanıyorsanız sadece resultset geçebilir ve bu bazı ayrılığı koruyarak uzun bir yol gidiyor. Controller Yani:

$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);

Görünümde paginator aracılığıyla verilere erişebilir

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

Bu basitleştirilmiş bir örnek (ben de kurulum kadar varsayılan kaydırma tarzı ve bootstrap kısmi varsayılan görünümü), fakat modelden alınan veriler Kontrolörü tarafından görünüme yerleştirilir çünkü denetleyicisi kurduktan thnk kötü değil Zaten bu uygulama resultset DEĞİL modeli kullanır.

Ayrıca, doğrudan Zend_Paginator_Adapter_Interface uygulamak veya sayfalandırma desteklemesi gerekir herhangi modeli Zend_Paginator_Adapter_DbSelect uzanabilir.

Bu şekilde, modeli doğrudan View, Kontrolör hatta Zend_Paginator hakkında hiçbir şey bilmiyor, ama en mantıklı yerde doğrudan Zend_Paginator ile kullanılabilir.

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);
    }
    ...
}

Böyle bir şey ile, doğrudan o en mantıklı yerde bu sınıfın bir örneğini kullanarak bir çağrı başlatabilirsiniz:

$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 ...
}

Şimdi, bir dizi herhangi bir "Model" için temel sınıf olarak bu sınıfı kullanabilirsiniz.