重写ii2的数据提供器ArraDataProvider类

首先看看ArrayDataProvider官方的doc:

ArrayDataProvider implements a data provider based on a data array.
ArrayDataProvider实现了一个基于数据数组的数据提供器。

The [[allModels]] property contains all data models that may be sorted and/or paginated.
[[allModels]]包含了需要排序和(或)分页的所有数据模型。

ArrayDataProvider will provide the data after sorting and/or pagination.
ArrayDataProvider提供排序和(或)分页后的数据。

You may configure the [[sort]] and [[pagination]] properties to
customize the sorting and pagination behaviors.
你可以配置[[sort]]和[[pagination]]属性自定义排序和分页行为。

Elements in the [[allModels]] array may be either objects (e.g. model objects) or associative arrays (e.g. query results of DAO).
[[allModels]]数组中的元素也许是对象(如,model对象)也许是关联数组(如,PDO的查询结果)。

Make sure to set the [[key]] property to the name of the field that uniquely identifies a data record or false if you do not have such a field.
确保设置的[[key]]属性是唯一标识一条记录的字段的名字,如果没有这样的字段,则设为false。

Compared to [[ActiveDataProvider]], ArrayDataProvider could be less efficient because it needs to have [[allModels]] ready.
与[[ActiveDataProvider]]比较,ArrayDataProvider可能效率较低,因为它需要准备[[allModels]]。

ArrayDataProvider may be used in the following way:
ArrayDataProvider可以按照下面的方式使用:

$query = new Query;
$provider = new ArrayDataProvider([
'allModels' => $query->from('post')->all(),
'sort' => [
'attributes' => ['id', 'username', 'email'],
],
'pagination' => [
'pageSize' => 10,
],
]);
// get the posts in the current page
$posts = $provider->getModels();
Note: if you want to use the sorting feature, you must configure the [[sort]] property
so that the provider knows which columns can be sorted.
注意:你给你想使用排序功能,你必须配置[[sort]]属性。

@author Qiang Xue
@since 2.0

从上面的指南可以看出,使用ArrayDataProvider需要准备好[[allModels]]数据,才开始渲染视图,并实现分页。

ArrayDataProvider是先把数据拉渠道内存中,然后再根据已有数据进行分页,这一点感觉像JQuery的DataTables插件,但是DataTables插件支持异步获取数据,也就是说可以根据配置可以分页从数据库中获取数据,显然,yii2自带的ArrayDataProvider明显不提供此功能。

先看看,yii2的ArrayDataProvider提供预处理models的方法,该方法处理排序和分页:

/      * @inheritdoc     */    protected function prepareModels()    {        if (($models = $this->allModels) === null) {            return [];        }        if (($sort = $this->getSort()) !== false) {            $models = $this->sortModels($models, $sort);        }        if (($pagination = $this->getPagination()) !== false) {            $pagination->totalCount = $this->getTotalCount();            if ($pagination->getPageSize() > 0) {                $models = array_slice($models, $pagination->getOffset(), $pagination->getLimit());            }        }        return $models;    }

对于分页代码,如过设置了pagination对象,也就是设置了分页,则统计数据总条数,然后根据每页的大小分片。

if (($pagination = $this->getPagination()) !== false) {

  $pagination->totalCount = $this->getTotalCount();  if ($pagination->getPageSize() > 0) {      $models = array_slice($models, $pagination->getOffset(), $pagination->getLimit());   }

}

再看看另一个方法,yii2的ArrayDataProvider提供的数据统计总条数的方法:

/      * @inheritdoc     */    protected function prepareTotalCount()    {        return count($this->allModels);    }

是的,ArrayDataProvider默认计算分页总数是根据allModels数组计算的,而allModels的数据就是我们查询赋值给提供器的。

这里面有两个很重要的方法必须看看:

    public function getTotalCount()    {        if ($this->getPagination() === false) {            return $this->getCount();        } elseif ($this->_totalCount === null) {            $this->_totalCount = $this->prepareTotalCount();        }        return $this->_totalCount;    }

该方法就是统计数据总数的,相应的应该有一个设置数据总数的:

    public function setTotalCount($value)    {        $this->_totalCount = $value;    }

而在ArrayDataProvider及其分类中,并没有一个public的totalCount属性,因此yii在处理的时候,将totalCount通过魔法函数进行设置,因为yii2中所有的类都是Object的子类,关于魔法函数,这一块内容参考深入理解yii2.0,在此感谢作者带我们走的这么深。

因此,不管你分页不分页,ArrayDataProvider并不是服务器端分页的,而是将已有数据分页处理的。

这种情况,如果数据量很大的时候,一点也不好,线上服务动辄上百万的数据,一下子拿出来分页,服务器吃不消,你也耗不起这个等待时间。

下面,我们需要重写这两个方法:

  1. models预处理方法

取消对已有数据的分片处理,统计数据总数根据我们的方式统计,比如数据库中的总条数。

     /*     *  @inheritdoc     */    protected function prepareModels()    {        if (($models = $this->allModels) === null) {            return [];        }        if (($sort = $this->getSort()) !== false) {            $models = $this->sortModels($models, $sort);        }        if (($pagination = $this->getPagination()) !== false) {            $pagination->totalCount = $this->getTotalCount();        }        return $models;    }
  1. 统计总数预处理函数

直接获取通过getTotalCount()函数获取传递给数据提供器的数据总和。

     /*     *       @inheritdoc     */    protected function prepareTotalCount()    {        return $this->getTotalCount();    }

下面给出重写后的完整ArrayDataProvider:

allModels) === null) {            return [];        }        if (($sort = $this->getSort()) !== false) {            $models = $this->sortModels($models, $sort);        }        if (($pagination = $this->getPagination()) !== false) {            $pagination->totalCount = $this->getTotalCount();        }        return $models;    }    /*     *       @inheritdoc     */    protected function prepareTotalCount()    {        return $this->getTotalCount();    }}

最后,来一个实际使用案例:

// TODO 业务逻辑$data = ... // 数据数组或对象$count = ... // 数据总条数,并不是count($data)的值,是数据库中符合条件的所有数据总数$dataProvider = new \backend\extensions\ArrayDataProvider(['allModels' => $data,'totalCount' => isset($count) ? $count : 0,'key' => 'ltime','sort' => [    'attributes' => [        'gmv',        'ltime',        'uv'    ],    'defaultOrder' => [        'gmv' => SORT_DESC,        'ltime' => SORT_DESC,        'uv' => SORT_DESC,    ],],'pagination' => [    'pageSize' => 15,],]);// 传递到test视图渲染return $this->render('test', ['model' => $model, 'dataProvider' => $dataProvider]);

在视图层接收该数据提供器,传递给一个数据渲染插件,比如GridView:

echo GridView::widget([    'dataProvider' => $dataProvider,    'columns' => [        ['class' => 'yii\grid\SerialColumn'],        [            'class' => 'yii\grid\DataColumn',            'value' => function ($data) {                if (isset($data['ltime']) && !empty($data['ltime'])) {                    return date('Y-m-d', $data['ltime']);                }            },            'label' => '日期',            'format' => 'raw',    ],    'moneyPerUvOrder:raw:订单UV单价',    'moneyPerUvPurchase:raw:销售UV单价'    ]]);

到此结束,如果帮到你,请点击收藏!

关键字:php, yii2


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部