• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lampager / lampager-cakephp / 8663957996

12 Apr 2024 02:51PM UTC coverage: 99.573% (-0.4%) from 100.0%
8663957996

push

github

web-flow
Update requirement to cakephp/cakephp@^4.5 (#47)

1 of 1 new or added line in 1 file covered. (100.0%)

1 existing line in 1 file now uncovered.

233 of 234 relevant lines covered (99.57%)

65.66 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.9
/src/ORM/Query.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Lampager\Cake\ORM;
6

7
use Cake\Database\Connection;
8
use Cake\Database\Expression\OrderByExpression;
9
use Cake\Database\Expression\OrderClauseExpression;
10
use Cake\Database\Expression\QueryExpression;
11
use Cake\Database\ExpressionInterface;
12
use Cake\Datasource\ResultSetInterface;
13
use Cake\ORM\Query as BaseQuery;
14
use Cake\ORM\Table;
15
use Lampager\Cake\PaginationResult;
16
use Lampager\Cake\Paginator;
17
use Lampager\Contracts\Cursor;
18
use Lampager\Exceptions\Query\BadKeywordException;
19
use Lampager\Exceptions\Query\InsufficientConstraintsException;
20
use Lampager\Exceptions\Query\LimitParameterException;
21

22
/**
23
 * @method $this forward(bool $forward = true)       Define that the current pagination is going forward.
24
 * @method $this backward(bool $backward = true)     Define that the current pagination is going backward.
25
 * @method $this exclusive(bool $exclusive = true)   Define that the cursor value is not included in the previous/next result.
26
 * @method $this inclusive(bool $inclusive = true)   Define that the cursor value is included in the previous/next result.
27
 * @method $this seekable(bool $seekable = true)     Define that the query can detect both "has_previous" and "has_next".
28
 * @method $this unseekable(bool $unseekable = true) Define that the query can detect only either "has_previous" or "has_next".
29
 * @method $this fromArray(mixed[] $options)         Define options from an associative array.
30
 */
31
class Query extends BaseQuery
32
{
33
    /** @var Paginator */
34
    protected $_paginator;
35

36
    /** @var Cursor|int[]|string[] */
37
    protected $_cursor = [];
38

39
    /**
40
     * Construct query.
41
     *
42
     * @param Connection $connection The connection object
43
     * @param Table      $table      The table this query is starting on
44
     */
45
    public function __construct(Connection $connection, Table $table)
46
    {
47
        parent::__construct($connection, $table);
157✔
48

49
        $this->_paginator = Paginator::create($this);
157✔
50
    }
51

52
    /**
53
     * Create query based on the existing query. This factory copies the internal
54
     * state of the given query to a new instance.
55
     */
56
    public static function fromQuery(BaseQuery $query)
57
    {
58
        $obj = new static($query->getConnection(), $query->getRepository());
64✔
59

60
        foreach (get_object_vars($query) as $k => $v) {
64✔
61
            $obj->$k = $v;
64✔
62
        }
63

64
        $obj->_executeOrder($obj->clause('order'));
64✔
65
        $obj->_executeLimit($obj->clause('limit'));
64✔
66

67
        return $obj;
64✔
68
    }
69

70
    /**
71
     * Set the cursor.
72
     *
73
     * @param Cursor|int[]|string[] $cursor
74
     */
75
    public function cursor($cursor = [])
76
    {
77
        $this->_cursor = $cursor;
88✔
78

79
        return $this;
88✔
80
    }
81

82
    /**
83
     * {@inheritdoc}
84
     */
85
    public function order($fields, $overwrite = false)
86
    {
87
        parent::order($fields, $overwrite);
38✔
88
        $this->_executeOrder($this->clause('order'));
38✔
89

90
        return $this;
37✔
91
    }
92

93
    /**
94
     * {@inheritdoc}
95
     */
96
    public function orderAsc($field, $overwrite = false)
97
    {
98
        parent::orderAsc($field, $overwrite);
30✔
99
        $this->_executeOrder($this->clause('order'));
30✔
100

101
        return $this;
30✔
102
    }
103

104
    /**
105
     * {@inheritdoc}
106
     */
107
    public function orderDesc($field, $overwrite = false)
108
    {
109
        parent::orderDesc($field, $overwrite);
24✔
110
        $this->_executeOrder($this->clause('order'));
24✔
111

112
        return $this;
24✔
113
    }
114

115
    /**
116
     * {@inheritdoc}
117
     */
118
    public function limit($num)
119
    {
120
        parent::limit($num);
89✔
121
        $this->_executeLimit($this->clause('limit'));
89✔
122

123
        return $this;
88✔
124
    }
125

126
    /**
127
     * {@inheritdoc}
128
     * @return PaginationResult
129
     */
130
    public function all(): ResultSetInterface
131
    {
132
        return $this->_paginator->paginate($this->_cursor);
120✔
133
    }
134

135
    /**
136
     * {@inheritdoc}
137
     */
138
    protected function _performCount(): int
139
    {
140
        return $this->all()->count();
16✔
141
    }
142

143
    protected function _executeOrder(?OrderByExpression $order): void
144
    {
145
        $this->_paginator->clearOrderBy();
156✔
146

147
        if ($order === null) {
156✔
148
            return;
2✔
149
        }
150

151
        $generator = $this->getValueBinder();
156✔
152
        $order->iterateParts(function ($condition, $key) use ($generator) {
156✔
153
            if (!is_int($key)) {
156✔
154
                /**
155
                 * @var string $key       The column
156
                 * @var string $condition The order
157
                 */
158
                $this->_paginator->orderBy($key, $condition);
52✔
159
            }
160

161
            if ($condition instanceof OrderClauseExpression) {
156✔
162
                $generator->resetCount();
103✔
163

164
                if (!preg_match('/ (?<direction>ASC|DESC)$/', $condition->sql($generator), $matches)) {
103✔
165
                    throw new BadKeywordException('OrderClauseExpression does not have direction');
1✔
166
                }
167

168
                /** @var string $direction */
169
                $direction = $matches['direction'];
102✔
170

171
                /** @var ExpressionInterface|string $field */
172
                $field = $condition->getField();
102✔
173

174
                if ($field instanceof ExpressionInterface) {
102✔
175
                    $generator->resetCount();
15✔
176
                    $this->_paginator->orderBy($field->sql($generator), $direction);
15✔
177
                } else {
178
                    $this->_paginator->orderBy($field, $direction);
87✔
179
                }
180
            }
181

182
            if ($condition instanceof QueryExpression) {
155✔
183
                $generator->resetCount();
1✔
184
                $this->_paginator->orderBy($condition->sql($generator));
1✔
185
            }
186

187
            return $condition;
155✔
188
        });
156✔
189
    }
190

191
    /**
192
     * @param null|int|QueryExpression $limit
193
     */
194
    protected function _executeLimit($limit): void
195
    {
196
        if (is_int($limit)) {
153✔
197
            $this->_paginator->limit($limit);
151✔
198
            return;
151✔
199
        }
200

201
        if ($limit instanceof QueryExpression) {
2✔
202
            $generator = $this->getValueBinder();
2✔
203
            $generator->resetCount();
2✔
204
            $sql = $limit->sql($generator);
2✔
205

206
            if (!ctype_digit($sql) || $sql <= 0) {
2✔
207
                throw new LimitParameterException('Limit must be positive integer');
1✔
208
            }
209

210
            $this->_paginator->limit((int)$sql);
1✔
211
            return;
1✔
212
        }
213

214
        // @codeCoverageIgnoreStart
215
        throw new \LogicException('Unreachable here');
216
        // @codeCoverageIgnoreEnd
217
    }
218

219
    /**
220
     * {@inheritdoc}
221
     */
222
    public function __call(string $method, array $arguments)
223
    {
224
        static $options = [
115✔
225
            'forward',
115✔
226
            'backward',
115✔
227
            'exclusive',
115✔
228
            'inclusive',
115✔
229
            'seekable',
115✔
230
            'unseekable',
115✔
231
            'fromArray',
115✔
232
        ];
115✔
233

234
        if (in_array($method, $options, true)) {
115✔
235
            $this->_paginator->$method(...$arguments);
115✔
236
            return $this;
115✔
237
        }
238

UNCOV
239
        return parent::__call($method, $arguments);
×
240
    }
241

242
    /**
243
     * {@inheritdoc}
244
     */
245
    public function __debugInfo(): array
246
    {
247
        try {
248
            $info = $this->_paginator->build($this->_cursor)->__debugInfo();
2✔
249
        } catch (InsufficientConstraintsException $e) {
1✔
250
            $info = [
1✔
251
                'sql' => 'SQL could not be generated for this query as it is incomplete: ' . $e->getMessage(),
1✔
252
                'params' => [],
1✔
253
                'defaultTypes' => $this->getDefaultTypes(),
1✔
254
                'decorators' => count($this->_resultDecorators),
1✔
255
                'executed' => (bool)$this->_iterator,
1✔
256
                'hydrate' => $this->_hydrate,
1✔
257
                'buffered' => $this->_useBufferedResults,
1✔
258
                'formatters' => count($this->_formatters),
1✔
259
                'mapReducers' => count($this->_mapReduce),
1✔
260
                'contain' => $this->_eagerLoader ? $this->_eagerLoader->getContain() : [],
1✔
261
                'matching' => $this->_eagerLoader ? $this->_eagerLoader->getMatching() : [],
1✔
262
                'extraOptions' => $this->_options,
1✔
263
                'repository' => $this->_repository,
1✔
264
            ];
1✔
265
        }
266

267
        return [
2✔
268
            '(help)' => 'This is a Lampager Query object to get the paginated results.',
2✔
269
            'paginator' => $this->_paginator,
2✔
270
        ] + $info;
2✔
271
    }
272
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc