Passed
Push — master ( 364bec...91edba )
by Wilmer
02:56
created

ActiveDataProvider   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 177
Duplicated Lines 0 %

Test Coverage

Coverage 36.51%

Importance

Changes 0
Metric Value
eloc 62
dl 0
loc 177
ccs 23
cts 63
cp 0.3651
rs 10
c 0
b 0
f 0
wmc 24

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A prepareQuery() 0 21 5
A prepareActiveRecord() 0 9 2
A prepareTotalCount() 0 12 2
B prepareKeys() 0 41 9
A prepareModels() 0 25 5
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\ActiveRecord;
6
7
use Yiisoft\Db\Connection\ConnectionInterface;
8
use Yiisoft\Db\Data\DataProvider;
9
use Yiisoft\Db\Exception\InvalidConfigException;
10
use Yiisoft\Db\Query\QueryInterface;
11
12
use function array_keys;
13
use function call_user_func;
14
use function count;
15
use function is_string;
16
17
/**
18
 * ActiveDataProvider implements a data provider based on {@see \Yiisoft\Db\Query\Query} and
19
 * {@see \Yiisoft\ActiveRecord\ActiveQuery}.
20
 *
21
 * ActiveDataProvider provides data by performing DB queries using {@see query}.
22
 *
23
 * The following is an example of using ActiveDataProvider to provide ActiveRecord instances:
24
 *
25
 * ```php
26
 *    $provider = new ActiveDataProvider(
27
 *        $db, // connection db
28
 *        Order::find()->orderBy('id')
29
 *    );
30
 * ```
31
 *
32
 * And the following example shows how to use ActiveDataProvider without ActiveRecord:
33
 *
34
 * ```php
35
 *    $query = new Query($db);
36
 *
37
 *    $provider = new ActiveDataProvider(
38
 *        $db, // connection db
39
 *        $query->from('order')->orderBy('id')
40
 *    );
41
 * ```
42
 *
43
 * For more details and usage information on ActiveDataProvider, see the
44
 * [guide article on data providers](guide:output-data-providers).
45
 */
46
final class ActiveDataProvider extends DataProvider
47
{
48
    /**
49
     * @var QueryInterface|null the query that is used to fetch data models and {@see totalCount}
50
     *
51
     * if it is not explicitly set.
52
     */
53
    private ?QueryInterface $query = null;
54
55
    /**
56
     * @var string|callable the column that is used as the key of the data models.
57
     *
58
     * This can be either a column name, or a callable that returns the key value of a given data model.
59
     *
60
     * If this is not set, the following rules will be used to determine the keys of the data models:
61
     *
62
     * - If {@see query} is an {@see \Yiisoft\ActiveRecord\ActiveQuery} instance, the primary keys of
63
     * {@see \Yiisoft\ActiveRecord\ActiveQuery::modelClass} will be used.
64
     *
65
     * - Otherwise, the keys of the {@see models} array will be used.
66
     *
67
     * @see getKeys()
68
     */
69
    private $key;
70
71 20
    public function __construct(ConnectionInterface $db, QueryInterface $query)
72
    {
73 20
        $this->db = $db;
0 ignored issues
show
Bug Best Practice introduced by
The property db does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
74 20
        $this->query = $query;
75
76 20
        parent::__construct();
77 20
    }
78
79
    protected function prepareActiveRecord(): array
80
    {
81
        $query = $this->prepareQuery();
82
83
        if ($query->shouldEmulateExecution()) {
0 ignored issues
show
Bug introduced by
The method shouldEmulateExecution() does not exist on Yiisoft\Db\Query\QueryInterface. It seems like you code against a sub-type of said class. However, the method does not exist in Yiisoft\ActiveRecord\ActiveQueryInterface. Are you sure you never get one of those? ( Ignorable by Annotation )

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

83
        if ($query->/** @scrutinizer ignore-call */ shouldEmulateExecution()) {
Loading history...
84
            return [];
85
        }
86
87
        return $query->all($this->db);
0 ignored issues
show
Unused Code introduced by
The call to Yiisoft\Db\Query\QueryInterface::all() has too many arguments starting with $this->db. ( Ignorable by Annotation )

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

87
        return $query->/** @scrutinizer ignore-call */ all($this->db);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
88
    }
89
90
    /**
91
     * Prepares the sql-query that will get the data for current page.
92
     *
93
     * @throws InvalidConfigException
94
     *
95
     * @return QueryInterface
96
     */
97
    public function prepareQuery(): QueryInterface
98
    {
99
        if (!$this->query instanceof QueryInterface) {
100
            throw new InvalidConfigException('The "query" property must be an instance of a class that implements the QueryInterface e.g. Yiisoft\Db\Query or its subclasses.');
101
        }
102
        $query = clone $this->query;
103
        if (($pagination = $this->getPagination()) !== false) {
0 ignored issues
show
introduced by
The condition $pagination = $this->getPagination() !== false is always true.
Loading history...
104
            $pagination->totalCount = $this->getTotalCount();
0 ignored issues
show
Bug introduced by
Accessing totalCount on the interface Yiisoft\Data\Paginator\PaginatorInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
105
106
            if ($pagination->totalCount === 0) {
107
                $query->emulateExecution();
108
            }
109
110
            $query->limit($pagination->getLimit())->offset($pagination->getOffset());
0 ignored issues
show
Bug introduced by
The method getLimit() does not exist on Yiisoft\Data\Paginator\PaginatorInterface. ( Ignorable by Annotation )

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

110
            $query->limit($pagination->/** @scrutinizer ignore-call */ getLimit())->offset($pagination->getOffset());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getOffset() does not exist on Yiisoft\Data\Paginator\PaginatorInterface. It seems like you code against a sub-type of Yiisoft\Data\Paginator\PaginatorInterface such as Yiisoft\Data\Paginator\OffsetPaginator. ( Ignorable by Annotation )

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

110
            $query->limit($pagination->getLimit())->offset($pagination->/** @scrutinizer ignore-call */ getOffset());
Loading history...
111
        }
112
113
        if (($sort = $this->getSort()) !== null) {
114
            $query->addOrderBy($sort->getOrders());
115
        }
116
117
        return $query;
118
    }
119
120
    /**
121
     * Prepares the keys associated with the currently available data models.
122
     *
123
     * @param array $models the available data models.
124
     *
125
     * @return array the keys.
126
     */
127 20
    protected function prepareKeys(array $models = []): array
128
    {
129 20
        $keys = [];
130
131 20
        if ($this->key !== null) {
132
            foreach ($models as $model) {
133
                if (is_string($this->key)) {
134
                    $keys[] = $model[$this->key];
135
                } else {
136
                    $keys[] = call_user_func($this->key, $model);
137
                }
138
            }
139
140
            return $keys;
141
        }
142
143 20
        if ($this->query instanceof ActiveQueryInterface) {
144
            /* @var $class ActiveRecordInterface */
145 16
            $class = $this->query->getModelClass();
0 ignored issues
show
Bug introduced by
The method getModelClass() does not exist on null. ( Ignorable by Annotation )

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

145
            /** @scrutinizer ignore-call */ 
146
            $class = $this->query->getModelClass();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getModelClass() does not exist on Yiisoft\Db\Query\QueryInterface. It seems like you code against a sub-type of Yiisoft\Db\Query\QueryInterface such as Yiisoft\ActiveRecord\ActiveQuery or Yiisoft\ActiveRecord\ActiveQuery. ( Ignorable by Annotation )

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

145
            /** @scrutinizer ignore-call */ 
146
            $class = $this->query->getModelClass();
Loading history...
146
147 16
            $pks = $class::primaryKey();
148
149 16
            if (count($pks) === 1) {
150 16
                $pk = $pks[0];
151 16
                foreach ($models as $model) {
152 16
                    $keys[] = $model[$pk];
153
                }
154
            } else {
155
                foreach ($models as $model) {
156
                    $kk = [];
157
                    foreach ($pks as $pk) {
158
                        $kk[$pk] = $model[$pk];
159
                    }
160
                    $keys[] = $kk;
161
                }
162
            }
163
164 16
            return $keys;
165
        }
166
167 4
        return array_keys($models);
168
    }
169
170
    /**
171
     * Prepares the data models that will be made available in the current page.
172
     *
173
     * @throws InvalidConfigException
174
     *
175
     * @return array the available data models.
176
     */
177 20
    protected function prepareModels(): array
178
    {
179 20
        if (!$this->query instanceof QueryInterface) {
180
            throw new InvalidConfigException(
181
                'The "query" property must be an instance of a class that implements the QueryInterface e.g.'
182
                    . '\Yiisoft\Db\Query\Query or its subclasses.'
183
            );
184
        }
185
186 20
        $query = clone $this->query;
187
188 20
        if (($pagination = $this->getPagination()) !== null) {
189
            $pagination->totalCount = $this->getTotalCount();
0 ignored issues
show
Bug introduced by
Accessing totalCount on the interface Yiisoft\Data\Paginator\PaginatorInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
190
            if ($pagination->totalCount === 0) {
191
                return [];
192
            }
193
194
            $query->limit($pagination->getLimit())->offset($pagination->getOffset());
195
        }
196
197 20
        if (($sort = $this->getSort()) !== null) {
198
            $query->addOrderBy($sort->getOrders());
199
        }
200
201 20
        return $query->all();
202
    }
203
204
    /**
205
     * Returns a value indicating the total number of data models in this data provider.
206
     *
207
     * @throws InvalidConfigException
208
     *
209
     * @return int total number of data models in this data provider.
210
     */
211
    protected function prepareTotalCount(): int
212
    {
213
        if (!$this->query instanceof QueryInterface) {
214
            throw new InvalidConfigException(
215
                'The "query" property must be an instance of a class that implements the QueryInterface e.g. '
216
                . '\Yiisoft\Db\Query\Query or its subclasses.'
217
            );
218
        }
219
220
        $query = clone $this->query;
221
222
        return (int) $query->limit(-1)->offset(-1)->orderBy([])->count('*');
223
    }
224
}
225