yii2 / 在这里有个叫BaseDataProvider的老爹

492 阅读3分钟

我们昨天说到了ActiveDataProvider、SqlDataProvider和ArrayDataProvider,从描述中不难发现这几个货的行为都差不多,因此yii2的开发者们为它们设置了一个BaseDataProvider的父类,而BaseDataProvider又实现了一个叫做DataProviderInterface的接口。

在PHP中,实现接口的类必须完成接口中声明的所有函数,当我们要看BaseDataProvider中有哪些可用方法的时候,首先要关注DataProviderInterface接口。

DataProviderInterface

  • prepare
  • getCount
  • getTotalCount
  • getModels
  • getKeys
  • getSort
  • getPagination

以上是DataProviderInterface为我们提供的函数声明,也就是说BaseDataProvider类实现了这些函数,而我们的DataProvider三兄弟也因为继承了BaseDataProvider自然拥有了这些方法。

因此昨天的文章『小谈yii2中3个数据提供者及与GridView的搭配使用』中,我们大胆的使用了getCount、getTotalCount、getModels等。

接下来我们先说在BaseDataProvider中来自接口DataProviderInterface的方法。

prepare

数据准备,这是一个功能性的函数,它负责组装DataProvider中的_models和_keys属性,我们来大体看一下这个函数。

public function prepare($forcePrepare = false){
    if ($forcePrepare || $this->_models === null) {
        $this->_models = $this->prepareModels();
    }
    if ($forcePrepare || $this->_keys === null) {
        $this->_keys = $this->prepareKeys($this->_models);
    }
}

要研究明白这个函数,我们首先要了解下 _models_keys 属性。

  • _models 这个很容易理解,我们通过getModels或GridView得到的对象集或数组集合。每一项代表着具体的数据。
  • _keys 表示每个数据项的唯一键,当我们使用ActiveDataProvider是就是每条数据的主键值,而其他两种DataProvider的是_models数组的key值。

prepare函数仅仅在BaseDataProvider类中定义,而每种DataProvider定义了自己的prepareModels和prepareKeys方法,因此我们在不同的DataProvider下调用prepare得到的是不同的值。

另外prepare也有一个叫做$forcePrepare的参数,用意很简单,是否在**_models** 和 _keys属性值已经存在的情况下强行刷新,默认为false不强行刷新。

getKeys

在介绍prepare的时候我们看到了prepareModels和prepareKeys,正因为有它们的存在我们才能正确通过getModels和getKeys函数拿到相应的数据。

我们先来说说getKeys函数,其实也就是在DataProvider中 $_keys 属性的内容。

这里有一点不同,我们来看下。

ActiveDataProvider

$dataProvider = new ActiveDataProvider([
    'query' => Blog::find()->select(['title','id']),
    'pagination' => [
        'pageSize' => 20,
    ],
]);

VarDumper::dump($dataProvider->getKeys(),10,true);

我们得到的结果如下

[
    0 => 1
    1 => 2
    2 => 3
    3 => 4
    4 => 5
    5 => 9
]

是的,你一定发现了,_keys是一个数组,数组的key代表每一行数据项,value值代表对应数据的主键。

注意:如果你数据项中不存在id列,将返回null。比如我们上面代码的$query是Blog::find()->select(['title'])。

而针对ArrayDataProvider和SqlDataProvider提供者,getKeys得到的数组就简单的多了,数据项就是一个数组,因此得到的都是如下

[
    0 => 0
    1 => 1
    2 => 2
    3 => 3
    4 => 4
    5 => 5
]

当然你也可以通过setKeys函数对这些规则进行重写,后续篇章会讲到

getCount & getTotalCount

这是两个关于数量统计的函数,用意也及其明白,之所以诞生是因为DataProvider支持分页功能。

  • getCount 当前页面的数据项数量
  • getTotalCount 数据项总数量

getSort

得到排序的信息,这个方法实现及其简单,看一下。

public function getSort()
{
    if ($this->_sort === null) {
        $this->setSort([]);
    }

    return $this->_sort;
}

从代码看还是返回了_sort属性,那么问题就回到setSort方法身上,研究明白如何对_sort赋值就能知道getSort得到的是什么了~

我们先把代码贴过来

public function setSort($value){
    if (is_array($value)) {
        $config = ['class' => Sort::className()];
        if ($this->id !== null) {
            $config['sortParam'] = $this->id . '-sort';
        }
        $this->_sort = Yii::createObject(array_merge($config, $value));
    } elseif ($value instanceof Sort || $value === false) {
        $this->_sort = $value;
    } else {
        throw new InvalidParamException('Only Sort instance, configuration array or false is allowed.');
    }
}

对于$value的传递一共有三种可能

  • Array 一个配置数组
  • Sort 一个sort对象
  • Bool 一个布尔型的值

这些我们可以通过新建DataProvider对象时候传入,比如

$dataProvider = new ActiveDataProvider([
    'query' => Blog::find()->select(['title','id']),
    'sort'=>false
]);

重点 当sort为false时候代表不排序,sort不能为true(会报错),当我们要排序的时候应该出入配置数组和sort对象。

setSort方法会根据参数的类型来进行不同的逻辑处理,比如传递数组类型的会生成一个一个Sort对象并将你的设置作为新建对象的参数,当然你也可以直接传入一个Sort对象,效果是一样的。

getPagination

这是一个重要的函数,它负责分页。

public function getPagination()
{
    if ($this->_pagination === null) {
        $this->setPagination([]);
    }

    return $this->_pagination;
}

但是不要担心,这个方法和getSort方法的思路完全一致,当我们发现对象的_pagination不存在时,调用setPagination对其进行设置,接下来的代码你会非常熟悉。

public function setPagination($value)
{
    if (is_array($value)) {
        $config = ['class' => Pagination::className()];
        if ($this->id !== null) {
            $config['pageParam'] = $this->id . '-page';
            $config['pageSizeParam'] = $this->id . '-per-page';
        }
        $this->_pagination = Yii::createObject(array_merge($config, $value));
    } elseif ($value instanceof Pagination || $value === false) {
        $this->_pagination = $value;
    } else {
        throw new InvalidParamException('Only Pagination instance, configuration array or false is allowed.');
    }
}

是吧,你是不是发现和setSort完全一致,接收的函数也一样。

小结

现在你知道DataProvider的运行原理以及常用方法了么?接下来我将为你介绍在GridView中是如何对DataProvider对象进行处理的。

本文来自于 https://nai8.me