1
|
|
|
<?php |
|
|
|
|
2
|
|
|
namespace GV; |
3
|
|
|
|
4
|
|
|
/** If this file is called directly, abort. */ |
5
|
|
|
if ( ! defined( 'GRAVITYVIEW_DIR' ) ) { |
6
|
|
|
die(); |
7
|
|
|
} |
8
|
|
|
|
9
|
|
|
/** |
10
|
|
|
* A collection of \GV\Entry objects. |
11
|
|
|
*/ |
12
|
|
|
class Entry_Collection extends Collection { |
13
|
|
|
/** |
14
|
|
|
* Lazy fetching and counting of data defers |
15
|
|
|
* all processing of entries and entry data until |
16
|
|
|
* it is really requested. |
17
|
|
|
* |
18
|
|
|
* @see \GV\Entry_Collection::add_fetch_callback |
19
|
|
|
* @see \GV\Entry_Collection::add_count_callback |
20
|
|
|
* |
21
|
|
|
* @var array Lazy data loading callbacks. |
22
|
|
|
*/ |
23
|
|
|
private $callbacks = array(); |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* @var \GV\Entry_Filter[] Filtering criteria. |
27
|
|
|
*/ |
28
|
|
|
private $filters = array(); |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* @var \GV\Entry_Sort[] Sorting criteria. |
32
|
|
|
*/ |
33
|
|
|
private $sorts = array(); |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* @var int The offset. |
37
|
|
|
*/ |
38
|
|
|
public $offset = 0; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* @var int The limit. |
42
|
|
|
*/ |
43
|
|
|
public $limit = 20; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* @var int The current page. |
47
|
|
|
*/ |
48
|
|
|
public $current_page = 1; |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* @var int The number of entries fetched. |
52
|
|
|
*/ |
53
|
|
|
private $fetched = -1; |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* Add an \GV\Entry to this collection. |
57
|
|
|
* |
58
|
|
|
* @param \GV\Entry $entry The entry to add to the internal array. |
59
|
|
|
* |
60
|
|
|
* @api |
61
|
|
|
* @since future |
62
|
|
|
* @return void |
63
|
|
|
*/ |
64
|
|
|
public function add( $entry ) { |
65
|
|
|
if ( ! $entry instanceof Entry ) { |
66
|
|
|
gravityview()->log->error( 'Entry_Collections can only contain objects of type \GV\Entry.' ); |
67
|
|
|
return; |
68
|
|
|
} |
69
|
|
|
parent::add( $entry ); |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* Get a \GV\Entry from this list. |
74
|
|
|
* |
75
|
|
|
* @param int $entry_id The ID of the entry to get. |
76
|
|
|
* @param string $backend The form backend identifier, allows for multiple form backends in the future. Unused until then. |
77
|
|
|
* |
78
|
|
|
* @api |
79
|
|
|
* @since future |
80
|
|
|
* |
81
|
|
|
* @return \GV\Entry|null The \GV\entry with the $entry_id as the ID, or null if not found. |
82
|
|
|
*/ |
83
|
|
|
public function get( $entry_id, $backend = 'gravityforms' ) { |
|
|
|
|
84
|
|
|
foreach ( $this->all() as $entry ) { |
85
|
|
|
if ( $entry->ID == $entry_id ) { |
86
|
|
|
return $entry; |
87
|
|
|
} |
88
|
|
|
} |
89
|
|
|
return null; |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
/** |
93
|
|
|
* Count the total number of \GV\Entry objects that are possible to get. |
94
|
|
|
* |
95
|
|
|
* @api |
96
|
|
|
* @since future |
97
|
|
|
* |
98
|
|
|
* @return int The total number of entries that are fetchable. |
99
|
|
|
*/ |
100
|
1 |
|
public function total() { |
101
|
1 |
|
$total = 0; |
102
|
|
|
|
103
|
|
|
/** Call all lazy callbacks. */ |
104
|
1 |
|
foreach ( $this->callbacks as $callback ) { |
105
|
1 |
|
if ( $callback[0] != 'count' ) { |
|
|
|
|
106
|
1 |
|
continue; |
107
|
|
|
} |
108
|
|
|
|
109
|
1 |
|
$total += $callback[1]( $this->filters ); |
110
|
|
|
} |
111
|
|
|
|
112
|
1 |
|
return $total - $this->offset; |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
/** |
116
|
|
|
* Get the entries as an array. |
117
|
|
|
* |
118
|
|
|
* @api |
119
|
|
|
* @since future |
120
|
|
|
* |
121
|
|
|
* @return \GV\Entry[] The entries as an array. |
122
|
|
|
*/ |
123
|
|
|
public function all() { |
124
|
|
|
if ( $this->fetched >= 0 || parent::count() ) { |
|
|
|
|
125
|
|
|
return parent::all(); |
126
|
|
|
} |
127
|
|
|
return $this->fetch()->all(); |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* Get the last \GV\Entry in this collection. |
132
|
|
|
* |
133
|
|
|
* @api |
134
|
|
|
* @since future |
135
|
|
|
* |
136
|
|
|
* @return \GV\Entry|null The last entry or null. |
137
|
|
|
*/ |
138
|
|
|
public function last() { |
139
|
|
|
if ( $this->fetched >= 0 || parent::count() ) { |
|
|
|
|
140
|
|
|
return parent::last(); |
141
|
|
|
} |
142
|
|
|
return $this->fetch()->last(); |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
/** |
146
|
|
|
* Hydrate this collection now. |
147
|
|
|
* |
148
|
|
|
* @api |
149
|
|
|
* @since future |
150
|
|
|
* |
151
|
|
|
* @return \GV\Entry_Collection This collection, now hydrated. |
152
|
|
|
*/ |
153
|
|
|
public function fetch() { |
154
|
|
|
$this->clear(); |
155
|
|
|
|
156
|
|
|
/** Calculate the offsets. */ |
157
|
|
|
$offset = new \GV\Entry_Offset(); |
158
|
|
|
$offset->limit = $this->limit; |
159
|
|
|
$offset->offset = ( $this->limit * ( $this->current_page - 1 ) ) + $this->offset; |
160
|
|
|
|
161
|
|
|
/** Call all lazy callbacks. */ |
162
|
|
|
foreach ( $this->callbacks as $i => $callback ) { |
163
|
|
|
if ( $callback[0] != 'fetch' ) { |
|
|
|
|
164
|
|
|
continue; |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
$this->merge( $callback[1]( $this->filters, $this->sorts, $offset ) ); |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
$this->fetched = $this->count(); |
171
|
|
|
|
172
|
|
|
return $this; |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
/** |
176
|
|
|
* Apply a filter to the current collection. |
177
|
|
|
* |
178
|
|
|
* This operation is non-destructive as a copy of the collection is returned. |
179
|
|
|
* |
180
|
|
|
* @param \GV\Entry_Filter $filter The filter to be applied. |
181
|
|
|
* |
182
|
|
|
* @api |
183
|
|
|
* @since future |
184
|
|
|
* |
185
|
|
|
* @return \GV\Entry_Collection A copy of the this collection with the filter applied. |
186
|
|
|
*/ |
187
|
1 |
|
public function filter( \GV\Entry_Filter $filter ) { |
188
|
1 |
|
$collection = clone( $this ); |
189
|
1 |
|
$collection->clear(); |
190
|
|
|
|
191
|
1 |
|
array_push( $collection->filters, $filter ); |
192
|
|
|
|
193
|
1 |
|
return $collection; |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
/** |
197
|
|
|
* Sort. |
198
|
|
|
* |
199
|
|
|
* @param \GV\Entry_Sort $sort The sort to apply to this collection. |
200
|
|
|
* |
201
|
|
|
* @api |
202
|
|
|
* @since future |
203
|
|
|
* |
204
|
|
|
* @return \GV\Entry_Collection A copy of the this collection with the sort applied. |
205
|
|
|
*/ |
206
|
1 |
|
public function sort( $sort ) { |
207
|
1 |
|
$collection = clone( $this ); |
208
|
1 |
|
$collection->clear(); |
209
|
|
|
|
210
|
1 |
|
array_push( $collection->sorts, $sort ); |
211
|
|
|
|
212
|
1 |
|
return $collection; |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
/** |
216
|
|
|
* Limit the fetch to a specified window. |
217
|
|
|
* |
218
|
|
|
* @param int $limit The limit. |
219
|
|
|
* |
220
|
|
|
* @api |
221
|
|
|
* @since future |
222
|
|
|
* |
223
|
|
|
* @return \GV\Entry_Collection A copy of the this collection with the limit applied. |
224
|
|
|
*/ |
225
|
1 |
|
public function limit( $limit ) { |
226
|
1 |
|
$collection = clone( $this ); |
227
|
1 |
|
$collection->clear(); |
228
|
1 |
|
$collection->limit = $limit; |
229
|
1 |
|
return $collection; |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
/** |
233
|
|
|
* Add an $offset to these entries. |
234
|
|
|
* |
235
|
|
|
* Useful, you know, for pagination and stuff. Not too useful directly. |
236
|
|
|
* |
237
|
|
|
* @see \GV\Entry_Collection::page() |
238
|
|
|
* |
239
|
|
|
* @param int $offset The number of entries to skip in the database. |
240
|
|
|
* |
241
|
|
|
* @api |
242
|
|
|
* @since future |
243
|
|
|
* |
244
|
|
|
* @return \GV\Entry_Collection A copy of the this collection with the offset applied. |
245
|
|
|
*/ |
246
|
1 |
|
public function offset( $offset ) { |
247
|
1 |
|
$collection = clone( $this ); |
248
|
1 |
|
$collection->clear(); |
249
|
1 |
|
$collection->offset = $offset; |
250
|
1 |
|
return $collection; |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
/** |
254
|
|
|
* Set the current page. |
255
|
|
|
* |
256
|
|
|
* @param int $page Set the current page to this page. Ends up agumenting the $offset in \GV\Entry_Offset |
257
|
|
|
* |
258
|
|
|
* @return \GV\Entry_Collection A copy of the this collection with the offset applied. |
259
|
|
|
*/ |
260
|
1 |
|
public function page( $page ) { |
261
|
1 |
|
$collection = clone( $this ); |
262
|
1 |
|
$collection->clear(); |
263
|
1 |
|
$collection->current_page = $page; |
264
|
1 |
|
return $collection; |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
/** |
268
|
|
|
* Defer fetching of data to the provided callable. |
269
|
|
|
* |
270
|
|
|
* The callback signature should be as follows: |
271
|
|
|
* \GV\Entry_Collection callback( \GV\Entry_Filter $filter, \GV\Entry_Sort $sort, \GV\Entry_Offset $offset ); |
272
|
|
|
* |
273
|
|
|
* The methods that trigger the callback are: |
274
|
|
|
* - \GV\Entry_Collection::fetch |
275
|
|
|
* |
276
|
|
|
* ::fetch is triggered via: |
277
|
|
|
* - \GV\Entry_Collection::all |
278
|
|
|
* - \GV\Entry_Collection::last |
279
|
|
|
* |
280
|
|
|
* @param callable $callback The callback to call when needed. |
281
|
|
|
* |
282
|
|
|
* @internal |
283
|
|
|
* @since future |
284
|
|
|
* |
285
|
|
|
* @return void |
286
|
|
|
*/ |
287
|
|
|
public function add_fetch_callback( $callback ) { |
288
|
|
|
$this->add_callback( 'fetch', $callback ); |
289
|
|
|
} |
290
|
|
|
|
291
|
|
|
/** |
292
|
|
|
* Defer counting of data to the provided callable. |
293
|
|
|
* |
294
|
|
|
* The callback signature should be as follows: |
295
|
|
|
* int callback( \GV\Entry_Filter $filter ); |
296
|
|
|
* |
297
|
|
|
* The methods that trigger the callback are: |
298
|
|
|
* - \GV\Entry_Collection::count |
299
|
|
|
* |
300
|
|
|
* @param callable $callback The callback to call when needed. |
301
|
|
|
* |
302
|
|
|
* @internal |
303
|
|
|
* @since future |
304
|
|
|
* |
305
|
|
|
* @return void |
306
|
|
|
*/ |
307
|
|
|
public function add_count_callback( $callback ) { |
308
|
|
|
$this->add_callback( 'count', $callback ); |
309
|
|
|
} |
310
|
|
|
|
311
|
|
|
/** |
312
|
|
|
* Add a callback for lazy loading/counting. |
313
|
|
|
* |
314
|
|
|
* @param callable $callback The callback to call when needed. |
315
|
|
|
* |
316
|
|
|
* @return void |
317
|
|
|
*/ |
318
|
|
|
private function add_callback( $type, $callback ) { |
319
|
|
|
if ( ! is_callable( $callback ) ) { |
320
|
|
|
return; |
321
|
|
|
} |
322
|
|
|
|
323
|
|
|
$this->callbacks []= array( $type, $callback ); |
|
|
|
|
324
|
|
|
} |
325
|
|
|
|
326
|
|
|
/** |
327
|
|
|
* @inheritdoc |
328
|
|
|
*/ |
329
|
|
|
public function clear() { |
330
|
|
|
$this->fetched = -1; |
331
|
|
|
parent::clear(); |
332
|
|
|
} |
333
|
|
|
} |
334
|
|
|
|
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.