Completed
Push — master ( 6e0700...30b471 )
by Todd
02:33
created

TableQuery   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 213
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 91.67%

Importance

Changes 0
Metric Value
wmc 30
lcom 1
cbo 3
dl 0
loc 213
ccs 66
cts 72
cp 0.9167
rs 10
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A getOrder() 0 3 1
A setOrder() 0 4 1
A getOffset() 0 3 1
A setOffset() 0 8 3
A getLimit() 0 3 1
A getOption() 0 3 2
A __construct() 0 20 4
B setLimit() 0 17 5
A setOption() 0 10 4
A getData() 0 21 3
B fetchAll() 0 14 5
1
<?php
2
/**
3
 * @author Todd Burry <[email protected]>
4
 * @copyright 2009-2017 Vanilla Forums Inc.
5
 * @license MIT
6
 */
7
8
namespace Garden\Db;
9
10
use PDO;
11
use Traversable;
12
13
class TableQuery implements \IteratorAggregate, DatasetInterface {
14
    use Utils\FetchModeTrait, DatasetTrait {
15
        fetchAll as protected fetchAllTrait;
16
        setFetchMode as protected;
17
    }
18
19
    /**
20
     * @var Db
21
     */
22
    private $db;
23
24
    /**
25
     * @var array|null
26
     */
27
    private $data;
28
29
    /**
30
     * @var string
31
     */
32
    private $table;
33
34
    /**
35
     * @var
36
     */
37
    private $where;
38
39
    /**
40
     * @var array
41
     */
42
    private $options;
43
44
    /**
45
     * @var callable A callback used to unserialize rows from the database.
46
     */
47
    private $rowCallback;
48
49
    /**
50
     * Construct a new {@link TableQuery} object.
51
     *
52
     * Note that this class is not meant to be modified after being constructed so it's important to pass everything you
53
     * need early.
54
     *
55
     * @param string $table The name of the table to query.
56
     * @param array $where The filter information for the query.
57
     * @param Db $db The database to fetch the data from. This can be changed later.
58
     * @param array $options Additional options for the object:
59
     *
60
     * fetchMode
61
     * : The PDO fetch mode. This can be one of the **PDO::FETCH_** constants, a class name or an array of arguments for
62
     * {@link PDOStatement::fetchAll()}
63
     * rowCallback
64
     * : A callable that will be applied to each row of the result set.
65
     */
66 17
    public function __construct($table, array $where, Db $db, array $options = []) {
67 17
        $this->table = $table;
68 17
        $this->where = $where;
69 17
        $this->db = $db;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
70
71
        $options += [
72 17
            Db::OPTION_FETCH_MODE => $db->getFetchArgs(),
73
            'rowCallback' => null
74 17
        ];
75
76 17
        $fetchMode = (array)$options[Db::OPTION_FETCH_MODE];
77 17
        if (in_array($fetchMode[0], [PDO::FETCH_OBJ | PDO::FETCH_NUM | PDO::FETCH_FUNC])) {
78
            throw new \InvalidArgumentException("Fetch mode not supported.", 500);
79 17
        } elseif ($fetchMode[0] == PDO::FETCH_CLASS && !is_a($fetchMode[1], \ArrayAccess::class, true)) {
80
            throw new \InvalidArgumentException("The {$fetchMode[1]} class must implement ArrayAccess.", 500);
81
        }
82
83 17
        $this->setFetchMode(...$fetchMode);
84 17
        $this->rowCallback = $options['rowCallback'];
85 17
    }
86
87
    /**
88
     * Get the order.
89
     *
90
     * @return array Returns the order.
91
     */
92 1
    public function getOrder() {
93 1
        return $this->getOption('order', []);
94
    }
95
96
    /**
97
     * Set the order.
98
     *
99
     * @param string[] $columns The column names to order by.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $columns not be string[][]?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
100
     * @return $this
101
     */
102 16
    public function setOrder(...$columns) {
103 16
        $this->setOption('order', $columns, true);
104 16
        return $this;
105
    }
106
107
    /**
108
     * Get the offset.
109
     *
110
     * @return int Returns the offset.
111
     */
112 1
    public function getOffset() {
113 1
        return $this->getOption('offset', 0);
114
    }
115
116
    /**
117
     * Set the offset.
118
     *
119
     * @param int $offset
120
     * @return $this
121
     */
122 2
    public function setOffset($offset) {
123 2
        if (!is_numeric($offset) || $offset < 0) {
124
            throw new \InvalidArgumentException("Invalid offset '$offset.'", 500);
125
        }
126
127 2
        $this->setOption('offset', (int)$offset, true);
128 2
        return $this;
129
    }
130
131
    /**
132
     * Get the limit.
133
     *
134
     * @return int Returns the limit.
135
     */
136 3
    public function getLimit() {
137 3
        return $this->getOption('limit', Model::DEFAULT_LIMIT);
138
    }
139
140
    /**
141
     * {@inheritdoc}
142
     */
143 17
    public function setLimit($limit) {
144 17
        if (!is_numeric($limit) || $limit < 0) {
145
            throw new \InvalidArgumentException("Invalid limit '$limit.'", 500);
146
        }
147
148
149 17
        $reset = true;
150
151 17
        if (is_array($this->data) && $limit < $this->getLimit()) {
152 2
            $this->data = array_slice($this->data, 0, $limit);
153 2
            $reset = false;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 6 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
154 2
        }
155
156 17
        $this->setOption('limit', (int)$limit, $reset);
157
158 17
        return $this;
159
    }
160
161 3
    protected function getOption($name, $default = null) {
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
162 3
        return isset($this->options[$name]) ? $this->options[$name] : $default;
163
    }
164
165
    /**
166
     * Set a query option.
167
     *
168
     * @param string $name The name of the option to set.
169
     * @param mixed $value The new value of the option.
170
     * @param bool $reset Pass **true** and the data will be queried again if the option value has changed.
171
     * @return $this
172
     */
173 17
    protected function setOption($name, $value, $reset = false) {
174 17
        $changed = !isset($this->options[$name]) || $this->options[$name] !== $value;
175
176 17
        if ($changed && $reset) {
177 17
            $this->data = null;
178 17
        }
179
180 17
        $this->options[$name] = $value;
181 17
        return $this;
182
    }
183
184
    /**
185
     * Get the data.
186
     *
187
     * @return array Returns the data.
188
     */
189 16
    protected function getData() {
190 16
        if ($this->data === null) {
191 16
            $stmt = $this->db->get(
192 16
                $this->table,
193 16
                $this->where,
194 16
                $this->options + [Db::OPTION_FETCH_MODE => $this->getFetchArgs()]
195 16
            );
196
197 16
            $data = $stmt->fetchAll(...(array)$this->getFetchArgs());
198
199 16
            if (is_callable($this->rowCallback)) {
200 15
                array_walk($data, function (&$row) {
201 15
                    $row = call_user_func($this->rowCallback, $row);
202 15
                });
203 15
            }
204
205 16
            $this->data = $data;
206 16
        }
207
208 16
        return $this->data;
209
    }
210
211 12
    public function fetchAll($mode = 0, ...$args) {
212 12
        $thisMode = $this->getFetchMode();
213 12
        if ($mode === 0 || $mode === $thisMode || $this->data === []) {
214 3
            return $this->getData();
215
        }
216
217 9
        $result = $this->fetchAllTrait($mode, ...$args);
218
219 9
        if ($result === null) {
220
            // Don't know what to do, fetch from the database again.
221
            $result = $this->db->get($this->table, $this->where, $this->options)->fetchAll($mode, ...$args);
222
        }
223 9
        return $result;
224
    }
225
}
226