Passed
Pull Request — master (#20176)
by Loban
08:00
created

SqlDataProvider   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 108
Duplicated Lines 0 %

Test Coverage

Coverage 83.33%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 44
dl 0
loc 108
ccs 35
cts 42
cp 0.8333
rs 10
c 3
b 0
f 0
wmc 14

4 Methods

Rating   Name   Duplication   Size   Complexity  
A init() 0 6 2
A prepareKeys() 0 14 4
A prepareModels() 0 29 6
A prepareTotalCount() 0 12 2
1
<?php
2
/**
3
 * @link https://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license https://www.yiiframework.com/license/
6
 */
7
8
namespace yii\data;
9
10
use yii\base\InvalidConfigException;
11
use yii\db\Connection;
12
use yii\db\Expression;
13
use yii\db\Query;
14
use yii\di\Instance;
15
16
/**
17
 * SqlDataProvider implements a data provider based on a plain SQL statement.
18
 *
19
 * SqlDataProvider provides data in terms of arrays, each representing a row of query result.
20
 *
21
 * Like other data providers, SqlDataProvider also supports sorting and pagination.
22
 * It does so by modifying the given [[sql]] statement with "ORDER BY" and "LIMIT"
23
 * clauses. You may configure the [[sort]] and [[pagination]] properties to
24
 * customize sorting and pagination behaviors.
25
 *
26
 * SqlDataProvider may be used in the following way:
27
 *
28
 * ```php
29
 * $count = Yii::$app->db->createCommand('
30
 *     SELECT COUNT(*) FROM user WHERE status=:status
31
 * ', [':status' => 1])->queryScalar();
32
 *
33
 * $dataProvider = new SqlDataProvider([
34
 *     'sql' => 'SELECT * FROM user WHERE status=:status',
35
 *     'params' => [':status' => 1],
36
 *     'totalCount' => $count,
37
 *     'sort' => [
38
 *         'attributes' => [
39
 *             'age',
40
 *             'name' => [
41
 *                 'asc' => ['first_name' => SORT_ASC, 'last_name' => SORT_ASC],
42
 *                 'desc' => ['first_name' => SORT_DESC, 'last_name' => SORT_DESC],
43
 *                 'default' => SORT_DESC,
44
 *                 'label' => 'Name',
45
 *             ],
46
 *         ],
47
 *     ],
48
 *     'pagination' => [
49
 *         'pageSize' => 20,
50
 *     ],
51
 * ]);
52
 *
53
 * // get the user records in the current page
54
 * $models = $dataProvider->getModels();
55
 * ```
56
 *
57
 * Note: if you want to use the pagination feature, you must configure the [[totalCount]] property
58
 * to be the total number of rows (without pagination). And if you want to use the sorting feature,
59
 * you must configure the [[sort]] property so that the provider knows which columns can be sorted.
60
 *
61
 * For more details and usage information on SqlDataProvider, see the [guide article on data providers](guide:output-data-providers).
62
 *
63
 * @author Qiang Xue <[email protected]>
64
 * @since 2.0
65
 */
66
class SqlDataProvider extends BaseDataProvider
67
{
68
    /**
69
     * @var Connection|array|string the DB connection object or the application component ID of the DB connection.
70
     * Starting from version 2.0.2, this can also be a configuration array for creating the object.
71
     */
72
    public $db = 'db';
73
    /**
74
     * @var string the SQL statement to be used for fetching data rows.
75
     */
76
    public $sql;
77
    /**
78
     * @var array parameters (name=>value) to be bound to the SQL statement.
79
     */
80
    public $params = [];
81
    /**
82
     * @var string|callable|null the column that is used as the key of the data models.
83
     * This can be either a column name, or a callable that returns the key value of a given data model.
84
     *
85
     * If this is not set, the keys of the [[models]] array will be used.
86
     */
87
    public $key;
88
89
90
    /**
91
     * Initializes the DB connection component.
92
     * This method will initialize the [[db]] property to make sure it refers to a valid DB connection.
93
     * @throws InvalidConfigException if [[db]] is invalid.
94
     */
95 11
    public function init()
96
    {
97 11
        parent::init();
98 11
        $this->db = Instance::ensure($this->db, Connection::className());
0 ignored issues
show
Deprecated Code introduced by
The function yii\base\BaseObject::className() has been deprecated: since 2.0.14. On PHP >=5.5, use `::class` instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

98
        $this->db = Instance::ensure($this->db, /** @scrutinizer ignore-deprecated */ Connection::className());

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
99 11
        if ($this->sql === null) {
100
            throw new InvalidConfigException('The "sql" property must be set.');
101
        }
102
    }
103
104
    /**
105
     * {@inheritdoc}
106
     */
107 9
    protected function prepareModels()
108
    {
109 9
        $sort = $this->getSort();
110 9
        $pagination = $this->getPagination();
111 9
        if ($pagination === false && $sort === false) {
112
            return $this->db->createCommand($this->sql, $this->params)->queryAll();
113
        }
114
115 9
        $sql = $this->sql;
116 9
        $orders = [];
117 9
        $limit = $offset = null;
118
119 9
        if ($sort !== false) {
120 9
            $orders = $sort->getOrders();
121 9
            $pattern = '/\s+order\s+by\s+([\w\s,\."`\[\]]+)$/i';
122 9
            if (preg_match($pattern, $sql, $matches)) {
123 8
                array_unshift($orders, new Expression($matches[1]));
124 8
                $sql = preg_replace($pattern, '', $sql);
125
            }
126
        }
127
128 9
        if ($pagination !== false) {
129 9
            $limit = $pagination->getLimit();
130 9
            $offset = $pagination->getOffset();
131
        }
132
133 9
        $sql = $this->db->getQueryBuilder()->buildOrderByAndLimit($sql, $orders, $limit, $offset);
134
135 9
        return $this->db->createCommand($sql, $this->params)->queryAll();
136
    }
137
138
    /**
139
     * {@inheritdoc}
140
     */
141 9
    protected function prepareKeys($models)
142
    {
143 9
        $keys = [];
144 9
        if ($this->key !== null) {
145
            foreach ($models as $model) {
146
                if (is_string($this->key)) {
147
                    $keys[] = $model[$this->key];
148
                } else {
149
                    $keys[] = call_user_func($this->key, $model);
150
                }
151
            }
152
            return $keys;
153
        }
154 9
        return array_keys($models);
155
    }
156
157
    private $_totalCount = [];
158
159
    /**
160
     * {@inheritdoc}
161
     */
162 11
    protected function prepareTotalCount()
163
    {
164 11
        $query = new Query([
165 11
            'from' => ['sub' => "({$this->sql})"],
166 11
            'params' => $this->params,
167 11
        ]);
168 11
        $key = md5((string)$query);
169
170 11
        if (isset($this->_totalCount[$key]) === false) {
171 11
            $this->_totalCount[$key] = (int)$query->count('*', $this->db);
172
        }
173 11
        return $this->_totalCount[$key];
174
    }
175
}
176