init
This commit is contained in:
203
vendor/yiisoft/yii2/data/ActiveDataFilter.php
vendored
Normal file
203
vendor/yiisoft/yii2/data/ActiveDataFilter.php
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\data;
|
||||
|
||||
/**
|
||||
* ActiveDataFilter allows composing a filtering condition in a format suitable for [[\yii\db\QueryInterface::where()]].
|
||||
*
|
||||
* @see DataFilter
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.0.13
|
||||
*/
|
||||
class ActiveDataFilter extends DataFilter
|
||||
{
|
||||
/**
|
||||
* @var array maps filtering condition keywords to build methods.
|
||||
* These methods are used by [[buildCondition()]] to build the actual filtering conditions.
|
||||
* Particular condition builder can be specified using a PHP callback. For example:
|
||||
*
|
||||
* ```php
|
||||
* [
|
||||
* 'XOR' => function (string $operator, mixed $condition) {
|
||||
* //return array;
|
||||
* },
|
||||
* 'LIKE' => function (string $operator, mixed $condition, string $attribute) {
|
||||
* //return array;
|
||||
* },
|
||||
* ]
|
||||
* ```
|
||||
*/
|
||||
public $conditionBuilders = [
|
||||
'AND' => 'buildConjunctionCondition',
|
||||
'OR' => 'buildConjunctionCondition',
|
||||
'NOT' => 'buildBlockCondition',
|
||||
'<' => 'buildOperatorCondition',
|
||||
'>' => 'buildOperatorCondition',
|
||||
'<=' => 'buildOperatorCondition',
|
||||
'>=' => 'buildOperatorCondition',
|
||||
'=' => 'buildOperatorCondition',
|
||||
'!=' => 'buildOperatorCondition',
|
||||
'IN' => 'buildOperatorCondition',
|
||||
'NOT IN' => 'buildOperatorCondition',
|
||||
'LIKE' => 'buildOperatorCondition',
|
||||
];
|
||||
/**
|
||||
* @var array map filtering operators to operators used in [[\yii\db\QueryInterface::where()]].
|
||||
* The format is: `[filterOperator => queryOperator]`.
|
||||
* If particular operator keyword does not appear in the map, it will be used as is.
|
||||
*
|
||||
* Usually the map can be left empty as filter operator names are consistent with the ones
|
||||
* used in [[\yii\db\QueryInterface::where()]]. However, you may want to adjust it in some special cases.
|
||||
* For example, when using PosgreSQL you may want to setup the following map:
|
||||
*
|
||||
* ```php
|
||||
* [
|
||||
* 'LIKE' => 'ILIKE'
|
||||
* ]
|
||||
* ```
|
||||
*/
|
||||
public $queryOperatorMap = [];
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildInternal()
|
||||
{
|
||||
$filter = $this->normalize(false);
|
||||
if (empty($filter)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->buildCondition($filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $condition
|
||||
* @return array built condition.
|
||||
*/
|
||||
protected function buildCondition($condition)
|
||||
{
|
||||
$parts = [];
|
||||
foreach ($condition as $key => $value) {
|
||||
if (isset($this->conditionBuilders[$key])) {
|
||||
$method = $this->conditionBuilders[$key];
|
||||
if (is_string($method)) {
|
||||
$callback = [$this, $method];
|
||||
} else {
|
||||
$callback = $method;
|
||||
}
|
||||
} else {
|
||||
$callback = [$this, 'buildAttributeCondition'];
|
||||
}
|
||||
$parts[] = $callback($key, $value);
|
||||
}
|
||||
|
||||
if (!empty($parts)) {
|
||||
if (count($parts) > 1) {
|
||||
$parts = array_merge(['AND'], $parts);
|
||||
} else {
|
||||
$parts = array_shift($parts);
|
||||
}
|
||||
}
|
||||
|
||||
return $parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds conjunction condition, which consists of multiple independent ones.
|
||||
* It covers such operators as `and` and `or`.
|
||||
* @param string $operator operator keyword.
|
||||
* @param mixed $condition raw condition.
|
||||
* @return array actual condition.
|
||||
*/
|
||||
protected function buildConjunctionCondition($operator, $condition)
|
||||
{
|
||||
if (isset($this->queryOperatorMap[$operator])) {
|
||||
$operator = $this->queryOperatorMap[$operator];
|
||||
}
|
||||
$result = [$operator];
|
||||
|
||||
foreach ($condition as $part) {
|
||||
$result[] = $this->buildCondition($part);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds block condition, which consists of a single condition.
|
||||
* It covers such operators as `not`.
|
||||
* @param string $operator operator keyword.
|
||||
* @param mixed $condition raw condition.
|
||||
* @return array actual condition.
|
||||
*/
|
||||
protected function buildBlockCondition($operator, $condition)
|
||||
{
|
||||
if (isset($this->queryOperatorMap[$operator])) {
|
||||
$operator = $this->queryOperatorMap[$operator];
|
||||
}
|
||||
return [
|
||||
$operator,
|
||||
$this->buildCondition($condition),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds search condition for a particular attribute.
|
||||
* @param string $attribute search attribute name.
|
||||
* @param mixed $condition search condition.
|
||||
* @return array actual condition.
|
||||
*/
|
||||
protected function buildAttributeCondition($attribute, $condition)
|
||||
{
|
||||
if (is_array($condition)) {
|
||||
$parts = [];
|
||||
foreach ($condition as $operator => $value) {
|
||||
if (isset($this->operatorTypes[$operator])) {
|
||||
if (isset($this->conditionBuilders[$operator])) {
|
||||
$method = $this->conditionBuilders[$operator];
|
||||
if (is_string($method)) {
|
||||
$callback = [$this, $method];
|
||||
} else {
|
||||
$callback = $method;
|
||||
}
|
||||
$parts[] = $callback($operator, $value, $attribute);
|
||||
} else {
|
||||
$parts[] = $this->buildOperatorCondition($operator, $value, $attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($parts)) {
|
||||
if (count($parts) > 1) {
|
||||
return array_merge(['AND'], $parts);
|
||||
}
|
||||
return array_shift($parts);
|
||||
}
|
||||
}
|
||||
|
||||
return [$attribute => $this->filterAttributeValue($attribute, $condition)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an operator condition.
|
||||
* @param string $operator operator keyword.
|
||||
* @param mixed $condition attribute condition.
|
||||
* @param string $attribute attribute name.
|
||||
* @return array actual condition.
|
||||
*/
|
||||
protected function buildOperatorCondition($operator, $condition, $attribute)
|
||||
{
|
||||
if (isset($this->queryOperatorMap[$operator])) {
|
||||
$operator = $this->queryOperatorMap[$operator];
|
||||
}
|
||||
return [$operator, $attribute, $this->filterAttributeValue($attribute, $condition)];
|
||||
}
|
||||
}
|
||||
199
vendor/yiisoft/yii2/data/ActiveDataProvider.php
vendored
Normal file
199
vendor/yiisoft/yii2/data/ActiveDataProvider.php
vendored
Normal file
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\data;
|
||||
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\base\Model;
|
||||
use yii\db\ActiveQueryInterface;
|
||||
use yii\db\Connection;
|
||||
use yii\db\QueryInterface;
|
||||
use yii\di\Instance;
|
||||
|
||||
/**
|
||||
* ActiveDataProvider implements a data provider based on [[\yii\db\Query]] and [[\yii\db\ActiveQuery]].
|
||||
*
|
||||
* ActiveDataProvider provides data by performing DB queries using [[query]].
|
||||
*
|
||||
* The following is an example of using ActiveDataProvider to provide ActiveRecord instances:
|
||||
*
|
||||
* ```php
|
||||
* $provider = new ActiveDataProvider([
|
||||
* 'query' => Post::find(),
|
||||
* 'pagination' => [
|
||||
* 'pageSize' => 20,
|
||||
* ],
|
||||
* ]);
|
||||
*
|
||||
* // get the posts in the current page
|
||||
* $posts = $provider->getModels();
|
||||
* ```
|
||||
*
|
||||
* And the following example shows how to use ActiveDataProvider without ActiveRecord:
|
||||
*
|
||||
* ```php
|
||||
* $query = new Query();
|
||||
* $provider = new ActiveDataProvider([
|
||||
* 'query' => $query->from('post'),
|
||||
* 'pagination' => [
|
||||
* 'pageSize' => 20,
|
||||
* ],
|
||||
* ]);
|
||||
*
|
||||
* // get the posts in the current page
|
||||
* $posts = $provider->getModels();
|
||||
* ```
|
||||
*
|
||||
* For more details and usage information on ActiveDataProvider, see the [guide article on data providers](guide:output-data-providers).
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class ActiveDataProvider extends BaseDataProvider
|
||||
{
|
||||
/**
|
||||
* @var QueryInterface the query that is used to fetch data models and [[totalCount]]
|
||||
* if it is not explicitly set.
|
||||
*/
|
||||
public $query;
|
||||
/**
|
||||
* @var string|callable the column that is used as the key of the data models.
|
||||
* This can be either a column name, or a callable that returns the key value of a given data model.
|
||||
*
|
||||
* If this is not set, the following rules will be used to determine the keys of the data models:
|
||||
*
|
||||
* - If [[query]] is an [[\yii\db\ActiveQuery]] instance, the primary keys of [[\yii\db\ActiveQuery::modelClass]] will be used.
|
||||
* - Otherwise, the keys of the [[models]] array will be used.
|
||||
*
|
||||
* @see getKeys()
|
||||
*/
|
||||
public $key;
|
||||
/**
|
||||
* @var Connection|array|string the DB connection object or the application component ID of the DB connection.
|
||||
* If not set, the default DB connection will be used.
|
||||
* Starting from version 2.0.2, this can also be a configuration array for creating the object.
|
||||
*/
|
||||
public $db;
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the DB connection component.
|
||||
* This method will initialize the [[db]] property to make sure it refers to a valid DB connection.
|
||||
* @throws InvalidConfigException if [[db]] is invalid.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
if (is_string($this->db)) {
|
||||
$this->db = Instance::ensure($this->db, Connection::className());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepareModels()
|
||||
{
|
||||
if (!$this->query instanceof QueryInterface) {
|
||||
throw new InvalidConfigException('The "query" property must be an instance of a class that implements the QueryInterface e.g. yii\db\Query or its subclasses.');
|
||||
}
|
||||
$query = clone $this->query;
|
||||
if (($pagination = $this->getPagination()) !== false) {
|
||||
$pagination->totalCount = $this->getTotalCount();
|
||||
if ($pagination->totalCount === 0) {
|
||||
return [];
|
||||
}
|
||||
$query->limit($pagination->getLimit())->offset($pagination->getOffset());
|
||||
}
|
||||
if (($sort = $this->getSort()) !== false) {
|
||||
$query->addOrderBy($sort->getOrders());
|
||||
}
|
||||
|
||||
return $query->all($this->db);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepareKeys($models)
|
||||
{
|
||||
$keys = [];
|
||||
if ($this->key !== null) {
|
||||
foreach ($models as $model) {
|
||||
if (is_string($this->key)) {
|
||||
$keys[] = $model[$this->key];
|
||||
} else {
|
||||
$keys[] = call_user_func($this->key, $model);
|
||||
}
|
||||
}
|
||||
|
||||
return $keys;
|
||||
} elseif ($this->query instanceof ActiveQueryInterface) {
|
||||
/* @var $class \yii\db\ActiveRecordInterface */
|
||||
$class = $this->query->modelClass;
|
||||
$pks = $class::primaryKey();
|
||||
if (count($pks) === 1) {
|
||||
$pk = $pks[0];
|
||||
foreach ($models as $model) {
|
||||
$keys[] = $model[$pk];
|
||||
}
|
||||
} else {
|
||||
foreach ($models as $model) {
|
||||
$kk = [];
|
||||
foreach ($pks as $pk) {
|
||||
$kk[$pk] = $model[$pk];
|
||||
}
|
||||
$keys[] = $kk;
|
||||
}
|
||||
}
|
||||
|
||||
return $keys;
|
||||
}
|
||||
|
||||
return array_keys($models);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepareTotalCount()
|
||||
{
|
||||
if (!$this->query instanceof QueryInterface) {
|
||||
throw new InvalidConfigException('The "query" property must be an instance of a class that implements the QueryInterface e.g. yii\db\Query or its subclasses.');
|
||||
}
|
||||
$query = clone $this->query;
|
||||
return (int) $query->limit(-1)->offset(-1)->orderBy([])->count('*', $this->db);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setSort($value)
|
||||
{
|
||||
parent::setSort($value);
|
||||
if (($sort = $this->getSort()) !== false && $this->query instanceof ActiveQueryInterface) {
|
||||
/* @var $modelClass Model */
|
||||
$modelClass = $this->query->modelClass;
|
||||
$model = $modelClass::instance();
|
||||
if (empty($sort->attributes)) {
|
||||
foreach ($model->attributes() as $attribute) {
|
||||
$sort->attributes[$attribute] = [
|
||||
'asc' => [$attribute => SORT_ASC],
|
||||
'desc' => [$attribute => SORT_DESC],
|
||||
'label' => $model->getAttributeLabel($attribute),
|
||||
];
|
||||
}
|
||||
} else {
|
||||
foreach ($sort->attributes as $attribute => $config) {
|
||||
if (!isset($config['label'])) {
|
||||
$sort->attributes[$attribute]['label'] = $model->getAttributeLabel($attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
144
vendor/yiisoft/yii2/data/ArrayDataProvider.php
vendored
Normal file
144
vendor/yiisoft/yii2/data/ArrayDataProvider.php
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\data;
|
||||
|
||||
use yii\helpers\ArrayHelper;
|
||||
|
||||
/**
|
||||
* ArrayDataProvider implements a data provider based on a data array.
|
||||
*
|
||||
* The [[allModels]] property contains all data models that may be sorted and/or paginated.
|
||||
* ArrayDataProvider will provide the data after sorting and/or pagination.
|
||||
* You may configure the [[sort]] and [[pagination]] properties to
|
||||
* customize the sorting and pagination behaviors.
|
||||
*
|
||||
* Elements in the [[allModels]] array may be either objects (e.g. model objects)
|
||||
* or associative arrays (e.g. query results of DAO).
|
||||
* 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.
|
||||
*
|
||||
* Compared to [[ActiveDataProvider]], ArrayDataProvider could be less efficient
|
||||
* because it needs to have [[allModels]] ready.
|
||||
*
|
||||
* ArrayDataProvider may be used in the following way:
|
||||
*
|
||||
* ```php
|
||||
* $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.
|
||||
*
|
||||
* For more details and usage information on ArrayDataProvider, see the [guide article on data providers](guide:output-data-providers).
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class ArrayDataProvider extends BaseDataProvider
|
||||
{
|
||||
/**
|
||||
* @var string|callable the column that is used as the key of the data models.
|
||||
* This can be either a column name, or a callable that returns the key value of a given data model.
|
||||
* If this is not set, the index of the [[models]] array will be used.
|
||||
* @see getKeys()
|
||||
*/
|
||||
public $key;
|
||||
/**
|
||||
* @var array the data that is not paginated or sorted. When pagination is enabled,
|
||||
* this property usually contains more elements than [[models]].
|
||||
* The array elements must use zero-based integer keys.
|
||||
*/
|
||||
public $allModels;
|
||||
/**
|
||||
* @var string the name of the [[\yii\base\Model|Model]] class that will be represented.
|
||||
* This property is used to get columns' names.
|
||||
* @since 2.0.9
|
||||
*/
|
||||
public $modelClass;
|
||||
|
||||
|
||||
/**
|
||||
* {@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(), true);
|
||||
}
|
||||
}
|
||||
|
||||
return $models;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepareKeys($models)
|
||||
{
|
||||
if ($this->key !== null) {
|
||||
$keys = [];
|
||||
foreach ($models as $model) {
|
||||
if (is_string($this->key)) {
|
||||
$keys[] = $model[$this->key];
|
||||
} else {
|
||||
$keys[] = call_user_func($this->key, $model);
|
||||
}
|
||||
}
|
||||
|
||||
return $keys;
|
||||
}
|
||||
|
||||
return array_keys($models);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepareTotalCount()
|
||||
{
|
||||
return count($this->allModels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the data models according to the given sort definition.
|
||||
* @param array $models the models to be sorted
|
||||
* @param Sort $sort the sort definition
|
||||
* @return array the sorted data models
|
||||
*/
|
||||
protected function sortModels($models, $sort)
|
||||
{
|
||||
$orders = $sort->getOrders();
|
||||
if (!empty($orders)) {
|
||||
ArrayHelper::multisort($models, array_keys($orders), array_values($orders));
|
||||
}
|
||||
|
||||
return $models;
|
||||
}
|
||||
}
|
||||
278
vendor/yiisoft/yii2/data/BaseDataProvider.php
vendored
Normal file
278
vendor/yiisoft/yii2/data/BaseDataProvider.php
vendored
Normal file
@@ -0,0 +1,278 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\data;
|
||||
|
||||
use Yii;
|
||||
use yii\base\Component;
|
||||
use yii\base\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* BaseDataProvider provides a base class that implements the [[DataProviderInterface]].
|
||||
*
|
||||
* For more details and usage information on BaseDataProvider, see the [guide article on data providers](guide:output-data-providers).
|
||||
*
|
||||
* @property int $count The number of data models in the current page. This property is read-only.
|
||||
* @property array $keys The list of key values corresponding to [[models]]. Each data model in [[models]] is
|
||||
* uniquely identified by the corresponding key value in this array.
|
||||
* @property array $models The list of data models in the current page.
|
||||
* @property Pagination|false $pagination The pagination object. If this is false, it means the pagination is
|
||||
* disabled. Note that the type of this property differs in getter and setter. See [[getPagination()]] and
|
||||
* [[setPagination()]] for details.
|
||||
* @property Sort|bool $sort The sorting object. If this is false, it means the sorting is disabled. Note that
|
||||
* the type of this property differs in getter and setter. See [[getSort()]] and [[setSort()]] for details.
|
||||
* @property int $totalCount Total number of possible data models.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
abstract class BaseDataProvider extends Component implements DataProviderInterface
|
||||
{
|
||||
/**
|
||||
* @var int Number of data providers on the current page. Used to generate unique IDs.
|
||||
*/
|
||||
private static $counter = 0;
|
||||
/**
|
||||
* @var string an ID that uniquely identifies the data provider among all data providers.
|
||||
* Generated automatically the following way in case it is not set:
|
||||
*
|
||||
* - First data provider ID is empty.
|
||||
* - Second and all subsequent data provider IDs are: "dp-1", "dp-2", etc.
|
||||
*/
|
||||
public $id;
|
||||
|
||||
private $_sort;
|
||||
private $_pagination;
|
||||
private $_keys;
|
||||
private $_models;
|
||||
private $_totalCount;
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
if ($this->id === null) {
|
||||
if (self::$counter > 0) {
|
||||
$this->id = 'dp-' . self::$counter;
|
||||
}
|
||||
self::$counter++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the data models that will be made available in the current page.
|
||||
* @return array the available data models
|
||||
*/
|
||||
abstract protected function prepareModels();
|
||||
|
||||
/**
|
||||
* Prepares the keys associated with the currently available data models.
|
||||
* @param array $models the available data models
|
||||
* @return array the keys
|
||||
*/
|
||||
abstract protected function prepareKeys($models);
|
||||
|
||||
/**
|
||||
* Returns a value indicating the total number of data models in this data provider.
|
||||
* @return int total number of data models in this data provider.
|
||||
*/
|
||||
abstract protected function prepareTotalCount();
|
||||
|
||||
/**
|
||||
* Prepares the data models and keys.
|
||||
*
|
||||
* This method will prepare the data models and keys that can be retrieved via
|
||||
* [[getModels()]] and [[getKeys()]].
|
||||
*
|
||||
* This method will be implicitly called by [[getModels()]] and [[getKeys()]] if it has not been called before.
|
||||
*
|
||||
* @param bool $forcePrepare whether to force data preparation even if it has been done before.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data models in the current page.
|
||||
* @return array the list of data models in the current page.
|
||||
*/
|
||||
public function getModels()
|
||||
{
|
||||
$this->prepare();
|
||||
|
||||
return $this->_models;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the data models in the current page.
|
||||
* @param array $models the models in the current page
|
||||
*/
|
||||
public function setModels($models)
|
||||
{
|
||||
$this->_models = $models;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key values associated with the data models.
|
||||
* @return array the list of key values corresponding to [[models]]. Each data model in [[models]]
|
||||
* is uniquely identified by the corresponding key value in this array.
|
||||
*/
|
||||
public function getKeys()
|
||||
{
|
||||
$this->prepare();
|
||||
|
||||
return $this->_keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the key values associated with the data models.
|
||||
* @param array $keys the list of key values corresponding to [[models]].
|
||||
*/
|
||||
public function setKeys($keys)
|
||||
{
|
||||
$this->_keys = $keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of data models in the current page.
|
||||
* @return int the number of data models in the current page.
|
||||
*/
|
||||
public function getCount()
|
||||
{
|
||||
return count($this->getModels());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of data models.
|
||||
* When [[pagination]] is false, this returns the same value as [[count]].
|
||||
* Otherwise, it will call [[prepareTotalCount()]] to get the count.
|
||||
* @return int total number of possible data models.
|
||||
*/
|
||||
public function getTotalCount()
|
||||
{
|
||||
if ($this->getPagination() === false) {
|
||||
return $this->getCount();
|
||||
} elseif ($this->_totalCount === null) {
|
||||
$this->_totalCount = $this->prepareTotalCount();
|
||||
}
|
||||
|
||||
return $this->_totalCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the total number of data models.
|
||||
* @param int $value the total number of data models.
|
||||
*/
|
||||
public function setTotalCount($value)
|
||||
{
|
||||
$this->_totalCount = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pagination object used by this data provider.
|
||||
* Note that you should call [[prepare()]] or [[getModels()]] first to get correct values
|
||||
* of [[Pagination::totalCount]] and [[Pagination::pageCount]].
|
||||
* @return Pagination|false the pagination object. If this is false, it means the pagination is disabled.
|
||||
*/
|
||||
public function getPagination()
|
||||
{
|
||||
if ($this->_pagination === null) {
|
||||
$this->setPagination([]);
|
||||
}
|
||||
|
||||
return $this->_pagination;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pagination for this data provider.
|
||||
* @param array|Pagination|bool $value the pagination to be used by this data provider.
|
||||
* This can be one of the following:
|
||||
*
|
||||
* - a configuration array for creating the pagination object. The "class" element defaults
|
||||
* to 'yii\data\Pagination'
|
||||
* - an instance of [[Pagination]] or its subclass
|
||||
* - false, if pagination needs to be disabled.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
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 InvalidArgumentException('Only Pagination instance, configuration array or false is allowed.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sorting object used by this data provider.
|
||||
* @return Sort|bool the sorting object. If this is false, it means the sorting is disabled.
|
||||
*/
|
||||
public function getSort()
|
||||
{
|
||||
if ($this->_sort === null) {
|
||||
$this->setSort([]);
|
||||
}
|
||||
|
||||
return $this->_sort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sort definition for this data provider.
|
||||
* @param array|Sort|bool $value the sort definition to be used by this data provider.
|
||||
* This can be one of the following:
|
||||
*
|
||||
* - a configuration array for creating the sort definition object. The "class" element defaults
|
||||
* to 'yii\data\Sort'
|
||||
* - an instance of [[Sort]] or its subclass
|
||||
* - false, if sorting needs to be disabled.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
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 InvalidArgumentException('Only Sort instance, configuration array or false is allowed.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the data provider.
|
||||
* After calling this method, if [[getModels()]], [[getKeys()]] or [[getTotalCount()]] is called again,
|
||||
* they will re-execute the query and return the latest data available.
|
||||
*/
|
||||
public function refresh()
|
||||
{
|
||||
$this->_totalCount = null;
|
||||
$this->_models = null;
|
||||
$this->_keys = null;
|
||||
}
|
||||
}
|
||||
834
vendor/yiisoft/yii2/data/DataFilter.php
vendored
Normal file
834
vendor/yiisoft/yii2/data/DataFilter.php
vendored
Normal file
@@ -0,0 +1,834 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\data;
|
||||
|
||||
use Yii;
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\base\Model;
|
||||
use yii\helpers\ArrayHelper;
|
||||
use yii\validators\BooleanValidator;
|
||||
use yii\validators\EachValidator;
|
||||
use yii\validators\NumberValidator;
|
||||
use yii\validators\StringValidator;
|
||||
use yii\validators\DateValidator;
|
||||
use yii\validators\Validator;
|
||||
|
||||
/**
|
||||
* DataFilter is a special [[Model]] for processing query filtering specification.
|
||||
* It allows validating and building a filter condition passed via request.
|
||||
*
|
||||
* Filter example:
|
||||
*
|
||||
* ```json
|
||||
* {
|
||||
* "or": [
|
||||
* {
|
||||
* "and": [
|
||||
* {
|
||||
* "name": "some name",
|
||||
* },
|
||||
* {
|
||||
* "price": "25",
|
||||
* }
|
||||
* ]
|
||||
* },
|
||||
* {
|
||||
* "id": {"in": [2, 5, 9]},
|
||||
* "price": {
|
||||
* "gt": 10,
|
||||
* "lt": 50
|
||||
* }
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* In the request the filter should be specified using a key name equal to [[filterAttributeName]]. Thus actual HTTP request body
|
||||
* will look like following:
|
||||
*
|
||||
* ```json
|
||||
* {
|
||||
* "filter": {"or": {...}},
|
||||
* "page": 2,
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Raw filter value should be assigned to [[filter]] property of the model.
|
||||
* You may populate it from request data via [[load()]] method:
|
||||
*
|
||||
* ```php
|
||||
* use yii\data\DataFilter;
|
||||
*
|
||||
* $dataFilter = new DataFilter();
|
||||
* $dataFilter->load(Yii::$app->request->getBodyParams());
|
||||
* ```
|
||||
*
|
||||
* In order to function this class requires a search model specified via [[searchModel]]. This search model should declare
|
||||
* all available search attributes and their validation rules. For example:
|
||||
*
|
||||
* ```php
|
||||
* class SearchModel extends \yii\base\Model
|
||||
* {
|
||||
* public $id;
|
||||
* public $name;
|
||||
*
|
||||
* public function rules()
|
||||
* {
|
||||
* return [
|
||||
* [['id', 'name'], 'trim'],
|
||||
* ['id', 'integer'],
|
||||
* ['name', 'string'],
|
||||
* ];
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* In order to reduce amount of classes, you may use [[\yii\base\DynamicModel]] instance as a [[searchModel]].
|
||||
* In this case you should specify [[searchModel]] using a PHP callable:
|
||||
*
|
||||
* ```php
|
||||
* function () {
|
||||
* return (new \yii\base\DynamicModel(['id' => null, 'name' => null]))
|
||||
* ->addRule(['id', 'name'], 'trim')
|
||||
* ->addRule('id', 'integer')
|
||||
* ->addRule('name', 'string');
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* You can use [[validate()]] method to check if filter value is valid. If validation fails you can use
|
||||
* [[getErrors()]] to get actual error messages.
|
||||
*
|
||||
* In order to acquire filter condition suitable for fetching data use [[build()]] method.
|
||||
*
|
||||
* > Note: This is a base class. Its implementation of [[build()]] simply returns normalized [[filter]] value.
|
||||
* In order to convert filter to particular format you should use descendant of this class that implements
|
||||
* [[buildInternal()]] method accordingly.
|
||||
*
|
||||
* @see ActiveDataFilter
|
||||
*
|
||||
* @property array $errorMessages Error messages in format `[errorKey => message]`. Note that the type of this
|
||||
* property differs in getter and setter. See [[getErrorMessages()]] and [[setErrorMessages()]] for details.
|
||||
* @property mixed $filter Raw filter value.
|
||||
* @property array $searchAttributeTypes Search attribute type map. Note that the type of this property
|
||||
* differs in getter and setter. See [[getSearchAttributeTypes()]] and [[setSearchAttributeTypes()]] for details.
|
||||
* @property Model $searchModel Model instance. Note that the type of this property differs in getter and
|
||||
* setter. See [[getSearchModel()]] and [[setSearchModel()]] for details.
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.0.13
|
||||
*/
|
||||
class DataFilter extends Model
|
||||
{
|
||||
const TYPE_INTEGER = 'integer';
|
||||
const TYPE_FLOAT = 'float';
|
||||
const TYPE_BOOLEAN = 'boolean';
|
||||
const TYPE_STRING = 'string';
|
||||
const TYPE_ARRAY = 'array';
|
||||
const TYPE_DATETIME = 'datetime';
|
||||
const TYPE_DATE = 'date';
|
||||
const TYPE_TIME = 'time';
|
||||
|
||||
/**
|
||||
* @var string name of the attribute that handles filter value.
|
||||
* The name is used to load data via [[load()]] method.
|
||||
*/
|
||||
public $filterAttributeName = 'filter';
|
||||
/**
|
||||
* @var string label for the filter attribute specified via [[filterAttributeName]].
|
||||
* It will be used during error messages composition.
|
||||
*/
|
||||
public $filterAttributeLabel;
|
||||
/**
|
||||
* @var array keywords or expressions that could be used in a filter.
|
||||
* Array keys are the expressions used in raw filter value obtained from user request.
|
||||
* Array values are internal build keys used in this class methods.
|
||||
*
|
||||
* Any unspecified keyword will not be recognized as a filter control and will be treated as
|
||||
* an attribute name. Thus you should avoid conflicts between control keywords and attribute names.
|
||||
* For example: in case you have control keyword 'like' and an attribute named 'like', specifying condition
|
||||
* for such attribute will be impossible.
|
||||
*
|
||||
* You may specify several keywords for the same filter build key, creating multiple aliases. For example:
|
||||
*
|
||||
* ```php
|
||||
* [
|
||||
* 'eq' => '=',
|
||||
* '=' => '=',
|
||||
* '==' => '=',
|
||||
* '===' => '=',
|
||||
* // ...
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* > Note: while specifying filter controls take actual data exchange format, which your API uses, in mind.
|
||||
* > Make sure each specified control keyword is valid for the format. For example, in XML tag name can start
|
||||
* > only with a letter character, thus controls like `>`, '=' or `$gt` will break the XML schema.
|
||||
*/
|
||||
public $filterControls = [
|
||||
'and' => 'AND',
|
||||
'or' => 'OR',
|
||||
'not' => 'NOT',
|
||||
'lt' => '<',
|
||||
'gt' => '>',
|
||||
'lte' => '<=',
|
||||
'gte' => '>=',
|
||||
'eq' => '=',
|
||||
'neq' => '!=',
|
||||
'in' => 'IN',
|
||||
'nin' => 'NOT IN',
|
||||
'like' => 'LIKE',
|
||||
];
|
||||
/**
|
||||
* @var array maps filter condition keywords to validation methods.
|
||||
* These methods are used by [[validateCondition()]] to validate raw filter conditions.
|
||||
*/
|
||||
public $conditionValidators = [
|
||||
'AND' => 'validateConjunctionCondition',
|
||||
'OR' => 'validateConjunctionCondition',
|
||||
'NOT' => 'validateBlockCondition',
|
||||
'<' => 'validateOperatorCondition',
|
||||
'>' => 'validateOperatorCondition',
|
||||
'<=' => 'validateOperatorCondition',
|
||||
'>=' => 'validateOperatorCondition',
|
||||
'=' => 'validateOperatorCondition',
|
||||
'!=' => 'validateOperatorCondition',
|
||||
'IN' => 'validateOperatorCondition',
|
||||
'NOT IN' => 'validateOperatorCondition',
|
||||
'LIKE' => 'validateOperatorCondition',
|
||||
];
|
||||
/**
|
||||
* @var array specifies the list of supported search attribute types per each operator.
|
||||
* This field should be in format: 'operatorKeyword' => ['type1', 'type2' ...].
|
||||
* Supported types list can be specified as `*`, which indicates that operator supports all types available.
|
||||
* Any unspecified keyword will not be considered as a valid operator.
|
||||
*/
|
||||
public $operatorTypes = [
|
||||
'<' => [self::TYPE_INTEGER, self::TYPE_FLOAT, self::TYPE_DATETIME, self::TYPE_DATE, self::TYPE_TIME],
|
||||
'>' => [self::TYPE_INTEGER, self::TYPE_FLOAT, self::TYPE_DATETIME, self::TYPE_DATE, self::TYPE_TIME],
|
||||
'<=' => [self::TYPE_INTEGER, self::TYPE_FLOAT, self::TYPE_DATETIME, self::TYPE_DATE, self::TYPE_TIME],
|
||||
'>=' => [self::TYPE_INTEGER, self::TYPE_FLOAT, self::TYPE_DATETIME, self::TYPE_DATE, self::TYPE_TIME],
|
||||
'=' => '*',
|
||||
'!=' => '*',
|
||||
'IN' => '*',
|
||||
'NOT IN' => '*',
|
||||
'LIKE' => [self::TYPE_STRING],
|
||||
];
|
||||
/**
|
||||
* @var array list of operators keywords, which should accept multiple values.
|
||||
*/
|
||||
public $multiValueOperators = [
|
||||
'IN',
|
||||
'NOT IN',
|
||||
];
|
||||
/**
|
||||
* @var array actual attribute names to be used in searched condition, in format: [filterAttribute => actualAttribute].
|
||||
* For example, in case of using table joins in the search query, attribute map may look like the following:
|
||||
*
|
||||
* ```php
|
||||
* [
|
||||
* 'authorName' => '{{author}}.[[name]]'
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* Attribute map will be applied to filter condition in [[normalize()]] method.
|
||||
*/
|
||||
public $attributeMap = [];
|
||||
|
||||
/**
|
||||
* @var array|\Closure list of error messages responding to invalid filter structure, in format: `[errorKey => message]`.
|
||||
*/
|
||||
private $_errorMessages;
|
||||
/**
|
||||
* @var mixed raw filter specification.
|
||||
*/
|
||||
private $_filter;
|
||||
/**
|
||||
* @var Model|array|string|callable model to be used for filter attributes validation.
|
||||
*/
|
||||
private $_searchModel;
|
||||
/**
|
||||
* @var array list of search attribute types in format: attributeName => type
|
||||
*/
|
||||
private $_searchAttributeTypes;
|
||||
|
||||
|
||||
/**
|
||||
* @return mixed raw filter value.
|
||||
*/
|
||||
public function getFilter()
|
||||
{
|
||||
return $this->_filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $filter raw filter value.
|
||||
*/
|
||||
public function setFilter($filter)
|
||||
{
|
||||
$this->_filter = $filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Model model instance.
|
||||
* @throws InvalidConfigException on invalid configuration.
|
||||
*/
|
||||
public function getSearchModel()
|
||||
{
|
||||
if (!is_object($this->_searchModel) || $this->_searchModel instanceof \Closure) {
|
||||
$model = Yii::createObject($this->_searchModel);
|
||||
if (!$model instanceof Model) {
|
||||
throw new InvalidConfigException('`' . get_class($this) . '::$searchModel` should be an instance of `' . Model::className() . '` or its DI compatible configuration.');
|
||||
}
|
||||
$this->_searchModel = $model;
|
||||
}
|
||||
return $this->_searchModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Model|array|string|callable $model model instance or its DI compatible configuration.
|
||||
* @throws InvalidConfigException on invalid configuration.
|
||||
*/
|
||||
public function setSearchModel($model)
|
||||
{
|
||||
if (is_object($model) && !$model instanceof Model && !$model instanceof \Closure) {
|
||||
throw new InvalidConfigException('`' . get_class($this) . '::$searchModel` should be an instance of `' . Model::className() . '` or its DI compatible configuration.');
|
||||
}
|
||||
$this->_searchModel = $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array search attribute type map.
|
||||
*/
|
||||
public function getSearchAttributeTypes()
|
||||
{
|
||||
if ($this->_searchAttributeTypes === null) {
|
||||
$this->_searchAttributeTypes = $this->detectSearchAttributeTypes();
|
||||
}
|
||||
return $this->_searchAttributeTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|null $searchAttributeTypes search attribute type map.
|
||||
*/
|
||||
public function setSearchAttributeTypes($searchAttributeTypes)
|
||||
{
|
||||
$this->_searchAttributeTypes = $searchAttributeTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes default value for [[searchAttributeTypes]] from the [[searchModel]] validation rules.
|
||||
* @return array attribute type map.
|
||||
*/
|
||||
protected function detectSearchAttributeTypes()
|
||||
{
|
||||
$model = $this->getSearchModel();
|
||||
|
||||
$attributeTypes = [];
|
||||
foreach ($model->activeAttributes() as $attribute) {
|
||||
$attributeTypes[$attribute] = self::TYPE_STRING;
|
||||
}
|
||||
|
||||
foreach ($model->getValidators() as $validator) {
|
||||
$type = $this->detectSearchAttributeType($validator);
|
||||
|
||||
if ($type !== null) {
|
||||
foreach ((array) $validator->attributes as $attribute) {
|
||||
$attributeTypes[$attribute] = $type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $attributeTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect attribute type from given validator.
|
||||
*
|
||||
* @param Validator validator from which to detect attribute type.
|
||||
* @return string|null detected attribute type.
|
||||
* @since 2.0.14
|
||||
*/
|
||||
protected function detectSearchAttributeType(Validator $validator)
|
||||
{
|
||||
if ($validator instanceof BooleanValidator) {
|
||||
return self::TYPE_BOOLEAN;
|
||||
}
|
||||
|
||||
if ($validator instanceof NumberValidator) {
|
||||
return $validator->integerOnly ? self::TYPE_INTEGER : self::TYPE_FLOAT;
|
||||
}
|
||||
|
||||
if ($validator instanceof StringValidator) {
|
||||
return self::TYPE_STRING;
|
||||
}
|
||||
|
||||
if ($validator instanceof EachValidator) {
|
||||
return self::TYPE_ARRAY;
|
||||
}
|
||||
|
||||
if ($validator instanceof DateValidator) {
|
||||
if ($validator->type == DateValidator::TYPE_DATETIME) {
|
||||
return self::TYPE_DATETIME;
|
||||
}
|
||||
|
||||
if ($validator->type == DateValidator::TYPE_TIME) {
|
||||
return self::TYPE_TIME;
|
||||
}
|
||||
return self::TYPE_DATE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array error messages in format `[errorKey => message]`.
|
||||
*/
|
||||
public function getErrorMessages()
|
||||
{
|
||||
if (!is_array($this->_errorMessages)) {
|
||||
if ($this->_errorMessages === null) {
|
||||
$this->_errorMessages = $this->defaultErrorMessages();
|
||||
} else {
|
||||
$this->_errorMessages = array_merge(
|
||||
$this->defaultErrorMessages(),
|
||||
call_user_func($this->_errorMessages)
|
||||
);
|
||||
}
|
||||
}
|
||||
return $this->_errorMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of error messages responding to invalid filter structure, in format: `[errorKey => message]`.
|
||||
* Message may contain placeholders that will be populated depending on the message context.
|
||||
* For each message a `{filter}` placeholder is available referring to the label for [[filterAttributeName]] attribute.
|
||||
* @param array|\Closure $errorMessages error messages in `[errorKey => message]` format, or a PHP callback returning them.
|
||||
*/
|
||||
public function setErrorMessages($errorMessages)
|
||||
{
|
||||
if (is_array($errorMessages)) {
|
||||
$errorMessages = array_merge($this->defaultErrorMessages(), $errorMessages);
|
||||
}
|
||||
$this->_errorMessages = $errorMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns default values for [[errorMessages]].
|
||||
* @return array default error messages in `[errorKey => message]` format.
|
||||
*/
|
||||
protected function defaultErrorMessages()
|
||||
{
|
||||
return [
|
||||
'invalidFilter' => Yii::t('yii', 'The format of {filter} is invalid.'),
|
||||
'operatorRequireMultipleOperands' => Yii::t('yii', 'Operator "{operator}" requires multiple operands.'),
|
||||
'unknownAttribute' => Yii::t('yii', 'Unknown filter attribute "{attribute}"'),
|
||||
'invalidAttributeValueFormat' => Yii::t('yii', 'Condition for "{attribute}" should be either a value or valid operator specification.'),
|
||||
'operatorRequireAttribute' => Yii::t('yii', 'Operator "{operator}" must be used with a search attribute.'),
|
||||
'unsupportedOperatorType' => Yii::t('yii', '"{attribute}" does not support operator "{operator}".'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses content of the message from [[errorMessages]], specified by message key.
|
||||
* @param string $messageKey message key.
|
||||
* @param array $params params to be parsed into the message.
|
||||
* @return string composed message string.
|
||||
*/
|
||||
protected function parseErrorMessage($messageKey, $params = [])
|
||||
{
|
||||
$messages = $this->getErrorMessages();
|
||||
if (isset($messages[$messageKey])) {
|
||||
$message = $messages[$messageKey];
|
||||
} else {
|
||||
$message = Yii::t('yii', 'The format of {filter} is invalid.');
|
||||
}
|
||||
|
||||
$params = array_merge(
|
||||
[
|
||||
'filter' => $this->getAttributeLabel($this->filterAttributeName),
|
||||
],
|
||||
$params
|
||||
);
|
||||
|
||||
return Yii::$app->getI18n()->format($message, $params, Yii::$app->language);
|
||||
}
|
||||
|
||||
// Model specific:
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function attributes()
|
||||
{
|
||||
return [
|
||||
$this->filterAttributeName,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function formName()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
[$this->filterAttributeName, 'validateFilter', 'skipOnEmpty' => false],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function attributeLabels()
|
||||
{
|
||||
return [
|
||||
$this->filterAttributeName => $this->filterAttributeLabel,
|
||||
];
|
||||
}
|
||||
|
||||
// Validation:
|
||||
|
||||
/**
|
||||
* Validates filter attribute value to match filer condition specification.
|
||||
*/
|
||||
public function validateFilter()
|
||||
{
|
||||
$value = $this->getFilter();
|
||||
if ($value !== null) {
|
||||
$this->validateCondition($value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates filter condition.
|
||||
* @param mixed $condition raw filter condition.
|
||||
*/
|
||||
protected function validateCondition($condition)
|
||||
{
|
||||
if (!is_array($condition)) {
|
||||
$this->addError($this->filterAttributeName, $this->parseErrorMessage('invalidFilter'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($condition)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($condition as $key => $value) {
|
||||
$method = 'validateAttributeCondition';
|
||||
if (isset($this->filterControls[$key])) {
|
||||
$controlKey = $this->filterControls[$key];
|
||||
if (isset($this->conditionValidators[$controlKey])) {
|
||||
$method = $this->conditionValidators[$controlKey];
|
||||
}
|
||||
}
|
||||
$this->$method($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates conjunction condition that consists of multiple independent ones.
|
||||
* This covers such operators as `and` and `or`.
|
||||
* @param string $operator raw operator control keyword.
|
||||
* @param mixed $condition raw condition.
|
||||
*/
|
||||
protected function validateConjunctionCondition($operator, $condition)
|
||||
{
|
||||
if (!is_array($condition) || !ArrayHelper::isIndexed($condition)) {
|
||||
$this->addError($this->filterAttributeName, $this->parseErrorMessage('operatorRequireMultipleOperands', ['operator' => $operator]));
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($condition as $part) {
|
||||
$this->validateCondition($part);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates block condition that consists of a single condition.
|
||||
* This covers such operators as `not`.
|
||||
* @param string $operator raw operator control keyword.
|
||||
* @param mixed $condition raw condition.
|
||||
*/
|
||||
protected function validateBlockCondition($operator, $condition)
|
||||
{
|
||||
$this->validateCondition($condition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates search condition for a particular attribute.
|
||||
* @param string $attribute search attribute name.
|
||||
* @param mixed $condition search condition.
|
||||
*/
|
||||
protected function validateAttributeCondition($attribute, $condition)
|
||||
{
|
||||
$attributeTypes = $this->getSearchAttributeTypes();
|
||||
if (!isset($attributeTypes[$attribute])) {
|
||||
$this->addError($this->filterAttributeName, $this->parseErrorMessage('unknownAttribute', ['attribute' => $attribute]));
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_array($condition)) {
|
||||
$operatorCount = 0;
|
||||
foreach ($condition as $rawOperator => $value) {
|
||||
if (isset($this->filterControls[$rawOperator])) {
|
||||
$operator = $this->filterControls[$rawOperator];
|
||||
if (isset($this->operatorTypes[$operator])) {
|
||||
$operatorCount++;
|
||||
$this->validateOperatorCondition($rawOperator, $value, $attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($operatorCount > 0) {
|
||||
if ($operatorCount < count($condition)) {
|
||||
$this->addError($this->filterAttributeName, $this->parseErrorMessage('invalidAttributeValueFormat', ['attribute' => $attribute]));
|
||||
}
|
||||
} else {
|
||||
// attribute may allow array value:
|
||||
$this->validateAttributeValue($attribute, $condition);
|
||||
}
|
||||
} else {
|
||||
$this->validateAttributeValue($attribute, $condition);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates operator condition.
|
||||
* @param string $operator raw operator control keyword.
|
||||
* @param mixed $condition attribute condition.
|
||||
* @param string $attribute attribute name.
|
||||
*/
|
||||
protected function validateOperatorCondition($operator, $condition, $attribute = null)
|
||||
{
|
||||
if ($attribute === null) {
|
||||
// absence of an attribute indicates that operator has been placed in a wrong position
|
||||
$this->addError($this->filterAttributeName, $this->parseErrorMessage('operatorRequireAttribute', ['operator' => $operator]));
|
||||
return;
|
||||
}
|
||||
|
||||
$internalOperator = $this->filterControls[$operator];
|
||||
|
||||
// check operator type :
|
||||
$operatorTypes = $this->operatorTypes[$internalOperator];
|
||||
if ($operatorTypes !== '*') {
|
||||
$attributeTypes = $this->getSearchAttributeTypes();
|
||||
$attributeType = $attributeTypes[$attribute];
|
||||
if (!in_array($attributeType, $operatorTypes, true)) {
|
||||
$this->addError($this->filterAttributeName, $this->parseErrorMessage('unsupportedOperatorType', ['attribute' => $attribute, 'operator' => $operator]));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array($internalOperator, $this->multiValueOperators, true)) {
|
||||
// multi-value operator:
|
||||
if (!is_array($condition)) {
|
||||
$this->addError($this->filterAttributeName, $this->parseErrorMessage('operatorRequireMultipleOperands', ['operator' => $operator]));
|
||||
} else {
|
||||
foreach ($condition as $v) {
|
||||
$this->validateAttributeValue($attribute, $v);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// single-value operator :
|
||||
$this->validateAttributeValue($attribute, $condition);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates attribute value in the scope of [[model]].
|
||||
* @param string $attribute attribute name.
|
||||
* @param mixed $value attribute value.
|
||||
*/
|
||||
protected function validateAttributeValue($attribute, $value)
|
||||
{
|
||||
$model = $this->getSearchModel();
|
||||
if (!$model->isAttributeSafe($attribute)) {
|
||||
$this->addError($this->filterAttributeName, $this->parseErrorMessage('unknownAttribute', ['attribute' => $attribute]));
|
||||
return;
|
||||
}
|
||||
|
||||
$model->{$attribute} = $value;
|
||||
if (!$model->validate([$attribute])) {
|
||||
$this->addError($this->filterAttributeName, $model->getFirstError($attribute));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates attribute value in the scope of [[searchModel]], applying attribute value filters if any.
|
||||
* @param string $attribute attribute name.
|
||||
* @param mixed $value attribute value.
|
||||
* @return mixed filtered attribute value.
|
||||
*/
|
||||
protected function filterAttributeValue($attribute, $value)
|
||||
{
|
||||
$model = $this->getSearchModel();
|
||||
if (!$model->isAttributeSafe($attribute)) {
|
||||
$this->addError($this->filterAttributeName, $this->parseErrorMessage('unknownAttribute', ['attribute' => $attribute]));
|
||||
return $value;
|
||||
}
|
||||
$model->{$attribute} = $value;
|
||||
if (!$model->validate([$attribute])) {
|
||||
$this->addError($this->filterAttributeName, $model->getFirstError($attribute));
|
||||
return $value;
|
||||
}
|
||||
|
||||
return $model->{$attribute};
|
||||
}
|
||||
|
||||
// Build:
|
||||
|
||||
/**
|
||||
* Builds actual filter specification form [[filter]] value.
|
||||
* @param bool $runValidation whether to perform validation (calling [[validate()]])
|
||||
* before building the filter. Defaults to `true`. If the validation fails, no filter will
|
||||
* be built and this method will return `false`.
|
||||
* @return mixed|false built actual filter value, or `false` if validation fails.
|
||||
*/
|
||||
public function build($runValidation = true)
|
||||
{
|
||||
if ($runValidation && !$this->validate()) {
|
||||
return false;
|
||||
}
|
||||
return $this->buildInternal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs actual filter build.
|
||||
* By default this method returns result of [[normalize()]].
|
||||
* The child class may override this method providing more specific implementation.
|
||||
* @return mixed built actual filter value.
|
||||
*/
|
||||
protected function buildInternal()
|
||||
{
|
||||
return $this->normalize(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes filter value, replacing raw keys according to [[filterControls]] and [[attributeMap]].
|
||||
* @param bool $runValidation whether to perform validation (calling [[validate()]])
|
||||
* before normalizing the filter. Defaults to `true`. If the validation fails, no filter will
|
||||
* be processed and this method will return `false`.
|
||||
* @return array|bool normalized filter value, or `false` if validation fails.
|
||||
*/
|
||||
public function normalize($runValidation = true)
|
||||
{
|
||||
if ($runValidation && !$this->validate()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$filter = $this->getFilter();
|
||||
if (!is_array($filter) || empty($filter)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->normalizeComplexFilter($filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes complex filter recursively.
|
||||
* @param array $filter raw filter.
|
||||
* @return array normalized filter.
|
||||
*/
|
||||
private function normalizeComplexFilter(array $filter)
|
||||
{
|
||||
$result = [];
|
||||
foreach ($filter as $key => $value) {
|
||||
if (isset($this->filterControls[$key])) {
|
||||
$key = $this->filterControls[$key];
|
||||
} elseif (isset($this->attributeMap[$key])) {
|
||||
$key = $this->attributeMap[$key];
|
||||
}
|
||||
if (is_array($value)) {
|
||||
$result[$key] = $this->normalizeComplexFilter($value);
|
||||
} else {
|
||||
$result[$key] = $value;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Property access:
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function canGetProperty($name, $checkVars = true, $checkBehaviors = true)
|
||||
{
|
||||
if ($name === $this->filterAttributeName) {
|
||||
return true;
|
||||
}
|
||||
return parent::canGetProperty($name, $checkVars, $checkBehaviors);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function canSetProperty($name, $checkVars = true, $checkBehaviors = true)
|
||||
{
|
||||
if ($name === $this->filterAttributeName) {
|
||||
return true;
|
||||
}
|
||||
return parent::canSetProperty($name, $checkVars, $checkBehaviors);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
if ($name === $this->filterAttributeName) {
|
||||
return $this->getFilter();
|
||||
}
|
||||
|
||||
return parent::__get($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __set($name, $value)
|
||||
{
|
||||
if ($name === $this->filterAttributeName) {
|
||||
$this->setFilter($value);
|
||||
} else {
|
||||
parent::__set($name, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __isset($name)
|
||||
{
|
||||
if ($name === $this->filterAttributeName) {
|
||||
return $this->getFilter() !== null;
|
||||
}
|
||||
|
||||
return parent::__isset($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __unset($name)
|
||||
{
|
||||
if ($name === $this->filterAttributeName) {
|
||||
$this->setFilter(null);
|
||||
} else {
|
||||
parent::__unset($name);
|
||||
}
|
||||
}
|
||||
}
|
||||
72
vendor/yiisoft/yii2/data/DataProviderInterface.php
vendored
Normal file
72
vendor/yiisoft/yii2/data/DataProviderInterface.php
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\data;
|
||||
|
||||
/**
|
||||
* DataProviderInterface is the interface that must be implemented by data provider classes.
|
||||
*
|
||||
* Data providers are components that sort and paginate data, and provide them to widgets
|
||||
* such as [[\yii\grid\GridView]], [[\yii\widgets\ListView]].
|
||||
*
|
||||
* For more details and usage information on DataProviderInterface, see the [guide article on data providers](guide:output-data-providers).
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
interface DataProviderInterface
|
||||
{
|
||||
/**
|
||||
* Prepares the data models and keys.
|
||||
*
|
||||
* This method will prepare the data models and keys that can be retrieved via
|
||||
* [[getModels()]] and [[getKeys()]].
|
||||
*
|
||||
* This method will be implicitly called by [[getModels()]] and [[getKeys()]] if it has not been called before.
|
||||
*
|
||||
* @param bool $forcePrepare whether to force data preparation even if it has been done before.
|
||||
*/
|
||||
public function prepare($forcePrepare = false);
|
||||
|
||||
/**
|
||||
* Returns the number of data models in the current page.
|
||||
* This is equivalent to `count($provider->getModels())`.
|
||||
* When [[getPagination|pagination]] is false, this is the same as [[getTotalCount|totalCount]].
|
||||
* @return int the number of data models in the current page.
|
||||
*/
|
||||
public function getCount();
|
||||
|
||||
/**
|
||||
* Returns the total number of data models.
|
||||
* When [[getPagination|pagination]] is false, this is the same as [[getCount|count]].
|
||||
* @return int total number of possible data models.
|
||||
*/
|
||||
public function getTotalCount();
|
||||
|
||||
/**
|
||||
* Returns the data models in the current page.
|
||||
* @return array the list of data models in the current page.
|
||||
*/
|
||||
public function getModels();
|
||||
|
||||
/**
|
||||
* Returns the key values associated with the data models.
|
||||
* @return array the list of key values corresponding to [[getModels|models]]. Each data model in [[getModels|models]]
|
||||
* is uniquely identified by the corresponding key value in this array.
|
||||
*/
|
||||
public function getKeys();
|
||||
|
||||
/**
|
||||
* @return Sort the sorting object. If this is false, it means the sorting is disabled.
|
||||
*/
|
||||
public function getSort();
|
||||
|
||||
/**
|
||||
* @return Pagination|false the pagination object. If this is false, it means the pagination is disabled.
|
||||
*/
|
||||
public function getPagination();
|
||||
}
|
||||
353
vendor/yiisoft/yii2/data/Pagination.php
vendored
Normal file
353
vendor/yiisoft/yii2/data/Pagination.php
vendored
Normal file
@@ -0,0 +1,353 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\data;
|
||||
|
||||
use Yii;
|
||||
use yii\base\BaseObject;
|
||||
use yii\web\Link;
|
||||
use yii\web\Linkable;
|
||||
use yii\web\Request;
|
||||
|
||||
/**
|
||||
* Pagination represents information relevant to pagination of data items.
|
||||
*
|
||||
* When data needs to be rendered in multiple pages, Pagination can be used to
|
||||
* represent information such as [[totalCount|total item count]], [[pageSize|page size]],
|
||||
* [[page|current page]], etc. These information can be passed to [[\yii\widgets\LinkPager|pagers]]
|
||||
* to render pagination buttons or links.
|
||||
*
|
||||
* The following example shows how to create a pagination object and feed it
|
||||
* to a pager.
|
||||
*
|
||||
* Controller action:
|
||||
*
|
||||
* ```php
|
||||
* public function actionIndex()
|
||||
* {
|
||||
* $query = Article::find()->where(['status' => 1]);
|
||||
* $countQuery = clone $query;
|
||||
* $pages = new Pagination(['totalCount' => $countQuery->count()]);
|
||||
* $models = $query->offset($pages->offset)
|
||||
* ->limit($pages->limit)
|
||||
* ->all();
|
||||
*
|
||||
* return $this->render('index', [
|
||||
* 'models' => $models,
|
||||
* 'pages' => $pages,
|
||||
* ]);
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* View:
|
||||
*
|
||||
* ```php
|
||||
* foreach ($models as $model) {
|
||||
* // display $model here
|
||||
* }
|
||||
*
|
||||
* // display pagination
|
||||
* echo LinkPager::widget([
|
||||
* 'pagination' => $pages,
|
||||
* ]);
|
||||
* ```
|
||||
*
|
||||
* For more details and usage information on Pagination, see the [guide article on pagination](guide:output-pagination).
|
||||
*
|
||||
* @property int $limit The limit of the data. This may be used to set the LIMIT value for a SQL statement for
|
||||
* fetching the current page of data. Note that if the page size is infinite, a value -1 will be returned. This
|
||||
* property is read-only.
|
||||
* @property array $links The links for navigational purpose. The array keys specify the purpose of the links
|
||||
* (e.g. [[LINK_FIRST]]), and the array values are the corresponding URLs. This property is read-only.
|
||||
* @property int $offset The offset of the data. This may be used to set the OFFSET value for a SQL statement
|
||||
* for fetching the current page of data. This property is read-only.
|
||||
* @property int $page The zero-based current page number.
|
||||
* @property int $pageCount Number of pages. This property is read-only.
|
||||
* @property int $pageSize The number of items per page. If it is less than 1, it means the page size is
|
||||
* infinite, and thus a single page contains all items.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Pagination extends BaseObject implements Linkable
|
||||
{
|
||||
const LINK_NEXT = 'next';
|
||||
const LINK_PREV = 'prev';
|
||||
const LINK_FIRST = 'first';
|
||||
const LINK_LAST = 'last';
|
||||
|
||||
/**
|
||||
* @var string name of the parameter storing the current page index.
|
||||
* @see params
|
||||
*/
|
||||
public $pageParam = 'page';
|
||||
/**
|
||||
* @var string name of the parameter storing the page size.
|
||||
* @see params
|
||||
*/
|
||||
public $pageSizeParam = 'per-page';
|
||||
/**
|
||||
* @var bool whether to always have the page parameter in the URL created by [[createUrl()]].
|
||||
* If false and [[page]] is 0, the page parameter will not be put in the URL.
|
||||
*/
|
||||
public $forcePageParam = true;
|
||||
/**
|
||||
* @var string the route of the controller action for displaying the paged contents.
|
||||
* If not set, it means using the currently requested route.
|
||||
*/
|
||||
public $route;
|
||||
/**
|
||||
* @var array parameters (name => value) that should be used to obtain the current page number
|
||||
* and to create new pagination URLs. If not set, all parameters from $_GET will be used instead.
|
||||
*
|
||||
* In order to add hash to all links use `array_merge($_GET, ['#' => 'my-hash'])`.
|
||||
*
|
||||
* The array element indexed by [[pageParam]] is considered to be the current page number (defaults to 0);
|
||||
* while the element indexed by [[pageSizeParam]] is treated as the page size (defaults to [[defaultPageSize]]).
|
||||
*/
|
||||
public $params;
|
||||
/**
|
||||
* @var \yii\web\UrlManager the URL manager used for creating pagination URLs. If not set,
|
||||
* the "urlManager" application component will be used.
|
||||
*/
|
||||
public $urlManager;
|
||||
/**
|
||||
* @var bool whether to check if [[page]] is within valid range.
|
||||
* When this property is true, the value of [[page]] will always be between 0 and ([[pageCount]]-1).
|
||||
* Because [[pageCount]] relies on the correct value of [[totalCount]] which may not be available
|
||||
* in some cases (e.g. MongoDB), you may want to set this property to be false to disable the page
|
||||
* number validation. By doing so, [[page]] will return the value indexed by [[pageParam]] in [[params]].
|
||||
*/
|
||||
public $validatePage = true;
|
||||
/**
|
||||
* @var int total number of items.
|
||||
*/
|
||||
public $totalCount = 0;
|
||||
/**
|
||||
* @var int the default page size. This property will be returned by [[pageSize]] when page size
|
||||
* cannot be determined by [[pageSizeParam]] from [[params]].
|
||||
*/
|
||||
public $defaultPageSize = 20;
|
||||
/**
|
||||
* @var array|false the page size limits. The first array element stands for the minimal page size, and the second
|
||||
* the maximal page size. If this is false, it means [[pageSize]] should always return the value of [[defaultPageSize]].
|
||||
*/
|
||||
public $pageSizeLimit = [1, 50];
|
||||
|
||||
/**
|
||||
* @var int number of items on each page.
|
||||
* If it is less than 1, it means the page size is infinite, and thus a single page contains all items.
|
||||
*/
|
||||
private $_pageSize;
|
||||
|
||||
|
||||
/**
|
||||
* @return int number of pages
|
||||
*/
|
||||
public function getPageCount()
|
||||
{
|
||||
$pageSize = $this->getPageSize();
|
||||
if ($pageSize < 1) {
|
||||
return $this->totalCount > 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
$totalCount = $this->totalCount < 0 ? 0 : (int) $this->totalCount;
|
||||
|
||||
return (int) (($totalCount + $pageSize - 1) / $pageSize);
|
||||
}
|
||||
|
||||
private $_page;
|
||||
|
||||
/**
|
||||
* Returns the zero-based current page number.
|
||||
* @param bool $recalculate whether to recalculate the current page based on the page size and item count.
|
||||
* @return int the zero-based current page number.
|
||||
*/
|
||||
public function getPage($recalculate = false)
|
||||
{
|
||||
if ($this->_page === null || $recalculate) {
|
||||
$page = (int) $this->getQueryParam($this->pageParam, 1) - 1;
|
||||
$this->setPage($page, true);
|
||||
}
|
||||
|
||||
return $this->_page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current page number.
|
||||
* @param int $value the zero-based index of the current page.
|
||||
* @param bool $validatePage whether to validate the page number. Note that in order
|
||||
* to validate the page number, both [[validatePage]] and this parameter must be true.
|
||||
*/
|
||||
public function setPage($value, $validatePage = false)
|
||||
{
|
||||
if ($value === null) {
|
||||
$this->_page = null;
|
||||
} else {
|
||||
$value = (int) $value;
|
||||
if ($validatePage && $this->validatePage) {
|
||||
$pageCount = $this->getPageCount();
|
||||
if ($value >= $pageCount) {
|
||||
$value = $pageCount - 1;
|
||||
}
|
||||
}
|
||||
if ($value < 0) {
|
||||
$value = 0;
|
||||
}
|
||||
$this->_page = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of items per page.
|
||||
* By default, this method will try to determine the page size by [[pageSizeParam]] in [[params]].
|
||||
* If the page size cannot be determined this way, [[defaultPageSize]] will be returned.
|
||||
* @return int the number of items per page. If it is less than 1, it means the page size is infinite,
|
||||
* and thus a single page contains all items.
|
||||
* @see pageSizeLimit
|
||||
*/
|
||||
public function getPageSize()
|
||||
{
|
||||
if ($this->_pageSize === null) {
|
||||
if (empty($this->pageSizeLimit)) {
|
||||
$pageSize = $this->defaultPageSize;
|
||||
$this->setPageSize($pageSize);
|
||||
} else {
|
||||
$pageSize = (int) $this->getQueryParam($this->pageSizeParam, $this->defaultPageSize);
|
||||
$this->setPageSize($pageSize, true);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_pageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $value the number of items per page.
|
||||
* @param bool $validatePageSize whether to validate page size.
|
||||
*/
|
||||
public function setPageSize($value, $validatePageSize = false)
|
||||
{
|
||||
if ($value === null) {
|
||||
$this->_pageSize = null;
|
||||
} else {
|
||||
$value = (int) $value;
|
||||
if ($validatePageSize && isset($this->pageSizeLimit[0], $this->pageSizeLimit[1]) && count($this->pageSizeLimit) === 2) {
|
||||
if ($value < $this->pageSizeLimit[0]) {
|
||||
$value = $this->pageSizeLimit[0];
|
||||
} elseif ($value > $this->pageSizeLimit[1]) {
|
||||
$value = $this->pageSizeLimit[1];
|
||||
}
|
||||
}
|
||||
$this->_pageSize = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the URL suitable for pagination with the specified page number.
|
||||
* This method is mainly called by pagers when creating URLs used to perform pagination.
|
||||
* @param int $page the zero-based page number that the URL should point to.
|
||||
* @param int $pageSize the number of items on each page. If not set, the value of [[pageSize]] will be used.
|
||||
* @param bool $absolute whether to create an absolute URL. Defaults to `false`.
|
||||
* @return string the created URL
|
||||
* @see params
|
||||
* @see forcePageParam
|
||||
*/
|
||||
public function createUrl($page, $pageSize = null, $absolute = false)
|
||||
{
|
||||
$page = (int) $page;
|
||||
$pageSize = (int) $pageSize;
|
||||
if (($params = $this->params) === null) {
|
||||
$request = Yii::$app->getRequest();
|
||||
$params = $request instanceof Request ? $request->getQueryParams() : [];
|
||||
}
|
||||
if ($page > 0 || $page == 0 && $this->forcePageParam) {
|
||||
$params[$this->pageParam] = $page + 1;
|
||||
} else {
|
||||
unset($params[$this->pageParam]);
|
||||
}
|
||||
if ($pageSize <= 0) {
|
||||
$pageSize = $this->getPageSize();
|
||||
}
|
||||
if ($pageSize != $this->defaultPageSize) {
|
||||
$params[$this->pageSizeParam] = $pageSize;
|
||||
} else {
|
||||
unset($params[$this->pageSizeParam]);
|
||||
}
|
||||
$params[0] = $this->route === null ? Yii::$app->controller->getRoute() : $this->route;
|
||||
$urlManager = $this->urlManager === null ? Yii::$app->getUrlManager() : $this->urlManager;
|
||||
if ($absolute) {
|
||||
return $urlManager->createAbsoluteUrl($params);
|
||||
}
|
||||
|
||||
return $urlManager->createUrl($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int the offset of the data. This may be used to set the
|
||||
* OFFSET value for a SQL statement for fetching the current page of data.
|
||||
*/
|
||||
public function getOffset()
|
||||
{
|
||||
$pageSize = $this->getPageSize();
|
||||
|
||||
return $pageSize < 1 ? 0 : $this->getPage() * $pageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int the limit of the data. This may be used to set the
|
||||
* LIMIT value for a SQL statement for fetching the current page of data.
|
||||
* Note that if the page size is infinite, a value -1 will be returned.
|
||||
*/
|
||||
public function getLimit()
|
||||
{
|
||||
$pageSize = $this->getPageSize();
|
||||
|
||||
return $pageSize < 1 ? -1 : $pageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a whole set of links for navigating to the first, last, next and previous pages.
|
||||
* @param bool $absolute whether the generated URLs should be absolute.
|
||||
* @return array the links for navigational purpose. The array keys specify the purpose of the links (e.g. [[LINK_FIRST]]),
|
||||
* and the array values are the corresponding URLs.
|
||||
*/
|
||||
public function getLinks($absolute = false)
|
||||
{
|
||||
$currentPage = $this->getPage();
|
||||
$pageCount = $this->getPageCount();
|
||||
$links = [
|
||||
Link::REL_SELF => $this->createUrl($currentPage, null, $absolute),
|
||||
];
|
||||
if ($currentPage > 0) {
|
||||
$links[self::LINK_FIRST] = $this->createUrl(0, null, $absolute);
|
||||
$links[self::LINK_PREV] = $this->createUrl($currentPage - 1, null, $absolute);
|
||||
}
|
||||
if ($currentPage < $pageCount - 1) {
|
||||
$links[self::LINK_NEXT] = $this->createUrl($currentPage + 1, null, $absolute);
|
||||
$links[self::LINK_LAST] = $this->createUrl($pageCount - 1, null, $absolute);
|
||||
}
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the specified query parameter.
|
||||
* This method returns the named parameter value from [[params]]. Null is returned if the value does not exist.
|
||||
* @param string $name the parameter name
|
||||
* @param string $defaultValue the value to be returned when the specified parameter does not exist in [[params]].
|
||||
* @return string the parameter value
|
||||
*/
|
||||
protected function getQueryParam($name, $defaultValue = null)
|
||||
{
|
||||
if (($params = $this->params) === null) {
|
||||
$request = Yii::$app->getRequest();
|
||||
$params = $request instanceof Request ? $request->getQueryParams() : [];
|
||||
}
|
||||
|
||||
return isset($params[$name]) && is_scalar($params[$name]) ? $params[$name] : $defaultValue;
|
||||
}
|
||||
}
|
||||
464
vendor/yiisoft/yii2/data/Sort.php
vendored
Normal file
464
vendor/yiisoft/yii2/data/Sort.php
vendored
Normal file
@@ -0,0 +1,464 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\data;
|
||||
|
||||
use Yii;
|
||||
use yii\base\BaseObject;
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\helpers\Html;
|
||||
use yii\helpers\Inflector;
|
||||
use yii\web\Request;
|
||||
|
||||
/**
|
||||
* Sort represents information relevant to sorting.
|
||||
*
|
||||
* When data needs to be sorted according to one or several attributes,
|
||||
* we can use Sort to represent the sorting information and generate
|
||||
* appropriate hyperlinks that can lead to sort actions.
|
||||
*
|
||||
* A typical usage example is as follows,
|
||||
*
|
||||
* ```php
|
||||
* public function actionIndex()
|
||||
* {
|
||||
* $sort = new Sort([
|
||||
* 'attributes' => [
|
||||
* 'age',
|
||||
* 'name' => [
|
||||
* 'asc' => ['first_name' => SORT_ASC, 'last_name' => SORT_ASC],
|
||||
* 'desc' => ['first_name' => SORT_DESC, 'last_name' => SORT_DESC],
|
||||
* 'default' => SORT_DESC,
|
||||
* 'label' => 'Name',
|
||||
* ],
|
||||
* ],
|
||||
* ]);
|
||||
*
|
||||
* $models = Article::find()
|
||||
* ->where(['status' => 1])
|
||||
* ->orderBy($sort->orders)
|
||||
* ->all();
|
||||
*
|
||||
* return $this->render('index', [
|
||||
* 'models' => $models,
|
||||
* 'sort' => $sort,
|
||||
* ]);
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* View:
|
||||
*
|
||||
* ```php
|
||||
* // display links leading to sort actions
|
||||
* echo $sort->link('name') . ' | ' . $sort->link('age');
|
||||
*
|
||||
* foreach ($models as $model) {
|
||||
* // display $model here
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* In the above, we declare two [[attributes]] that support sorting: `name` and `age`.
|
||||
* We pass the sort information to the Article query so that the query results are
|
||||
* sorted by the orders specified by the Sort object. In the view, we show two hyperlinks
|
||||
* that can lead to pages with the data sorted by the corresponding attributes.
|
||||
*
|
||||
* For more details and usage information on Sort, see the [guide article on sorting](guide:output-sorting).
|
||||
*
|
||||
* @property array $attributeOrders Sort directions indexed by attribute names. Sort direction can be either
|
||||
* `SORT_ASC` for ascending order or `SORT_DESC` for descending order. Note that the type of this property
|
||||
* differs in getter and setter. See [[getAttributeOrders()]] and [[setAttributeOrders()]] for details.
|
||||
* @property array $orders The columns (keys) and their corresponding sort directions (values). This can be
|
||||
* passed to [[\yii\db\Query::orderBy()]] to construct a DB query. This property is read-only.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Sort extends BaseObject
|
||||
{
|
||||
/**
|
||||
* @var bool whether the sorting can be applied to multiple attributes simultaneously.
|
||||
* Defaults to `false`, which means each time the data can only be sorted by one attribute.
|
||||
*/
|
||||
public $enableMultiSort = false;
|
||||
/**
|
||||
* @var array list of attributes that are allowed to be sorted. Its syntax can be
|
||||
* described using the following example:
|
||||
*
|
||||
* ```php
|
||||
* [
|
||||
* 'age',
|
||||
* 'name' => [
|
||||
* 'asc' => ['first_name' => SORT_ASC, 'last_name' => SORT_ASC],
|
||||
* 'desc' => ['first_name' => SORT_DESC, 'last_name' => SORT_DESC],
|
||||
* 'default' => SORT_DESC,
|
||||
* 'label' => 'Name',
|
||||
* ],
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* In the above, two attributes are declared: `age` and `name`. The `age` attribute is
|
||||
* a simple attribute which is equivalent to the following:
|
||||
*
|
||||
* ```php
|
||||
* 'age' => [
|
||||
* 'asc' => ['age' => SORT_ASC],
|
||||
* 'desc' => ['age' => SORT_DESC],
|
||||
* 'default' => SORT_ASC,
|
||||
* 'label' => Inflector::camel2words('age'),
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* Since 2.0.12 particular sort direction can be also specified as direct sort expression, like following:
|
||||
*
|
||||
* ```php
|
||||
* 'name' => [
|
||||
* 'asc' => '[[last_name]] ASC NULLS FIRST', // PostgreSQL specific feature
|
||||
* 'desc' => '[[last_name]] DESC NULLS LAST',
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* The `name` attribute is a composite attribute:
|
||||
*
|
||||
* - The `name` key represents the attribute name which will appear in the URLs leading
|
||||
* to sort actions.
|
||||
* - The `asc` and `desc` elements specify how to sort by the attribute in ascending
|
||||
* and descending orders, respectively. Their values represent the actual columns and
|
||||
* the directions by which the data should be sorted by.
|
||||
* - The `default` element specifies by which direction the attribute should be sorted
|
||||
* if it is not currently sorted (the default value is ascending order).
|
||||
* - The `label` element specifies what label should be used when calling [[link()]] to create
|
||||
* a sort link. If not set, [[Inflector::camel2words()]] will be called to get a label.
|
||||
* Note that it will not be HTML-encoded.
|
||||
*
|
||||
* Note that if the Sort object is already created, you can only use the full format
|
||||
* to configure every attribute. Each attribute must include these elements: `asc` and `desc`.
|
||||
*/
|
||||
public $attributes = [];
|
||||
/**
|
||||
* @var string the name of the parameter that specifies which attributes to be sorted
|
||||
* in which direction. Defaults to `sort`.
|
||||
* @see params
|
||||
*/
|
||||
public $sortParam = 'sort';
|
||||
/**
|
||||
* @var array the order that should be used when the current request does not specify any order.
|
||||
* The array keys are attribute names and the array values are the corresponding sort directions. For example,
|
||||
*
|
||||
* ```php
|
||||
* [
|
||||
* 'name' => SORT_ASC,
|
||||
* 'created_at' => SORT_DESC,
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* @see attributeOrders
|
||||
*/
|
||||
public $defaultOrder;
|
||||
/**
|
||||
* @var string the route of the controller action for displaying the sorted contents.
|
||||
* If not set, it means using the currently requested route.
|
||||
*/
|
||||
public $route;
|
||||
/**
|
||||
* @var string the character used to separate different attributes that need to be sorted by.
|
||||
*/
|
||||
public $separator = ',';
|
||||
/**
|
||||
* @var array parameters (name => value) that should be used to obtain the current sort directions
|
||||
* and to create new sort URLs. If not set, `$_GET` will be used instead.
|
||||
*
|
||||
* In order to add hash to all links use `array_merge($_GET, ['#' => 'my-hash'])`.
|
||||
*
|
||||
* The array element indexed by [[sortParam]] is considered to be the current sort directions.
|
||||
* If the element does not exist, the [[defaultOrder|default order]] will be used.
|
||||
*
|
||||
* @see sortParam
|
||||
* @see defaultOrder
|
||||
*/
|
||||
public $params;
|
||||
/**
|
||||
* @var \yii\web\UrlManager the URL manager used for creating sort URLs. If not set,
|
||||
* the `urlManager` application component will be used.
|
||||
*/
|
||||
public $urlManager;
|
||||
|
||||
|
||||
/**
|
||||
* Normalizes the [[attributes]] property.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$attributes = [];
|
||||
foreach ($this->attributes as $name => $attribute) {
|
||||
if (!is_array($attribute)) {
|
||||
$attributes[$attribute] = [
|
||||
'asc' => [$attribute => SORT_ASC],
|
||||
'desc' => [$attribute => SORT_DESC],
|
||||
];
|
||||
} elseif (!isset($attribute['asc'], $attribute['desc'])) {
|
||||
$attributes[$name] = array_merge([
|
||||
'asc' => [$name => SORT_ASC],
|
||||
'desc' => [$name => SORT_DESC],
|
||||
], $attribute);
|
||||
} else {
|
||||
$attributes[$name] = $attribute;
|
||||
}
|
||||
}
|
||||
$this->attributes = $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the columns and their corresponding sort directions.
|
||||
* @param bool $recalculate whether to recalculate the sort directions
|
||||
* @return array the columns (keys) and their corresponding sort directions (values).
|
||||
* This can be passed to [[\yii\db\Query::orderBy()]] to construct a DB query.
|
||||
*/
|
||||
public function getOrders($recalculate = false)
|
||||
{
|
||||
$attributeOrders = $this->getAttributeOrders($recalculate);
|
||||
$orders = [];
|
||||
foreach ($attributeOrders as $attribute => $direction) {
|
||||
$definition = $this->attributes[$attribute];
|
||||
$columns = $definition[$direction === SORT_ASC ? 'asc' : 'desc'];
|
||||
if (is_array($columns) || $columns instanceof \Traversable) {
|
||||
foreach ($columns as $name => $dir) {
|
||||
$orders[$name] = $dir;
|
||||
}
|
||||
} else {
|
||||
$orders[] = $columns;
|
||||
}
|
||||
}
|
||||
|
||||
return $orders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var array the currently requested sort order as computed by [[getAttributeOrders]].
|
||||
*/
|
||||
private $_attributeOrders;
|
||||
|
||||
/**
|
||||
* Returns the currently requested sort information.
|
||||
* @param bool $recalculate whether to recalculate the sort directions
|
||||
* @return array sort directions indexed by attribute names.
|
||||
* Sort direction can be either `SORT_ASC` for ascending order or
|
||||
* `SORT_DESC` for descending order.
|
||||
*/
|
||||
public function getAttributeOrders($recalculate = false)
|
||||
{
|
||||
if ($this->_attributeOrders === null || $recalculate) {
|
||||
$this->_attributeOrders = [];
|
||||
if (($params = $this->params) === null) {
|
||||
$request = Yii::$app->getRequest();
|
||||
$params = $request instanceof Request ? $request->getQueryParams() : [];
|
||||
}
|
||||
if (isset($params[$this->sortParam])) {
|
||||
foreach ($this->parseSortParam($params[$this->sortParam]) as $attribute) {
|
||||
$descending = false;
|
||||
if (strncmp($attribute, '-', 1) === 0) {
|
||||
$descending = true;
|
||||
$attribute = substr($attribute, 1);
|
||||
}
|
||||
|
||||
if (isset($this->attributes[$attribute])) {
|
||||
$this->_attributeOrders[$attribute] = $descending ? SORT_DESC : SORT_ASC;
|
||||
if (!$this->enableMultiSort) {
|
||||
return $this->_attributeOrders;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (empty($this->_attributeOrders) && is_array($this->defaultOrder)) {
|
||||
$this->_attributeOrders = $this->defaultOrder;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_attributeOrders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the value of [[sortParam]] into an array of sort attributes.
|
||||
*
|
||||
* The format must be the attribute name only for ascending
|
||||
* or the attribute name prefixed with `-` for descending.
|
||||
*
|
||||
* For example the following return value will result in ascending sort by
|
||||
* `category` and descending sort by `created_at`:
|
||||
*
|
||||
* ```php
|
||||
* [
|
||||
* 'category',
|
||||
* '-created_at'
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* @param string $param the value of the [[sortParam]].
|
||||
* @return array the valid sort attributes.
|
||||
* @since 2.0.12
|
||||
* @see $separator for the attribute name separator.
|
||||
* @see $sortParam
|
||||
*/
|
||||
protected function parseSortParam($param)
|
||||
{
|
||||
return is_scalar($param) ? explode($this->separator, $param) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the currently sort information.
|
||||
* @param array|null $attributeOrders sort directions indexed by attribute names.
|
||||
* Sort direction can be either `SORT_ASC` for ascending order or
|
||||
* `SORT_DESC` for descending order.
|
||||
* @param bool $validate whether to validate given attribute orders against [[attributes]] and [[enableMultiSort]].
|
||||
* If validation is enabled incorrect entries will be removed.
|
||||
* @since 2.0.10
|
||||
*/
|
||||
public function setAttributeOrders($attributeOrders, $validate = true)
|
||||
{
|
||||
if ($attributeOrders === null || !$validate) {
|
||||
$this->_attributeOrders = $attributeOrders;
|
||||
} else {
|
||||
$this->_attributeOrders = [];
|
||||
foreach ($attributeOrders as $attribute => $order) {
|
||||
if (isset($this->attributes[$attribute])) {
|
||||
$this->_attributeOrders[$attribute] = $order;
|
||||
if (!$this->enableMultiSort) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sort direction of the specified attribute in the current request.
|
||||
* @param string $attribute the attribute name
|
||||
* @return bool|null Sort direction of the attribute. Can be either `SORT_ASC`
|
||||
* for ascending order or `SORT_DESC` for descending order. Null is returned
|
||||
* if the attribute is invalid or does not need to be sorted.
|
||||
*/
|
||||
public function getAttributeOrder($attribute)
|
||||
{
|
||||
$orders = $this->getAttributeOrders();
|
||||
|
||||
return isset($orders[$attribute]) ? $orders[$attribute] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a hyperlink that links to the sort action to sort by the specified attribute.
|
||||
* Based on the sort direction, the CSS class of the generated hyperlink will be appended
|
||||
* with "asc" or "desc".
|
||||
* @param string $attribute the attribute name by which the data should be sorted by.
|
||||
* @param array $options additional HTML attributes for the hyperlink tag.
|
||||
* There is one special attribute `label` which will be used as the label of the hyperlink.
|
||||
* If this is not set, the label defined in [[attributes]] will be used.
|
||||
* If no label is defined, [[\yii\helpers\Inflector::camel2words()]] will be called to get a label.
|
||||
* Note that it will not be HTML-encoded.
|
||||
* @return string the generated hyperlink
|
||||
* @throws InvalidConfigException if the attribute is unknown
|
||||
*/
|
||||
public function link($attribute, $options = [])
|
||||
{
|
||||
if (($direction = $this->getAttributeOrder($attribute)) !== null) {
|
||||
$class = $direction === SORT_DESC ? 'desc' : 'asc';
|
||||
if (isset($options['class'])) {
|
||||
$options['class'] .= ' ' . $class;
|
||||
} else {
|
||||
$options['class'] = $class;
|
||||
}
|
||||
}
|
||||
|
||||
$url = $this->createUrl($attribute);
|
||||
$options['data-sort'] = $this->createSortParam($attribute);
|
||||
|
||||
if (isset($options['label'])) {
|
||||
$label = $options['label'];
|
||||
unset($options['label']);
|
||||
} else {
|
||||
if (isset($this->attributes[$attribute]['label'])) {
|
||||
$label = $this->attributes[$attribute]['label'];
|
||||
} else {
|
||||
$label = Inflector::camel2words($attribute);
|
||||
}
|
||||
}
|
||||
|
||||
return Html::a($label, $url, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a URL for sorting the data by the specified attribute.
|
||||
* This method will consider the current sorting status given by [[attributeOrders]].
|
||||
* For example, if the current page already sorts the data by the specified attribute in ascending order,
|
||||
* then the URL created will lead to a page that sorts the data by the specified attribute in descending order.
|
||||
* @param string $attribute the attribute name
|
||||
* @param bool $absolute whether to create an absolute URL. Defaults to `false`.
|
||||
* @return string the URL for sorting. False if the attribute is invalid.
|
||||
* @throws InvalidConfigException if the attribute is unknown
|
||||
* @see attributeOrders
|
||||
* @see params
|
||||
*/
|
||||
public function createUrl($attribute, $absolute = false)
|
||||
{
|
||||
if (($params = $this->params) === null) {
|
||||
$request = Yii::$app->getRequest();
|
||||
$params = $request instanceof Request ? $request->getQueryParams() : [];
|
||||
}
|
||||
$params[$this->sortParam] = $this->createSortParam($attribute);
|
||||
$params[0] = $this->route === null ? Yii::$app->controller->getRoute() : $this->route;
|
||||
$urlManager = $this->urlManager === null ? Yii::$app->getUrlManager() : $this->urlManager;
|
||||
if ($absolute) {
|
||||
return $urlManager->createAbsoluteUrl($params);
|
||||
}
|
||||
|
||||
return $urlManager->createUrl($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the sort variable for the specified attribute.
|
||||
* The newly created sort variable can be used to create a URL that will lead to
|
||||
* sorting by the specified attribute.
|
||||
* @param string $attribute the attribute name
|
||||
* @return string the value of the sort variable
|
||||
* @throws InvalidConfigException if the specified attribute is not defined in [[attributes]]
|
||||
*/
|
||||
public function createSortParam($attribute)
|
||||
{
|
||||
if (!isset($this->attributes[$attribute])) {
|
||||
throw new InvalidConfigException("Unknown attribute: $attribute");
|
||||
}
|
||||
$definition = $this->attributes[$attribute];
|
||||
$directions = $this->getAttributeOrders();
|
||||
if (isset($directions[$attribute])) {
|
||||
$direction = $directions[$attribute] === SORT_DESC ? SORT_ASC : SORT_DESC;
|
||||
unset($directions[$attribute]);
|
||||
} else {
|
||||
$direction = isset($definition['default']) ? $definition['default'] : SORT_ASC;
|
||||
}
|
||||
|
||||
if ($this->enableMultiSort) {
|
||||
$directions = array_merge([$attribute => $direction], $directions);
|
||||
} else {
|
||||
$directions = [$attribute => $direction];
|
||||
}
|
||||
|
||||
$sorts = [];
|
||||
foreach ($directions as $attribute => $direction) {
|
||||
$sorts[] = $direction === SORT_DESC ? '-' . $attribute : $attribute;
|
||||
}
|
||||
|
||||
return implode($this->separator, $sorts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value indicating whether the sort definition supports sorting by the named attribute.
|
||||
* @param string $name the attribute name
|
||||
* @return bool whether the sort definition supports sorting by the named attribute.
|
||||
*/
|
||||
public function hasAttribute($name)
|
||||
{
|
||||
return isset($this->attributes[$name]);
|
||||
}
|
||||
}
|
||||
171
vendor/yiisoft/yii2/data/SqlDataProvider.php
vendored
Normal file
171
vendor/yiisoft/yii2/data/SqlDataProvider.php
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\data;
|
||||
|
||||
use Yii;
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\db\Connection;
|
||||
use yii\db\Expression;
|
||||
use yii\db\Query;
|
||||
use yii\di\Instance;
|
||||
|
||||
/**
|
||||
* SqlDataProvider implements a data provider based on a plain SQL statement.
|
||||
*
|
||||
* SqlDataProvider provides data in terms of arrays, each representing a row of query result.
|
||||
*
|
||||
* Like other data providers, SqlDataProvider also supports sorting and pagination.
|
||||
* It does so by modifying the given [[sql]] statement with "ORDER BY" and "LIMIT"
|
||||
* clauses. You may configure the [[sort]] and [[pagination]] properties to
|
||||
* customize sorting and pagination behaviors.
|
||||
*
|
||||
* SqlDataProvider may be used in the following way:
|
||||
*
|
||||
* ```php
|
||||
* $count = Yii::$app->db->createCommand('
|
||||
* SELECT COUNT(*) FROM user WHERE status=:status
|
||||
* ', [':status' => 1])->queryScalar();
|
||||
*
|
||||
* $dataProvider = new SqlDataProvider([
|
||||
* 'sql' => 'SELECT * FROM user WHERE status=:status',
|
||||
* 'params' => [':status' => 1],
|
||||
* 'totalCount' => $count,
|
||||
* 'sort' => [
|
||||
* 'attributes' => [
|
||||
* 'age',
|
||||
* 'name' => [
|
||||
* 'asc' => ['first_name' => SORT_ASC, 'last_name' => SORT_ASC],
|
||||
* 'desc' => ['first_name' => SORT_DESC, 'last_name' => SORT_DESC],
|
||||
* 'default' => SORT_DESC,
|
||||
* 'label' => 'Name',
|
||||
* ],
|
||||
* ],
|
||||
* ],
|
||||
* 'pagination' => [
|
||||
* 'pageSize' => 20,
|
||||
* ],
|
||||
* ]);
|
||||
*
|
||||
* // get the user records in the current page
|
||||
* $models = $dataProvider->getModels();
|
||||
* ```
|
||||
*
|
||||
* Note: if you want to use the pagination feature, you must configure the [[totalCount]] property
|
||||
* to be the total number of rows (without pagination). And if you want to use the sorting feature,
|
||||
* you must configure the [[sort]] property so that the provider knows which columns can be sorted.
|
||||
*
|
||||
* For more details and usage information on SqlDataProvider, see the [guide article on data providers](guide:output-data-providers).
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class SqlDataProvider extends BaseDataProvider
|
||||
{
|
||||
/**
|
||||
* @var Connection|array|string the DB connection object or the application component ID of the DB connection.
|
||||
* Starting from version 2.0.2, this can also be a configuration array for creating the object.
|
||||
*/
|
||||
public $db = 'db';
|
||||
/**
|
||||
* @var string the SQL statement to be used for fetching data rows.
|
||||
*/
|
||||
public $sql;
|
||||
/**
|
||||
* @var array parameters (name=>value) to be bound to the SQL statement.
|
||||
*/
|
||||
public $params = [];
|
||||
/**
|
||||
* @var string|callable the column that is used as the key of the data models.
|
||||
* This can be either a column name, or a callable that returns the key value of a given data model.
|
||||
*
|
||||
* If this is not set, the keys of the [[models]] array will be used.
|
||||
*/
|
||||
public $key;
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the DB connection component.
|
||||
* This method will initialize the [[db]] property to make sure it refers to a valid DB connection.
|
||||
* @throws InvalidConfigException if [[db]] is invalid.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
$this->db = Instance::ensure($this->db, Connection::className());
|
||||
if ($this->sql === null) {
|
||||
throw new InvalidConfigException('The "sql" property must be set.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepareModels()
|
||||
{
|
||||
$sort = $this->getSort();
|
||||
$pagination = $this->getPagination();
|
||||
if ($pagination === false && $sort === false) {
|
||||
return $this->db->createCommand($this->sql, $this->params)->queryAll();
|
||||
}
|
||||
|
||||
$sql = $this->sql;
|
||||
$orders = [];
|
||||
$limit = $offset = null;
|
||||
|
||||
if ($sort !== false) {
|
||||
$orders = $sort->getOrders();
|
||||
$pattern = '/\s+order\s+by\s+([\w\s,\.]+)$/i';
|
||||
if (preg_match($pattern, $sql, $matches)) {
|
||||
array_unshift($orders, new Expression($matches[1]));
|
||||
$sql = preg_replace($pattern, '', $sql);
|
||||
}
|
||||
}
|
||||
|
||||
if ($pagination !== false) {
|
||||
$pagination->totalCount = $this->getTotalCount();
|
||||
$limit = $pagination->getLimit();
|
||||
$offset = $pagination->getOffset();
|
||||
}
|
||||
|
||||
$sql = $this->db->getQueryBuilder()->buildOrderByAndLimit($sql, $orders, $limit, $offset);
|
||||
|
||||
return $this->db->createCommand($sql, $this->params)->queryAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepareKeys($models)
|
||||
{
|
||||
$keys = [];
|
||||
if ($this->key !== null) {
|
||||
foreach ($models as $model) {
|
||||
if (is_string($this->key)) {
|
||||
$keys[] = $model[$this->key];
|
||||
} else {
|
||||
$keys[] = call_user_func($this->key, $model);
|
||||
}
|
||||
}
|
||||
|
||||
return $keys;
|
||||
}
|
||||
|
||||
return array_keys($models);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepareTotalCount()
|
||||
{
|
||||
return (new Query([
|
||||
'from' => ['sub' => "({$this->sql})"],
|
||||
'params' => $this->params,
|
||||
]))->count('*', $this->db);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user