资讯专栏INFORMATION COLUMN

重写yii2的数据提供器ArrayDataProvider类

xiaokai / 919人阅读

摘要:再看看另一个方法,的提供的数据统计总条数的方法是的,默认计算分页总数是根据数组计算的,而的数据就是我们查询赋值给提供器的。统计总数预处理函数直接获取通过函数获取传递给数据提供器的数据总和。

首先看看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并不是服务器端分页的,而是将已有数据分页处理的。

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

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

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

统计总数预处理函数

直接获取通过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 ackendextensionsArrayDataProvider([
"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" => "yiigridSerialColumn"],
        [
            "class" => "yiigridDataColumn",
            "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单价"
    ]
]);

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/21705.html

相关文章

  • Yii2 GridView使用方法

    摘要:是实现网格视图的小部件,一般用于报表视图的展示。就是连续的列,主要用于网格的行号,属于自增式的列。指定处理的类,必须。 Yii2 GridView是实现yii网格视图的小部件,一般用于报表视图的展示。今天,结合DataProvider(ArrayDataProvider以及SqlDataProvider)说一下GridView中的几个Columns(SerialColumn,DataC...

    Paul_King 评论0 收藏0
  • 阿北知识分享小程序中restful使用经验贴

    摘要:大家知道我最近在给阿北的知识分享微信小程序改版,使用的是中的功能,接下来把遇到的一些问题及小技巧分享一下。小结以上就是目前为止在使用的开发小程序时候使用的一些知识和技巧,希望对你有用,以后如果有再分享哈。 大家知道我最近在给阿北的知识分享微信小程序改版,使用的是yii2中的restful功能,接下来把遇到的一些问题及小技巧分享一下。 先安利一下小程序码 链接 开始分享。 URL要重写 ...

    Meils 评论0 收藏0
  • Yii修行之路 - Extension 扩展

    摘要:运行来安装指定的扩展。这更便于用户辨别是否是的扩展。当用户运行安装一个扩展时,文件会被自动更新使之包含新扩展的信息。上述代码表明该扩展依赖于包。例如,上述的条目声明将对应于别名。为达到这个目的,你应当在公开发布前做测试。 简述 扩展是专门设计的在 Yii 应用中随时可拿来使用的, 并可重发布的软件包。 基础 例如, yiisoft/yii2-debug 扩展在你的应用的每个页面底部添加...

    bovenson 评论0 收藏0
  • 微信小程序[第十一篇] -- 添加照片(小程序图片上传功能)

    摘要:注拍照功能在某些机型上还有闪退现象,希望微信官方可以尽快完善。这涉及到函数,这是微信小程序内置的,用来上传一个文件,有几个点要说下绿色框要上传文件资源的路径,也就是我们相册里选择的图片路径。 我们喜欢小程序的原因之一就是它提供了更多和手机系统交互的接口,比如今天要说的这个从相册选择 / 拍照功能。注:拍照功能在某些机型上还有闪退现象,希望微信官方可以尽快完善。 在上一篇中我们搞定了相册...

    muzhuyu 评论0 收藏0
  • YII2项目常用技能知识总结

    摘要:不通过日志获取执行的原生语句和打印变量数据打印变量数据可以这样写引用命名空间使用使用第二个参数是数组的深度第三个参数是是否显示代码高亮默认不显示从数据库二维数组中返回一维数组并配合验证规则实现分类数据过滤。 1、不通过日志获取AR执行的原生SQL语句和打印变量数据 $query = User::find() ->select([username])->where([id=>[1,2,3...

    W_BinaryTree 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<