6

私は、Zend_Db_Table を使用してデータベースにアクセスするフロント コントローラー、アクション コントローラー、ビュー、モデルなど、クイックスタート アプリケーションの構造に大まかに基づいた構造を持つ Zend Framework (1.7) プロジェクトに取り組んでいます。私の主なモデルの 1 つは、主要なリストを取得するために高価な結合に依存しているため、Zend_Paginator を使用してデータベースから返される行数を減らすことを検討しています。私の問題は、Zend_Paginator には 4 つのアダプターしか付属していないことです。どれも実際には私に適しているとは思えません。

  • Array : ZP にフィードする配列を作成するには、すべてのレコードをフェッチする必要がありますが、これは回避しようとしています。
  • イテレータ: 愚かなイテレータは配列と同じ問題を引き起こし、スマートなイテレータはモデルにはあま​​り適合しないように感じます
  • DbSelect : DbSelect オブジェクトを Controller まで取得すると、Controller が DB の内部動作に不快に結び付けられます (カプセル化されたオブジェクトではなく生の結果行を生成することは言うまでもありません)
  • DbTableSelect : DbSelect と同じ
  • Null Adapter : すべての詳細を手動でやり取りします。

ページネーターをモデルに渡すことも、MVC の分離に違反するように感じます。問題は、モデルを正しく構築していないこと、MVC の分離を維持することについて独断的であること、またはすべての可動部分をくっつけるクリーンでエレガントな方法を欠いていることですか?

4

7 に答える 7

2

$current_pageモデルに、現在のページのデータセットとページネーターオブジェクトを受け入れて$per_pageパラメーターを設定して返すインターフェイスを提供できます。

このようにして、すべてのページネーションコードがモデル内に含まれ、概念を破ったように感じることなく、Dbアダプターを自由に使用できます。

さらに、データに関連付けられているのは正しいので、コントローラーは実際にはポケットベルを設定するべきではありません(モデルはデータベース接続だけでなくデータ用です)。

class Model
{
    //...
    /**
     * @return array Array in the form: array( 'paginator' => obj, 'resultset' => obj )
     */
    public function getAll( $where = array(), $current_page = null, $per_page = null );
    //...
}
于 2008-12-05T18:12:41.747 に答える
2

Zend_Paginator の setFilter メソッドが追加され、行オブジェクトから任意のモデル オブジェクトにデータをロードできるようになりました。

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;
    }
}
于 2010-02-23T19:55:45.987 に答える
1

配列や Zend_Db_Select オブジェクトの代わりに、Zend_Db_Table クラス メソッドをページネーター アダプターのリソースとして使用できるソリューションが本当に必要でした。

この種の高度なモデリングは、Zend_Paginator の標準アダプターと互換性がありません。私は先に進み、私と同じように答えを切望しているすべての人のためにこれを修正しました.

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

    }

?>

コントローラーでの動作は次のとおりです。

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

        }

    }

?>

このアダプターが機能するための唯一の要件は、モデル メソッドの引数リストに次のパラメーターをリストすることです (任意の順序で [アダプターはリフレクションを通じてメソッド シグネチャを検出します)。

$limit = 0、$offset = 0、$count = false

ページネーターは、$limit、$offset、および $count 引数に適切な値を指定してメソッドを呼び出します。それでおしまい!

例:

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

            }

        }

    ?>
于 2013-12-30T05:06:28.810 に答える
0

Zend_Paginator_Adapter_Interfaceを直接実装することも、ページネーションをサポートする必要のある任意のモデルでZend_Paginator_Adapter_DbSelectを拡張することもできます。

このように、モデルはView、Controller、さらにはZend_Paginatorについて直接何も知りませんが、最も意味のある場所であればどこでもZend_Paginatorで直接使用できます。

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

このようなものを使用すると、このクラスのインスタンスを使用して、最も意味のある場所でページャーを直接インスタンス化できます。

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

これで、このクラスを、セットである任意の「モデル」の基本クラスとして使用できます。

于 2010-02-24T06:08:20.043 に答える
0

DbSelect アダプターを使用する場合は、結果セットを渡すだけで、ある程度の分離を維持できます。したがって、コントローラーで:

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

ビューでは、ページネーターを介してデータにアクセスできます

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

これは単純化された例です (ブートストラップでデフォルトのスクロール スタイルとデフォルトのビュー パーシャルもセットアップします)、モデルから取得したデータはコントローラーによってビューに配置されるため、コントローラーでセットアップすることは悪くないと思いますとにかく、この実装はモデルではなく結果セットを利用します。

于 2008-12-06T22:14:14.443 に答える
0

MVC を使用する際に考慮すべき重要な考慮事項は、モデルはすべてのドメイン ロジック用であり、コントローラーはビジネス ロジック用であるということです。一般的な経験則では、モデルはインターフェース (コントローラーまたはビュー) を認識してはいけませんが、単純な DB アクセサーである必要はありません。可能な限り移植性を持たせるには、書式設定や表示プロパティについても何も知らない必要があります (それがドメイン ロジックの一部でない限り)。

実際、ドメイン ロジックを操作するすべてのロジックは、コントローラーではなくモデル内にある必要があります。コントローラーは、必要に応じて変換してインターフェースから情報をモデルに渡し、表示/更新するビューを選択する必要があります。インターフェイスと関係がない場合は、後でコントローラーとビューのペアを交換することにした場合にコードを再利用できるため、コントローラーよりもモデルで表現する方がよい場合があります。

理想的には、モデルは、必要な情報にアクセスするためのインターフェイスを提供する必要があります。モデルが MVC の VC 部分を認識しない限り、そのインターフェイスの背後でそれがどのように実装されるかは MVC の問題ではありません。それがページネーター オブジェクトを渡すことを意味する場合、それは MVC 原則の直接の違反ではありません。それを(レンダリングメソッドがありません)、モデルに操作/入力させてから、元に戻します。そうすれば、レンダリング コードをモデルから生成する必要がなくなり、後でアプリケーションをコンソール アプリにする (または何らかの API インターフェイスを追加する) 場合に、ページネーターの実装を置き換えることができます。

于 2008-12-05T19:01:58.263 に答える
0

DbSelect の使用に関するあなたの懸念にお答えすることはできませんが、プルされる行数を減らすという問題に関連するこのコード (ibuildings ブログのコメント内) に出くわしました。一部の読者には役立つかもしれません。

$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;
于 2008-12-05T04:23:32.250 に答える