|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/** |
|
4
|
|
|
* @package toolkit |
|
5
|
|
|
*/ |
|
6
|
|
|
/** |
|
7
|
|
|
* The `EntryManager` is responsible for all `Entry` objects in Symphony. |
|
8
|
|
|
* Entries are stored in the database in a cluster of tables. There is a |
|
9
|
|
|
* parent entry row stored in `tbl_entries` and then each field's data is |
|
10
|
|
|
* stored in a separate table, `tbl_entries_data_{field_id}`. Where Field ID |
|
11
|
|
|
* is generated when the Section is saved. This Manager provides basic |
|
12
|
|
|
* add, edit, delete and fetching methods for Entries. |
|
13
|
|
|
*/ |
|
14
|
|
|
|
|
15
|
|
|
class EntryManager |
|
16
|
|
|
{ |
|
17
|
|
|
/** |
|
18
|
|
|
* The Field ID that will be used to sort when fetching Entries, defaults |
|
19
|
|
|
* to null, which implies the Entry ID (id column in `tbl_entries`) |
|
20
|
|
|
* @var integer |
|
21
|
|
|
*/ |
|
22
|
|
|
protected static $_fetchSortField = null; |
|
23
|
|
|
|
|
24
|
|
|
/** |
|
25
|
|
|
* The direction that entries should be sorted in, available options are |
|
26
|
|
|
* RAND, ASC or DESC. Defaults to null, which implies ASC |
|
27
|
|
|
* @var string |
|
28
|
|
|
*/ |
|
29
|
|
|
protected static $_fetchSortDirection = null; |
|
30
|
|
|
|
|
31
|
|
|
/** |
|
32
|
|
|
* Setter function for the default sorting direction of the Fetch |
|
33
|
|
|
* function. Available options are RAND, ASC or DESC. |
|
34
|
|
|
* |
|
35
|
|
|
* @param string $direction |
|
36
|
|
|
* The direction that entries should be sorted in, available options |
|
37
|
|
|
* are RAND, ASC or DESC. |
|
38
|
|
|
*/ |
|
39
|
|
|
public static function setFetchSortingDirection($direction) |
|
40
|
|
|
{ |
|
41
|
|
|
$direction = strtoupper($direction); |
|
42
|
|
|
|
|
43
|
|
|
if ($direction === 'RANDOM') { |
|
44
|
|
|
$direction = 'RAND'; |
|
45
|
|
|
} |
|
46
|
|
|
|
|
47
|
|
|
self::$_fetchSortDirection = (in_array($direction, array('RAND', 'ASC', 'DESC')) ? $direction : null); |
|
48
|
|
|
} |
|
49
|
|
|
|
|
50
|
|
|
/** |
|
51
|
|
|
* Sets the field to applying the sorting direction on when fetching |
|
52
|
|
|
* entries |
|
53
|
|
|
* |
|
54
|
|
|
* @param integer $field_id |
|
55
|
|
|
* The ID of the Field that should be sorted on |
|
56
|
|
|
*/ |
|
57
|
|
|
public static function setFetchSortingField($field_id) |
|
58
|
|
|
{ |
|
59
|
|
|
self::$_fetchSortField = $field_id; |
|
60
|
|
|
} |
|
61
|
|
|
|
|
62
|
|
|
/** |
|
63
|
|
|
* Convenience function that will set sorting field and direction |
|
64
|
|
|
* by calling `setFetchSortingField()` & `setFetchSortingDirection()` |
|
65
|
|
|
* |
|
66
|
|
|
* @see toolkit.EntryManager#setFetchSortingField() |
|
67
|
|
|
* @see toolkit.EntryManager#setFetchSortingDirection() |
|
68
|
|
|
* @param integer $field_id |
|
69
|
|
|
* The ID of the Field that should be sorted on |
|
70
|
|
|
* @param string $direction |
|
71
|
|
|
* The direction that entries should be sorted in, available options |
|
72
|
|
|
* are RAND, ASC or DESC. Defaults to ASC |
|
73
|
|
|
*/ |
|
74
|
|
|
public static function setFetchSorting($field_id, $direction = 'ASC') |
|
75
|
|
|
{ |
|
76
|
|
|
self::setFetchSortingField($field_id); |
|
77
|
|
|
self::setFetchSortingDirection($direction); |
|
78
|
|
|
} |
|
79
|
|
|
|
|
80
|
|
|
/** |
|
81
|
|
|
* Returns an object representation of the sorting for the |
|
82
|
|
|
* EntryManager, with the field and direction provided |
|
83
|
|
|
* |
|
84
|
|
|
* @return StdClass |
|
85
|
|
|
*/ |
|
86
|
|
|
public static function getFetchSorting() |
|
87
|
|
|
{ |
|
88
|
|
|
return (object)array( |
|
89
|
|
|
'field' => self::$_fetchSortField, |
|
90
|
|
|
'direction' => self::$_fetchSortDirection |
|
91
|
|
|
); |
|
92
|
|
|
} |
|
93
|
|
|
|
|
94
|
|
|
/** |
|
95
|
|
|
* Given an Entry object, iterate over all of the fields in that object |
|
96
|
|
|
* an insert them into their relevant entry tables. |
|
97
|
|
|
* |
|
98
|
|
|
* @param Entry $entry |
|
99
|
|
|
* An Entry object to insert into the database |
|
100
|
|
|
* @throws DatabaseException |
|
101
|
|
|
* @return boolean |
|
102
|
|
|
*/ |
|
103
|
|
|
public static function add(Entry $entry) |
|
104
|
|
|
{ |
|
105
|
|
|
$fields = $entry->get(); |
|
106
|
|
|
Symphony::Database()->insert($fields, 'tbl_entries'); |
|
107
|
|
|
|
|
108
|
|
|
if (!$entry_id = Symphony::Database()->getInsertID()) { |
|
109
|
|
|
return false; |
|
110
|
|
|
} |
|
111
|
|
|
|
|
112
|
|
|
foreach ($entry->getData() as $field_id => $field) { |
|
113
|
|
|
if (!is_array($field) || empty($field)) { |
|
114
|
|
|
continue; |
|
115
|
|
|
} |
|
116
|
|
|
|
|
117
|
|
|
Symphony::Database()->delete('tbl_entries_data_' . $field_id, " `entry_id` = ?", array($entry_id)); |
|
118
|
|
|
|
|
119
|
|
|
$data = array( |
|
120
|
|
|
'entry_id' => $entry_id |
|
121
|
|
|
); |
|
122
|
|
|
|
|
123
|
|
|
$fields = array(); |
|
124
|
|
|
|
|
125
|
|
View Code Duplication |
foreach ($field as $key => $value) { |
|
126
|
|
|
if (is_array($value)) { |
|
127
|
|
|
foreach ($value as $ii => $v) { |
|
128
|
|
|
$fields[$ii][$key] = $v; |
|
129
|
|
|
} |
|
130
|
|
|
} else { |
|
131
|
|
|
$fields[max(0, count($fields) - 1)][$key] = $value; |
|
132
|
|
|
} |
|
133
|
|
|
} |
|
134
|
|
|
|
|
135
|
|
|
$fieldCount = count($fields); |
|
136
|
|
|
for ($ii = 0; $ii < $fieldCount; $ii++) { |
|
137
|
|
|
$fields[$ii] = array_merge($data, $fields[$ii]); |
|
138
|
|
|
} |
|
139
|
|
|
|
|
140
|
|
|
Symphony::Database()->insert($fields, 'tbl_entries_data_' . $field_id); |
|
141
|
|
|
} |
|
142
|
|
|
|
|
143
|
|
|
$entry->set('id', $entry_id); |
|
144
|
|
|
|
|
145
|
|
|
return true; |
|
146
|
|
|
} |
|
147
|
|
|
|
|
148
|
|
|
/** |
|
149
|
|
|
* Update an existing Entry object given an Entry object |
|
150
|
|
|
* |
|
151
|
|
|
* @param Entry $entry |
|
152
|
|
|
* An Entry object |
|
153
|
|
|
* @throws DatabaseException |
|
154
|
|
|
* @return boolean |
|
155
|
|
|
*/ |
|
156
|
|
|
public static function edit(Entry $entry) |
|
157
|
|
|
{ |
|
158
|
|
|
// Update modification date. |
|
159
|
|
|
Symphony::Database()->update( |
|
160
|
|
|
array( |
|
161
|
|
|
'modification_date' => $entry->get('modification_date'), |
|
162
|
|
|
'modification_date_gmt' => $entry->get('modification_date_gmt') |
|
163
|
|
|
), |
|
164
|
|
|
'tbl_entries', |
|
165
|
|
|
' `id` = ?', |
|
166
|
|
|
array( |
|
167
|
|
|
$entry->get('id') |
|
168
|
|
|
) |
|
169
|
|
|
); |
|
170
|
|
|
|
|
171
|
|
|
// Iterate over all data for this entry, deleting existing data first |
|
172
|
|
|
// then inserting a new row for the data |
|
173
|
|
|
foreach ($entry->getData() as $field_id => $field) { |
|
174
|
|
|
if (empty($field_id)) { |
|
175
|
|
|
continue; |
|
176
|
|
|
} |
|
177
|
|
|
|
|
178
|
|
|
$did_lock = false; |
|
179
|
|
|
try { |
|
180
|
|
|
$did_lock = Symphony::Database()->query('LOCK TABLES tbl_entries_data_' . $field_id . ' WRITE'); |
|
181
|
|
|
Symphony::Database()->delete('tbl_entries_data_' . $field_id, '`entry_id` = ?', array($entry->get('id'))); |
|
182
|
|
|
|
|
183
|
|
|
if (!is_array($field) || empty($field)) { |
|
184
|
|
|
if ($did_lock) { |
|
185
|
|
|
Symphony::Database()->query('UNLOCK TABLES'); |
|
186
|
|
|
} |
|
187
|
|
|
continue; |
|
188
|
|
|
} |
|
189
|
|
|
|
|
190
|
|
|
$data = array( |
|
191
|
|
|
'entry_id' => $entry->get('id') |
|
192
|
|
|
); |
|
193
|
|
|
|
|
194
|
|
|
$fields = array(); |
|
195
|
|
|
|
|
196
|
|
View Code Duplication |
foreach ($field as $key => $value) { |
|
197
|
|
|
if (is_array($value)) { |
|
198
|
|
|
foreach ($value as $ii => $v) { |
|
199
|
|
|
$fields[$ii][$key] = $v; |
|
200
|
|
|
} |
|
201
|
|
|
} else { |
|
202
|
|
|
$fields[max(0, count($fields) - 1)][$key] = $value; |
|
203
|
|
|
} |
|
204
|
|
|
} |
|
205
|
|
|
|
|
206
|
|
|
foreach ($fields as $index => $field_data) { |
|
207
|
|
|
$fields[$index] = array_merge($data, $field_data); |
|
208
|
|
|
} |
|
209
|
|
|
|
|
210
|
|
|
Symphony::Database()->insert($fields, 'tbl_entries_data_' . $field_id); |
|
211
|
|
|
} catch (Exception $e) { |
|
212
|
|
|
Symphony::Log()->pushExceptionToLog($e, true); |
|
|
|
|
|
|
213
|
|
|
} |
|
214
|
|
|
|
|
215
|
|
|
if ($did_lock) { |
|
216
|
|
|
Symphony::Database()->query('UNLOCK TABLES'); |
|
217
|
|
|
} |
|
218
|
|
|
} |
|
219
|
|
|
|
|
220
|
|
|
return true; |
|
221
|
|
|
} |
|
222
|
|
|
|
|
223
|
|
|
/** |
|
224
|
|
|
* Given an Entry ID, or an array of Entry ID's, delete all |
|
225
|
|
|
* data associated with this Entry using a Field's `entryDataCleanup()` |
|
226
|
|
|
* function, and then remove this Entry from `tbl_entries`. If the `$entries` |
|
227
|
|
|
* all belong to the same section, passing `$section_id` will improve |
|
228
|
|
|
* performance |
|
229
|
|
|
* |
|
230
|
|
|
* @param array|integer $entries |
|
231
|
|
|
* An entry_id, or an array of entry id's to delete |
|
232
|
|
|
* @param integer $section_id (optional) |
|
233
|
|
|
* If possible, the `$section_id` of the the `$entries`. This parameter |
|
234
|
|
|
* should be left as null if the `$entries` array contains entry_id's for |
|
235
|
|
|
* multiple sections. |
|
236
|
|
|
* @throws DatabaseException |
|
237
|
|
|
* @throws Exception |
|
238
|
|
|
* @return boolean |
|
239
|
|
|
*/ |
|
240
|
|
|
public static function delete($entries, $section_id = null) |
|
241
|
|
|
{ |
|
242
|
|
|
$needs_data = true; |
|
243
|
|
|
|
|
244
|
|
|
if (!is_array($entries)) { |
|
245
|
|
|
$entries = array($entries); |
|
246
|
|
|
} |
|
247
|
|
|
|
|
248
|
|
|
// Get the section's schema |
|
249
|
|
|
if (!is_null($section_id)) { |
|
250
|
|
|
$section = SectionManager::fetch($section_id); |
|
251
|
|
|
|
|
252
|
|
|
if ($section instanceof Section) { |
|
253
|
|
|
$fields = $section->fetchFields(); |
|
254
|
|
|
$data = array(); |
|
255
|
|
|
|
|
256
|
|
|
foreach ($fields as $field) { |
|
257
|
|
|
$reflection = new ReflectionClass($field); |
|
258
|
|
|
// This field overrides the default implementation, so pass it data. |
|
259
|
|
|
$data[$field->get('element_name')] = $reflection->getMethod('entryDataCleanup')->class === 'Field' ? false : true; |
|
260
|
|
|
} |
|
261
|
|
|
|
|
262
|
|
|
$data = array_filter($data); |
|
263
|
|
|
|
|
264
|
|
|
if (empty($data)) { |
|
265
|
|
|
$needs_data = false; |
|
266
|
|
|
} |
|
267
|
|
|
} |
|
268
|
|
|
} |
|
269
|
|
|
|
|
270
|
|
|
// We'll split $entries into blocks of 2500 (random number) |
|
271
|
|
|
// and process the deletion in chunks. |
|
272
|
|
|
$chunks = array_chunk($entries, 2500); |
|
273
|
|
|
|
|
274
|
|
|
foreach ($chunks as $chunk) { |
|
275
|
|
|
// If we weren't given a `section_id` we'll have to process individually |
|
276
|
|
|
// If we don't need data for any field, we can process the whole chunk |
|
277
|
|
|
// without building Entry objects, otherwise we'll need to build |
|
278
|
|
|
// Entry objects with data |
|
279
|
|
|
if (is_null($section_id) || !$needs_data) { |
|
280
|
|
|
$entries = $chunk; |
|
281
|
|
|
} elseif ($needs_data) { |
|
282
|
|
|
$entries = self::fetch($chunk, $section_id); |
|
283
|
|
|
} |
|
284
|
|
|
|
|
285
|
|
|
if ($needs_data) { |
|
286
|
|
|
foreach ($entries as $id) { |
|
287
|
|
|
// Handles the case where `section_id` was not provided |
|
288
|
|
|
if (is_null($section_id)) { |
|
289
|
|
|
$e = self::fetch($id); |
|
290
|
|
|
|
|
291
|
|
|
if (!is_array($e)) { |
|
292
|
|
|
continue; |
|
293
|
|
|
} |
|
294
|
|
|
|
|
295
|
|
|
$e = current($e); |
|
296
|
|
|
|
|
297
|
|
|
if (!$e instanceof Entry) { |
|
298
|
|
|
continue; |
|
299
|
|
|
} |
|
300
|
|
|
|
|
301
|
|
|
// If we needed data, whole Entry objects will exist |
|
302
|
|
|
} elseif ($needs_data) { |
|
303
|
|
|
$e = $id; |
|
304
|
|
|
$id = $e->get('id'); |
|
305
|
|
|
} |
|
306
|
|
|
|
|
307
|
|
|
// Time to loop over it and send it to the fields. |
|
308
|
|
|
// Note we can't rely on the `$fields` array as we may |
|
309
|
|
|
// also be dealing with the case where `section_id` hasn't |
|
310
|
|
|
// been provided |
|
311
|
|
|
$entry_data = $e->getData(); |
|
|
|
|
|
|
312
|
|
|
|
|
313
|
|
|
foreach ($entry_data as $field_id => $data) { |
|
314
|
|
|
$field = FieldManager::fetch($field_id); |
|
315
|
|
|
$field->entryDataCleanup($id, $data); |
|
|
|
|
|
|
316
|
|
|
} |
|
317
|
|
|
} |
|
318
|
|
|
} else { |
|
319
|
|
|
foreach ($fields as $field) { |
|
|
|
|
|
|
320
|
|
|
$field->entryDataCleanup($chunk); |
|
321
|
|
|
} |
|
322
|
|
|
} |
|
323
|
|
|
|
|
324
|
|
|
$placeholders = Database::addPlaceholders($chunk); |
|
325
|
|
|
Symphony::Database()->delete('tbl_entries', " `id` IN ($placeholders) ", $chunk); |
|
326
|
|
|
} |
|
327
|
|
|
|
|
328
|
|
|
return true; |
|
329
|
|
|
} |
|
330
|
|
|
|
|
331
|
|
|
/** |
|
332
|
|
|
* This function will return an array of Entry objects given an ID or an array of ID's. |
|
333
|
|
|
* Do not provide `$entry_id` as an array if not specifying the `$section_id`. This function |
|
334
|
|
|
* is commonly passed custom SQL statements through the `$where` and `$join` parameters |
|
335
|
|
|
* that is generated by the fields of this section |
|
336
|
|
|
* |
|
337
|
|
|
* @param integer|array $entry_id |
|
338
|
|
|
* An array of Entry ID's or an Entry ID to return |
|
339
|
|
|
* @param integer $section_id |
|
340
|
|
|
* The ID of the Section that these entries are contained in |
|
341
|
|
|
* @param integer $limit |
|
342
|
|
|
* The limit of entries to return |
|
343
|
|
|
* @param integer $start |
|
344
|
|
|
* The starting offset of the entries to return |
|
345
|
|
|
* @param string $where |
|
346
|
|
|
* Any custom WHERE clauses. The tbl_entries alias is `e` |
|
347
|
|
|
* @param string $joins |
|
348
|
|
|
* Any custom JOIN's |
|
349
|
|
|
* @param boolean $group |
|
350
|
|
|
* Whether the entries need to be grouped by Entry ID or not |
|
351
|
|
|
* @param boolean $buildentries |
|
352
|
|
|
* Whether to return an array of entry ID's or Entry objects. Defaults to |
|
353
|
|
|
* true, which will return Entry objects |
|
354
|
|
|
* @param array $element_names |
|
355
|
|
|
* Choose whether to get data from a subset of fields or all fields in a section, |
|
356
|
|
|
* by providing an array of field names. Defaults to null, which will load data |
|
357
|
|
|
* from all fields in a section. |
|
358
|
|
|
* @param boolean $enable_sort |
|
359
|
|
|
* Defaults to true, if false this function will not apply any sorting |
|
360
|
|
|
* @throws Exception |
|
361
|
|
|
* @return array |
|
362
|
|
|
* If `$buildentries` is true, this function will return an array of Entry objects, |
|
363
|
|
|
* otherwise it will return an associative array of Entry data from `tbl_entries` |
|
364
|
|
|
*/ |
|
365
|
|
|
public static function fetch($entry_id = null, $section_id = null, $limit = null, $start = null, $where = null, $joins = null, $group = false, $buildentries = true, $element_names = null, $enable_sort = true) |
|
366
|
|
|
{ |
|
367
|
|
|
$sort = null; |
|
368
|
|
|
|
|
369
|
|
|
if (!$entry_id && !$section_id) { |
|
|
|
|
|
|
370
|
|
|
return false; |
|
371
|
|
|
} |
|
372
|
|
|
|
|
373
|
|
|
if (!$section_id) { |
|
|
|
|
|
|
374
|
|
|
$section_id = self::fetchEntrySectionID($entry_id); |
|
|
|
|
|
|
375
|
|
|
} |
|
376
|
|
|
|
|
377
|
|
|
$section = SectionManager::fetch($section_id); |
|
378
|
|
|
if (!is_object($section)) { |
|
379
|
|
|
return false; |
|
380
|
|
|
} |
|
381
|
|
|
|
|
382
|
|
|
// SORTING |
|
383
|
|
|
// A single $entry_id doesn't need to be sorted on, or if it's explicitly disabled |
|
384
|
|
|
if ((!is_array($entry_id) && !is_null($entry_id) && is_int($entry_id)) || !$enable_sort) { |
|
385
|
|
|
$sort = null; |
|
386
|
|
|
|
|
387
|
|
|
// Check for RAND first, since this works independently of any specific field |
|
388
|
|
|
} elseif (self::$_fetchSortDirection === 'RAND') { |
|
389
|
|
|
$sort = 'ORDER BY RAND() '; |
|
390
|
|
|
|
|
391
|
|
|
// Handle Creation Date or the old Date sorting |
|
392
|
|
|
} elseif (self::$_fetchSortField === 'system:creation-date' || self::$_fetchSortField === 'date') { |
|
|
|
|
|
|
393
|
|
|
$sort = sprintf('ORDER BY `e`.`creation_date_gmt` %s', self::$_fetchSortDirection); |
|
394
|
|
|
|
|
395
|
|
|
// Handle Modification Date sorting |
|
396
|
|
|
} elseif (self::$_fetchSortField === 'system:modification-date') { |
|
|
|
|
|
|
397
|
|
|
$sort = sprintf('ORDER BY `e`.`modification_date_gmt` %s', self::$_fetchSortDirection); |
|
398
|
|
|
|
|
399
|
|
|
// Handle sorting for System ID |
|
400
|
|
View Code Duplication |
} elseif (self::$_fetchSortField === 'system:id' || self::$_fetchSortField === 'id') { |
|
|
|
|
|
|
401
|
|
|
$sort = sprintf('ORDER BY `e`.`id` %s', self::$_fetchSortDirection); |
|
402
|
|
|
|
|
403
|
|
|
// Handle when the sort field is an actual Field |
|
404
|
|
|
} elseif (self::$_fetchSortField && $field = FieldManager::fetch(self::$_fetchSortField)) { |
|
405
|
|
|
if ($field->isSortable()) { |
|
|
|
|
|
|
406
|
|
|
$field->buildSortingSQL($joins, $where, $sort, self::$_fetchSortDirection); |
|
|
|
|
|
|
407
|
|
|
} |
|
408
|
|
|
|
|
409
|
|
|
// Handle if the section has a default sorting field |
|
410
|
|
|
} elseif ($section->getSortingField() && $field = FieldManager::fetch($section->getSortingField())) { |
|
|
|
|
|
|
411
|
|
|
if ($field->isSortable()) { |
|
|
|
|
|
|
412
|
|
|
$field->buildSortingSQL($joins, $where, $sort, $section->getSortingOrder()); |
|
|
|
|
|
|
413
|
|
|
} |
|
414
|
|
|
|
|
415
|
|
|
if (!empty($field) && !$group) { |
|
416
|
|
|
$group = $field->requiresSQLGrouping(); |
|
|
|
|
|
|
417
|
|
|
} |
|
418
|
|
View Code Duplication |
} else if (self::$_fetchSortField === 'system:id' || self::$_fetchSortField === 'id') { |
|
|
|
|
|
|
419
|
|
|
$sort = 'ORDER BY `e`.`id` ' . self::$_fetchSortDirection; |
|
420
|
|
|
|
|
421
|
|
|
} else { |
|
422
|
|
|
$sort = sprintf('ORDER BY `e`.`id` %s', self::$_fetchSortDirection); |
|
423
|
|
|
} |
|
424
|
|
|
|
|
425
|
|
|
if ($field && !$group) { |
|
|
|
|
|
|
426
|
|
|
$group = $field->requiresSQLGrouping(); |
|
|
|
|
|
|
427
|
|
|
} |
|
428
|
|
|
|
|
429
|
|
|
if ($entry_id && !is_array($entry_id)) { |
|
430
|
|
|
$entry_id = array($entry_id); |
|
431
|
|
|
} |
|
432
|
|
|
|
|
433
|
|
|
$sql = sprintf(" |
|
434
|
|
|
SELECT %s`e`.`id`, `e`.section_id, `e`.`author_id`, |
|
435
|
|
|
`e`.`creation_date` AS `creation_date`, |
|
436
|
|
|
`e`.`modification_date` AS `modification_date` |
|
437
|
|
|
FROM `tbl_entries` AS `e` |
|
438
|
|
|
%s |
|
439
|
|
|
WHERE 1 |
|
440
|
|
|
%s |
|
441
|
|
|
%s |
|
442
|
|
|
%s |
|
443
|
|
|
%s |
|
444
|
|
|
%s |
|
445
|
|
|
", |
|
446
|
|
|
$group ? 'DISTINCT ' : '', |
|
447
|
|
|
$joins, |
|
448
|
|
|
$entry_id ? "AND `e`.`id` IN ('".implode("', '", $entry_id)."') " : '', |
|
449
|
|
|
$section_id ? sprintf("AND `e`.`section_id` = %d", $section_id) : '', |
|
450
|
|
|
$where, |
|
451
|
|
|
$sort, |
|
452
|
|
|
$limit ? sprintf('LIMIT %d, %d', $start, $limit) : '' |
|
453
|
|
|
); |
|
454
|
|
|
|
|
455
|
|
|
$rows = Symphony::Database()->fetch($sql); |
|
456
|
|
|
|
|
457
|
|
|
// Create UNIX timestamps, as it has always been (Re: #2501) |
|
458
|
|
|
foreach ($rows as &$entry) { |
|
|
|
|
|
|
459
|
|
|
$entry['creation_date'] = DateTimeObj::get('U', $entry['creation_date']); |
|
460
|
|
|
$entry['modification_date'] = DateTimeObj::get('U', $entry['modification_date']); |
|
461
|
|
|
} |
|
462
|
|
|
unset($entry); |
|
463
|
|
|
|
|
464
|
|
|
return ($buildentries && (is_array($rows) && !empty($rows)) ? self::__buildEntries($rows, $section_id, $element_names) : $rows); |
|
465
|
|
|
} |
|
466
|
|
|
|
|
467
|
|
|
/** |
|
468
|
|
|
* Given an array of Entry data from `tbl_entries` and a section ID, return an |
|
469
|
|
|
* array of Entry objects. For performance reasons, it's possible to pass an array |
|
470
|
|
|
* of field handles via `$element_names`, so that only a subset of the section schema |
|
471
|
|
|
* will be queried. This function currently only supports Entry from one section at a |
|
472
|
|
|
* time. |
|
473
|
|
|
* |
|
474
|
|
|
* @param array $rows |
|
475
|
|
|
* An array of Entry data from `tbl_entries` including the Entry ID, Entry section, |
|
476
|
|
|
* the ID of the Author who created the Entry, and a Unix timestamp of creation |
|
477
|
|
|
* @param integer $section_id |
|
478
|
|
|
* The section ID of the entries in the `$rows` |
|
479
|
|
|
* @param array $element_names |
|
480
|
|
|
* Choose whether to get data from a subset of fields or all fields in a section, |
|
481
|
|
|
* by providing an array of field names. Defaults to null, which will load data |
|
482
|
|
|
* from all fields in a section. |
|
483
|
|
|
* @throws DatabaseException |
|
484
|
|
|
* @return array |
|
485
|
|
|
* An array of Entry objects |
|
486
|
|
|
*/ |
|
487
|
|
|
public static function __buildEntries(array $rows, $section_id, $element_names = null) |
|
488
|
|
|
{ |
|
489
|
|
|
$entries = array(); |
|
490
|
|
|
|
|
491
|
|
|
if (empty($rows)) { |
|
492
|
|
|
return $entries; |
|
493
|
|
|
} |
|
494
|
|
|
|
|
495
|
|
|
$schema = FieldManager::fetchFieldIDFromElementName($element_names, $section_id); |
|
|
|
|
|
|
496
|
|
|
|
|
497
|
|
|
if (is_int($schema)) { |
|
498
|
|
|
$schema = array($schema); |
|
499
|
|
|
} |
|
500
|
|
|
|
|
501
|
|
|
$raw = array(); |
|
502
|
|
|
$rows_string = ''; |
|
503
|
|
|
|
|
504
|
|
|
// Append meta data: |
|
505
|
|
|
foreach ($rows as $entry) { |
|
506
|
|
|
$raw[$entry['id']]['meta'] = $entry; |
|
507
|
|
|
$rows_string .= $entry['id'] . ','; |
|
508
|
|
|
} |
|
509
|
|
|
|
|
510
|
|
|
$rows_string = trim($rows_string, ','); |
|
511
|
|
|
|
|
512
|
|
|
// Append field data: |
|
513
|
|
|
if (is_array($schema)) { |
|
514
|
|
|
foreach ($schema as $field_id) { |
|
515
|
|
|
try { |
|
516
|
|
|
$row = Symphony::Database()->fetch("SELECT * FROM `tbl_entries_data_{$field_id}` WHERE `entry_id` IN ($rows_string) ORDER BY `id` ASC"); |
|
517
|
|
|
} catch (Exception $e) { |
|
518
|
|
|
// No data due to error |
|
519
|
|
|
continue; |
|
520
|
|
|
} |
|
521
|
|
|
|
|
522
|
|
|
if (!is_array($row) || empty($row)) { |
|
523
|
|
|
continue; |
|
524
|
|
|
} |
|
525
|
|
|
|
|
526
|
|
|
foreach ($row as $r) { |
|
527
|
|
|
$entry_id = $r['entry_id']; |
|
528
|
|
|
|
|
529
|
|
|
unset($r['id']); |
|
530
|
|
|
unset($r['entry_id']); |
|
531
|
|
|
|
|
532
|
|
|
if (!isset($raw[$entry_id]['fields'][$field_id])) { |
|
533
|
|
|
$raw[$entry_id]['fields'][$field_id] = $r; |
|
534
|
|
|
} else { |
|
535
|
|
|
foreach (array_keys($r) as $key) { |
|
536
|
|
|
// If this field already has been set, we need to take the existing |
|
537
|
|
|
// value and make it array, adding the current value to it as well |
|
538
|
|
|
// There is a special check incase the the field's value has been |
|
539
|
|
|
// purposely set to null in the database. |
|
540
|
|
|
if ( |
|
541
|
|
|
( |
|
542
|
|
|
isset($raw[$entry_id]['fields'][$field_id][$key]) |
|
543
|
|
|
|| is_null($raw[$entry_id]['fields'][$field_id][$key]) |
|
544
|
|
|
) |
|
545
|
|
|
&& !is_array($raw[$entry_id]['fields'][$field_id][$key]) |
|
546
|
|
|
) { |
|
547
|
|
|
$raw[$entry_id]['fields'][$field_id][$key] = array( |
|
548
|
|
|
$raw[$entry_id]['fields'][$field_id][$key], |
|
549
|
|
|
$r[$key] |
|
550
|
|
|
); |
|
551
|
|
|
|
|
552
|
|
|
// This key/value hasn't been set previously, so set it |
|
553
|
|
|
} elseif (!isset($raw[$entry_id]['fields'][$field_id][$key])) { |
|
554
|
|
|
$raw[$entry_id]['fields'][$field_id] = array($r[$key]); |
|
555
|
|
|
|
|
556
|
|
|
// This key has been set and it's an array, so just append |
|
557
|
|
|
// this value onto the array |
|
558
|
|
|
} else { |
|
559
|
|
|
$raw[$entry_id]['fields'][$field_id][$key][] = $r[$key]; |
|
560
|
|
|
} |
|
561
|
|
|
} |
|
562
|
|
|
} |
|
563
|
|
|
} |
|
564
|
|
|
} |
|
565
|
|
|
} |
|
566
|
|
|
|
|
567
|
|
|
// Loop over the array of entry data and convert it to an array of Entry objects |
|
568
|
|
|
foreach ($raw as $entry) { |
|
569
|
|
|
$obj = self::create(); |
|
570
|
|
|
|
|
571
|
|
|
$obj->set('id', $entry['meta']['id']); |
|
572
|
|
|
$obj->set('author_id', $entry['meta']['author_id']); |
|
573
|
|
|
$obj->set('section_id', $entry['meta']['section_id']); |
|
574
|
|
|
$obj->set('creation_date', DateTimeObj::get('c', $entry['meta']['creation_date'])); |
|
575
|
|
|
|
|
576
|
|
|
if (isset($entry['meta']['modification_date'])) { |
|
577
|
|
|
$obj->set('modification_date', DateTimeObj::get('c', $entry['meta']['modification_date'])); |
|
578
|
|
|
} else { |
|
579
|
|
|
$obj->set('modification_date', $obj->get('creation_date')); |
|
580
|
|
|
} |
|
581
|
|
|
|
|
582
|
|
|
if (isset($entry['fields']) && is_array($entry['fields'])) { |
|
583
|
|
|
foreach ($entry['fields'] as $field_id => $data) { |
|
584
|
|
|
$obj->setData($field_id, $data); |
|
585
|
|
|
} |
|
586
|
|
|
} |
|
587
|
|
|
|
|
588
|
|
|
$entries[] = $obj; |
|
589
|
|
|
} |
|
590
|
|
|
|
|
591
|
|
|
return $entries; |
|
592
|
|
|
} |
|
593
|
|
|
|
|
594
|
|
|
|
|
595
|
|
|
/** |
|
596
|
|
|
* Given an Entry ID, return the Section ID that it belongs to |
|
597
|
|
|
* |
|
598
|
|
|
* @param integer $entry_id |
|
599
|
|
|
* The ID of the Entry to return it's section |
|
600
|
|
|
* @return integer |
|
601
|
|
|
* The Section ID for this Entry's section |
|
602
|
|
|
*/ |
|
603
|
|
|
public static function fetchEntrySectionID($entry_id) |
|
604
|
|
|
{ |
|
605
|
|
|
return Symphony::Database()->fetchVar('section_id', 0, "SELECT `section_id` FROM `tbl_entries` WHERE `id` = ? LIMIT 1", |
|
606
|
|
|
array($entry_id) |
|
607
|
|
|
); |
|
608
|
|
|
} |
|
609
|
|
|
|
|
610
|
|
|
/** |
|
611
|
|
|
* Return the count of the number of entries in a particular section. |
|
612
|
|
|
* |
|
613
|
|
|
* @param integer $section_id |
|
614
|
|
|
* The ID of the Section where the Entries are to be counted |
|
615
|
|
|
* @param string $where |
|
616
|
|
|
* Any custom WHERE clauses |
|
617
|
|
|
* @param string $joins |
|
618
|
|
|
* Any custom JOIN's |
|
619
|
|
|
* @param boolean $group |
|
620
|
|
|
* Whether the entries need to be grouped by Entry ID or not |
|
621
|
|
|
* @return integer |
|
622
|
|
|
*/ |
|
623
|
|
|
public static function fetchCount($section_id = null, $where = null, $joins = null, $group = false) |
|
624
|
|
|
{ |
|
625
|
|
|
if (is_null($section_id)) { |
|
626
|
|
|
return false; |
|
627
|
|
|
} |
|
628
|
|
|
|
|
629
|
|
|
$section = SectionManager::fetch($section_id); |
|
630
|
|
|
|
|
631
|
|
|
if (!is_object($section)) { |
|
632
|
|
|
return false; |
|
633
|
|
|
} |
|
634
|
|
|
|
|
635
|
|
|
return Symphony::Database()->fetchVar('count', 0, sprintf(" |
|
636
|
|
|
SELECT COUNT(%s`e`.id) as `count` |
|
637
|
|
|
FROM `tbl_entries` AS `e` |
|
638
|
|
|
%s |
|
639
|
|
|
WHERE `e`.`section_id` = %d |
|
640
|
|
|
%s |
|
641
|
|
|
", |
|
642
|
|
|
$group ? 'DISTINCT ' : '', |
|
643
|
|
|
$joins, |
|
644
|
|
|
$section_id, |
|
645
|
|
|
$where |
|
646
|
|
|
)); |
|
647
|
|
|
} |
|
648
|
|
|
|
|
649
|
|
|
/** |
|
650
|
|
|
* Returns an array of Entry objects, with some basic pagination given |
|
651
|
|
|
* the number of Entry's to return and the current starting offset. This |
|
652
|
|
|
* function in turn calls the fetch function that does alot of the heavy |
|
653
|
|
|
* lifting. For instance, if there are 60 entries in a section and the pagination |
|
654
|
|
|
* dictates that per page, 15 entries are to be returned, by passing 2 to |
|
655
|
|
|
* the $page parameter you could return entries 15-30 |
|
656
|
|
|
* |
|
657
|
|
|
* @param integer $page |
|
658
|
|
|
* The page to return, defaults to 1 |
|
659
|
|
|
* @param integer $section_id |
|
660
|
|
|
* The ID of the Section that these entries are contained in |
|
661
|
|
|
* @param integer $entriesPerPage |
|
662
|
|
|
* The number of entries to return per page. |
|
663
|
|
|
* @param string $where |
|
664
|
|
|
* Any custom WHERE clauses |
|
665
|
|
|
* @param string $joins |
|
666
|
|
|
* Any custom JOIN's |
|
667
|
|
|
* @param boolean $group |
|
668
|
|
|
* Whether the entries need to be grouped by Entry ID or not |
|
669
|
|
|
* @param boolean $records_only |
|
670
|
|
|
* If this is set to true, an array of Entry objects will be returned |
|
671
|
|
|
* without any basic pagination information. Defaults to false |
|
672
|
|
|
* @param boolean $buildentries |
|
673
|
|
|
* Whether to return an array of entry ID's or Entry objects. Defaults to |
|
674
|
|
|
* true, which will return Entry objects |
|
675
|
|
|
* @param array $element_names |
|
676
|
|
|
* Choose whether to get data from a subset of fields or all fields in a section, |
|
677
|
|
|
* by providing an array of field names. Defaults to null, which will load data |
|
678
|
|
|
* from all fields in a section. |
|
679
|
|
|
* @throws Exception |
|
680
|
|
|
* @return array |
|
681
|
|
|
* Either an array of Entry objects, or an associative array containing |
|
682
|
|
|
* the total entries, the start position, the entries per page and the |
|
683
|
|
|
* Entry objects |
|
684
|
|
|
*/ |
|
685
|
|
|
public static function fetchByPage($page = 1, $section_id, $entriesPerPage, $where = null, $joins = null, $group = false, $records_only = false, $buildentries = true, array $element_names = null) |
|
686
|
|
|
{ |
|
687
|
|
|
if ($entriesPerPage !== null && !is_string($entriesPerPage) && !is_numeric($entriesPerPage)) { |
|
688
|
|
|
throw new Exception(__('Entry limit specified was not a valid type. String or Integer expected.')); |
|
689
|
|
|
} elseif ($entriesPerPage === null) { |
|
690
|
|
|
$records = self::fetch(null, $section_id, null, null, $where, $joins, $group, $buildentries, $element_names); |
|
691
|
|
|
|
|
692
|
|
|
$count = self::fetchCount($section_id, $where, $joins, $group); |
|
693
|
|
|
|
|
694
|
|
|
$entries = array( |
|
695
|
|
|
'total-entries' => $count, |
|
696
|
|
|
'total-pages' => 1, |
|
697
|
|
|
'remaining-pages' => 0, |
|
698
|
|
|
'remaining-entries' => 0, |
|
699
|
|
|
'start' => 1, |
|
700
|
|
|
'limit' => $count, |
|
701
|
|
|
'records' => $records |
|
702
|
|
|
); |
|
703
|
|
|
|
|
704
|
|
|
return $entries; |
|
705
|
|
|
} else { |
|
706
|
|
|
$start = (max(1, $page) - 1) * $entriesPerPage; |
|
707
|
|
|
|
|
708
|
|
|
$records = ($entriesPerPage === '0' ? null : self::fetch(null, $section_id, $entriesPerPage, $start, $where, $joins, $group, $buildentries, $element_names)); |
|
709
|
|
|
|
|
710
|
|
|
if ($records_only) { |
|
711
|
|
|
return array('records' => $records); |
|
712
|
|
|
} |
|
713
|
|
|
|
|
714
|
|
|
$entries = array( |
|
715
|
|
|
'total-entries' => self::fetchCount($section_id, $where, $joins, $group), |
|
716
|
|
|
'records' => $records, |
|
717
|
|
|
'start' => max(1, $start), |
|
718
|
|
|
'limit' => $entriesPerPage |
|
719
|
|
|
); |
|
720
|
|
|
|
|
721
|
|
|
$entries['remaining-entries'] = max(0, $entries['total-entries'] - ($start + $entriesPerPage)); |
|
722
|
|
|
$entries['total-pages'] = max(1, ceil($entries['total-entries'] * (1 / $entriesPerPage))); |
|
723
|
|
|
$entries['remaining-pages'] = max(0, $entries['total-pages'] - $page); |
|
724
|
|
|
|
|
725
|
|
|
return $entries; |
|
726
|
|
|
} |
|
727
|
|
|
} |
|
728
|
|
|
|
|
729
|
|
|
/** |
|
730
|
|
|
* Creates a new Entry object using this class as the parent. |
|
731
|
|
|
* |
|
732
|
|
|
* @return Entry |
|
733
|
|
|
*/ |
|
734
|
|
|
public static function create() |
|
735
|
|
|
{ |
|
736
|
|
|
return new Entry; |
|
737
|
|
|
} |
|
738
|
|
|
} |
|
739
|
|
|
|
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.
In this case you can add the
@ignorePhpDoc annotation to the duplicate definition and it will be ignored.