Open Journal Systems  2.4.4
 All Classes Namespaces Functions Variables Groups Pages
DAO.inc.php
1 <?php
2 
22 import('lib.pkp.classes.db.DBConnection');
23 import('lib.pkp.classes.db.DAOResultFactory');
24 import('lib.pkp.classes.core.DataObject');
25 
26 define('SORT_DIRECTION_ASC', 0x00001);
27 define('SORT_DIRECTION_DESC', 0x00002);
28 
29 class DAO {
32 
37  function &getDataSource() {
38  return $this->_dataSource;
39  }
40 
45  function setDataSource(&$dataSource) {
46  $this->_dataSource =& $dataSource;
47  }
48 
52  function concat() {
53  $args = func_get_args();
54  return call_user_func_array(array($this->getDataSource(), 'Concat'), $args);
55  }
56 
61  function DAO($dataSource = null, $callHooks = true) {
62  if ($callHooks === true && checkPhpVersion('4.3.0')) {
63  // Call hooks based on the object name. Results
64  // in hook calls named e.g. "sessiondao::_Constructor"
65  if (HookRegistry::call(strtolower_codesafe(get_class($this)) . '::_Constructor', array(&$this, &$dataSource))) {
66  return;
67  }
68  }
69 
70  if (!isset($dataSource)) {
72  } else {
73  $this->setDataSource($dataSource);
74  }
75  }
76 
83  function &retrieve($sql, $params = false, $callHooks = true) {
84  if ($callHooks === true && checkPhpVersion('4.3.0')) {
85  $trace = debug_backtrace();
86  // Call hooks based on the calling entity, assuming
87  // this method is only called by a subclass. Results
88  // in hook calls named e.g. "sessiondao::_getsession"
89  // (always lower case).
90  $value = null;
91  if (HookRegistry::call(strtolower_codesafe($trace[1]['class'] . '::_' . $trace[1]['function']), array(&$sql, &$params, &$value))) {
92  return $value;
93  }
94  }
95 
96  $start = Core::microtime();
97  $dataSource = $this->getDataSource();
98  $result =& $dataSource->execute($sql, $params !== false && !is_array($params) ? array($params) : $params);
99  DBConnection::logQuery($sql, $start, $params);
100  if ($dataSource->errorNo()) {
101  // FIXME Handle errors more elegantly.
102  fatalError('DB Error: ' . $dataSource->errorMsg());
103  }
104  return $result;
105  }
106 
113  function &retrieveCached($sql, $params = false, $secsToCache = 3600, $callHooks = true) {
114  if ($callHooks === true && checkPhpVersion('4.3.0')) {
115  $trace = debug_backtrace();
116  // Call hooks based on the calling entity, assuming
117  // this method is only called by a subclass. Results
118  // in hook calls named e.g. "sessiondao::_getsession"
119  // (all lowercase).
120  $value = null;
121  if (HookRegistry::call(strtolower_codesafe($trace[1]['class'] . '::_' . $trace[1]['function']), array(&$sql, &$params, &$secsToCache, &$value))) {
122  return $value;
123  }
124  }
125 
126  $this->setCacheDir();
127 
128  $start = Core::microtime();
129  $dataSource = $this->getDataSource();
130  $result =& $dataSource->CacheExecute($secsToCache, $sql, $params !== false && !is_array($params) ? array($params) : $params);
131  DBConnection::logQuery($sql, $start, $params);
132  if ($dataSource->errorNo()) {
133  // FIXME Handle errors more elegantly.
134  fatalError('DB Error: ' . $dataSource->errorMsg());
135  }
136  return $result;
137  }
138 
147  function &retrieveLimit($sql, $params = false, $numRows = false, $offset = false, $callHooks = true) {
148  if ($callHooks === true && checkPhpVersion('4.3.0')) {
149  $trace = debug_backtrace();
150  // Call hooks based on the calling entity, assuming
151  // this method is only called by a subclass. Results
152  // in hook calls named e.g. "sessiondao::_getsession"
153  // (all lowercase).
154  $value = null;
155  if (HookRegistry::call(strtolower_codesafe($trace[1]['class'] . '::_' . $trace[1]['function']), array(&$sql, &$params, &$numRows, &$offset, &$value))) {
156  return $value;
157  }
158  }
159 
160  $start = Core::microtime();
161  $dataSource = $this->getDataSource();
162  $result =& $dataSource->selectLimit($sql, $numRows === false ? -1 : $numRows, $offset === false ? -1 : $offset, $params !== false && !is_array($params) ? array($params) : $params);
163  DBConnection::logQuery($sql, $start, $params);
164  if ($dataSource->errorNo()) {
165  fatalError('DB Error: ' . $dataSource->errorMsg());
166  }
167  return $result;
168  }
169 
176  function &retrieveRange($sql, $params = false, $dbResultRange = null, $callHooks = true) {
177  if ($callHooks === true && checkPhpVersion('4.3.0')) {
178  $trace = debug_backtrace();
179  // Call hooks based on the calling entity, assuming
180  // this method is only called by a subclass. Results
181  // in hook calls named e.g. "sessiondao::_getsession"
182  $value = null;
183  if (HookRegistry::call(strtolower_codesafe($trace[1]['class'] . '::_' . $trace[1]['function']), array(&$sql, &$params, &$dbResultRange, &$value))) {
184  return $value;
185  }
186  }
187 
188  if (isset($dbResultRange) && $dbResultRange->isValid()) {
189  $start = Core::microtime();
190  $dataSource = $this->getDataSource();
191  $result =& $dataSource->PageExecute($sql, $dbResultRange->getCount(), $dbResultRange->getPage(), $params);
192  DBConnection::logQuery($sql, $start, $params);
193  if ($dataSource->errorNo()) {
194  fatalError('DB Error: ' . $dataSource->errorMsg());
195  }
196  }
197  else {
198  $result =& $this->retrieve($sql, $params, false);
199  }
200  return $result;
201  }
202 
211  function update($sql, $params = false, $callHooks = true, $dieOnError = true) {
212  if ($callHooks === true && checkPhpVersion('4.3.0')) {
213  $trace = debug_backtrace();
214  // Call hooks based on the calling entity, assuming
215  // this method is only called by a subclass. Results
216  // in hook calls named e.g. "sessiondao::_updateobject"
217  // (all lowercase)
218  $value = null;
219  if (HookRegistry::call(strtolower_codesafe($trace[1]['class'] . '::_' . $trace[1]['function']), array(&$sql, &$params, &$value))) {
220  return $value;
221  }
222  }
223 
224  $start = Core::microtime();
225  $dataSource = $this->getDataSource();
226  $dataSource->execute($sql, $params !== false && !is_array($params) ? array($params) : $params);
227  DBConnection::logQuery($sql, $start, $params);
228  if ($dieOnError && $dataSource->errorNo()) {
229  fatalError('DB Error: ' . $dataSource->errorMsg());
230  }
231  return $dataSource->errorNo() == 0 ? true : false;
232  }
233 
240  function replace($table, $arrFields, $keyCols) {
241  $dataSource = $this->getDataSource();
242  $arrFields = array_map(array($dataSource, 'qstr'), $arrFields);
243  $dataSource->Replace($table, $arrFields, $keyCols, false);
244  }
245 
252  function getInsertId($table = '', $id = '', $callHooks = true) {
253  $dataSource = $this->getDataSource();
254  return $dataSource->po_insert_id($table, $id);
255  }
256 
261  function getAffectedRows() {
262  $dataSource = $this->getDataSource();
263  return $dataSource->Affected_Rows();
264  }
265 
271  function setCacheDir() {
272  static $cacheDir;
273  if (!isset($cacheDir)) {
274  global $ADODB_CACHE_DIR;
275 
276  $cacheDir = CacheManager::getFileCachePath() . '/_db';
277 
278  $ADODB_CACHE_DIR = $cacheDir;
279  }
280  }
281 
285  function flushCache() {
286  $this->setCacheDir();
287  $dataSource = $this->getDataSource();
288  $dataSource->CacheFlush();
289  }
290 
296  function datetimeToDB($dt) {
297  $dataSource = $this->getDataSource();
298  return $dataSource->DBTimeStamp($dt);
299  }
300 
306  function dateToDB($d) {
307  $dataSource = $this->getDataSource();
308  return $dataSource->DBDate($d);
309  }
310 
316  function datetimeFromDB($dt) {
317  if ($dt === null) return null;
318  $dataSource = $this->getDataSource();
319  return $dataSource->UserTimeStamp($dt, 'Y-m-d H:i:s');
320  }
326  function dateFromDB($d) {
327  if ($d === null) return null;
328  $dataSource = $this->getDataSource();
329  return $dataSource->UserDate($d, 'Y-m-d');
330  }
331 
338  function convertFromDB($value, $type) {
339  switch ($type) {
340  case 'bool':
341  $value = (bool) $value;
342  break;
343  case 'int':
344  $value = (int) $value;
345  break;
346  case 'float':
347  $value = (float) $value;
348  break;
349  case 'object':
350  $value = unserialize($value);
351  break;
352  case 'date':
353  if ($value !== null) $value = strtotime($value);
354  break;
355  case 'string':
356  default:
357  // Nothing required.
358  break;
359  }
360  return $value;
361  }
362 
368  function getType($value) {
369  switch (gettype($value)) {
370  case 'boolean':
371  case 'bool':
372  return 'bool';
373  case 'integer':
374  case 'int':
375  return 'int';
376  case 'double':
377  case 'float':
378  return 'float';
379  case 'array':
380  case 'object':
381  return 'object';
382  case 'string':
383  default:
384  return 'string';
385  }
386  }
387 
394  function convertToDB($value, &$type) {
395  if ($type == null) {
396  $type = $this->getType($value);
397  }
398 
399  switch ($type) {
400  case 'object':
401  $value = serialize($value);
402  break;
403  case 'bool':
404  // Cast to boolean, ensuring that string
405  // "false" evaluates to boolean false
406  $value = ($value && $value !== 'false') ? 1 : 0;
407  break;
408  case 'int':
409  $value = (int) $value;
410  break;
411  case 'float':
412  $value = (float) $value;
413  break;
414  case 'date':
415  if ($value !== null) {
416  if (!is_numeric($value)) $value = strtotime($value);
417  $value = strftime('%Y-%m-%d %H:%M:%S', $value);
418  }
419  break;
420  case 'string':
421  default:
422  // do nothing.
423  }
424 
425  return $value;
426  }
427 
428  function nullOrInt($value) {
429  return (empty($value)?null:(int) $value);
430  }
431 
432  function getAdditionalFieldNames() {
433  $returner = array();
434  // Call hooks based on the calling entity, assuming
435  // this method is only called by a subclass. Results
436  // in hook calls named e.g. "sessiondao::getAdditionalFieldNames"
437  // (class names lowercase)
438  HookRegistry::call(strtolower_codesafe(get_class($this)) . '::getAdditionalFieldNames', array(&$this, &$returner));
439 
440  return $returner;
441  }
442 
443  function getLocaleFieldNames() {
444  $returner = array();
445  // Call hooks based on the calling entity, assuming
446  // this method is only called by a subclass. Results
447  // in hook calls named e.g. "sessiondao::getLocaleFieldNames"
448  // (class names lowercase)
449  HookRegistry::call(strtolower_codesafe(get_class($this)) . '::getLocaleFieldNames', array(&$this, &$returner));
450 
451  return $returner;
452  }
453 
460  function updateDataObjectSettings($tableName, &$dataObject, $idArray) {
461  // Initialize variables
462  $idFields = array_keys($idArray);
463  $idFields[] = 'locale';
464  $idFields[] = 'setting_name';
465 
466  // Build a data structure that we can process efficiently.
467  $translated = $metadata = 1;
468  $settings = !$metadata;
469  $settingFields = array(
470  // Translated data
471  $translated => array(
472  $settings => $this->getLocaleFieldNames(),
473  $metadata => $dataObject->getLocaleMetadataFieldNames()
474  ),
475  // Shared data
476  !$translated => array(
477  $settings => $this->getAdditionalFieldNames(),
478  $metadata => $dataObject->getAdditionalMetadataFieldNames()
479  )
480  );
481 
482  // Loop over all fields and update them in the settings table
483  $updateArray = $idArray;
484  $noLocale = 0;
485  $staleMetadataSettings = array();
486  foreach ($settingFields as $isTranslated => $fieldTypes) {
487  foreach ($fieldTypes as $isMetadata => $fieldNames) {
488  foreach ($fieldNames as $fieldName) {
489  // Now we have the following control data:
490  // - $isTranslated: true for translated data, false data shared between locales
491  // - $isMetadata: true for metadata fields, false for normal settings
492  // - $fieldName: the field in the data object to be updated
493  if ($dataObject->hasData($fieldName)) {
494  if ($isTranslated) {
495  // Translated data comes in as an array
496  // with the locale as the key.
497  $values = $dataObject->getData($fieldName);
498  if (!is_array($values)) {
499  // Inconsistent data: should have been an array
500  assert(false);
501  continue;
502  }
503  } else {
504  // Transform shared data into an array so that
505  // we can handle them the same way as translated data.
506  $values = array(
507  $noLocale => $dataObject->getData($fieldName)
508  );
509  }
510 
511  // Loop over the values and update them in the database
512  foreach ($values as $locale => $value) {
513  $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
514  $updateArray['setting_name'] = $fieldName;
515  $updateArray['setting_type'] = null;
516  // Convert the data value and implicitly set the setting type.
517  $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
518  $this->replace($tableName, $updateArray, $idFields);
519  }
520  } else {
521  // Meta-data fields are maintained "sparsly". Only set fields will be
522  // recorded in the settings table. Fields that are not explicity set
523  // in the data object will be deleted.
524  if ($isMetadata) $staleMetadataSettings[] = $fieldName;
525  }
526  }
527  }
528  }
529 
530  // Remove stale meta-data
531  if (count($staleMetadataSettings)) {
532  $removeWhere = '';
533  $removeParams = array();
534  foreach ($idArray as $idField => $idValue) {
535  if (!empty($removeWhere)) $removeWhere .= ' AND ';
536  $removeWhere .= $idField.' = ?';
537  $removeParams[] = $idValue;
538  }
539  $removeWhere .= rtrim(' AND setting_name IN ( '.str_repeat('? ,', count($staleMetadataSettings)), ',').')';
540  $removeParams = array_merge($removeParams, $staleMetadataSettings);
541  $removeSql = 'DELETE FROM '.$tableName.' WHERE '.$removeWhere;
542  $this->update($removeSql, $removeParams);
543  }
544  }
545 
546  function getDataObjectSettings($tableName, $idFieldName, $idFieldValue, &$dataObject) {
547  if ($idFieldName !== null) {
548  $sql = "SELECT * FROM $tableName WHERE $idFieldName = ?";
549  $params = array($idFieldValue);
550  } else {
551  $sql = "SELECT * FROM $tableName";
552  $params = false;
553  }
554  $result =& $this->retrieve($sql, $params);
555 
556  while (!$result->EOF) {
557  $row =& $result->getRowAssoc(false);
558  $dataObject->setData(
559  $row['setting_name'],
560  $this->convertFromDB(
561  $row['setting_value'],
562  $row['setting_type']
563  ),
564  empty($row['locale'])?null:$row['locale']
565  );
566  unset($row);
567  $result->MoveNext();
568  }
569 
570  $result->Close();
571  unset($result);
572  }
573 
578  function getDriver() {
579  $conn =& DBConnection::getInstance();
580  return $conn->getDriver();
581  }
582 
588  function getDirectionMapping($direction) {
589  switch ($direction) {
590  case SORT_DIRECTION_ASC:
591  return 'ASC';
592  case SORT_DIRECTION_DESC:
593  return 'DESC';
594  default:
595  return 'ASC';
596  }
597  }
598 
612  function getDataChangedEvent($elementId = null, $parentElementId = null) {
613  // Create the event data.
614  $eventData = null;
615  if ($elementId) {
616  $eventData = array($elementId);
617  if ($parentElementId) {
618  $eventData['parentElementId'] = $parentElementId;
619  }
620  }
621 
622  // Create and render the JSON message with the
623  // event to be triggered on the client side.
624  import('lib.pkp.classes.core.JSONMessage');
625  $json = new JSONMessage(true);
626  $json->setEvent('dataChanged', $eventData);
627  return $json->getString();
628  }
629 
641  function formatDateToDB($date, $defaultNumWeeks = null, $acceptPastDate = true) {
642  $today = getDate();
643  $todayTimestamp = mktime(0, 0, 0, $today['mon'], $today['mday'], $today['year']);
644  if ($date != null) {
645  $dueDateParts = explode('-', $date);
646 
647  // If we don't accept past dates...
648  if (!$acceptPastDate && $todayTimestamp > strtotime($date)) {
649  // ... return today.
650  return date('Y-m-d H:i:s', $todayTimestamp);
651  } else {
652  // Return the passed date.
653  return date('Y-m-d H:i:s', mktime(0, 0, 0, $dueDateParts[1], $dueDateParts[2], $dueDateParts[0]));
654  }
655  } elseif (isset($defaultNumWeeks)) {
656  // Add the equivalent of $numWeeks weeks, measured in seconds, to $todaysTimestamp.
657  $numWeeks = max((int) $defaultNumWeeks, 2);
658  $newDueDateTimestamp = $todayTimestamp + ($numWeeks * 7 * 24 * 60 * 60);
659  return date('Y-m-d H:i:s', $newDueDateTimestamp);
660  } else {
661  // Either the date or the defaultNumWeeks must be set
662  assert(false);
663  return null;
664  }
665  }
666 }
667 
668 ?>
formatDateToDB($date, $defaultNumWeeks=null, $acceptPastDate=true)
Definition: DAO.inc.php:641
Operations for retrieving and modifying objects from a database.
Definition: DAO.inc.php:29
DAO($dataSource=null, $callHooks=true)
Definition: DAO.inc.php:61
& retrieveLimit($sql, $params=false, $numRows=false, $offset=false, $callHooks=true)
Definition: DAO.inc.php:147
& retrieve($sql, $params=false, $callHooks=true)
Definition: DAO.inc.php:83
getInsertId($table= '', $id= '', $callHooks=true)
Definition: DAO.inc.php:252
updateDataObjectSettings($tableName, &$dataObject, $idArray)
Definition: DAO.inc.php:460
dateToDB($d)
Definition: DAO.inc.php:306
& retrieveRange($sql, $params=false, $dbResultRange=null, $callHooks=true)
Definition: DAO.inc.php:176
microtime()
Definition: Core.inc.php:102
Class to represent a JSON (Javascript Object Notation) message.
& getInstance($setInstance=null)
concat()
Definition: DAO.inc.php:52
datetimeFromDB($dt)
Definition: DAO.inc.php:316
getAffectedRows()
Definition: DAO.inc.php:261
& getDataSource()
Definition: DAO.inc.php:37
$_dataSource
Definition: DAO.inc.php:31
convertFromDB($value, $type)
Definition: DAO.inc.php:338
& retrieveCached($sql, $params=false, $secsToCache=3600, $callHooks=true)
Definition: DAO.inc.php:113
convertToDB($value, &$type)
Definition: DAO.inc.php:394
setCacheDir()
Definition: DAO.inc.php:271
call($hookName, $args=null)
flushCache()
Definition: DAO.inc.php:285
getDriver()
Definition: DAO.inc.php:578
replace($table, $arrFields, $keyCols)
Definition: DAO.inc.php:240
logQuery($sql, $start, $params=array())
datetimeToDB($dt)
Definition: DAO.inc.php:296
getDataChangedEvent($elementId=null, $parentElementId=null)
Definition: DAO.inc.php:612
getType($value)
Definition: DAO.inc.php:368
update($sql, $params=false, $callHooks=true, $dieOnError=true)
Definition: DAO.inc.php:211
getDirectionMapping($direction)
Definition: DAO.inc.php:588
setDataSource(&$dataSource)
Definition: DAO.inc.php:45
dateFromDB($d)
Definition: DAO.inc.php:326