Open Journal Systems  3.0.0
 All Classes Namespaces Functions Variables Groups Pages
DAO.inc.php
1 <?php
2 
23 import('lib.pkp.classes.db.DBConnection');
24 import('lib.pkp.classes.db.DAOResultFactory');
25 import('lib.pkp.classes.core.DataObject');
26 
27 define('SORT_DIRECTION_ASC', 0x00001);
28 define('SORT_DIRECTION_DESC', 0x00002);
29 
30 class DAO {
33 
38  function &getDataSource() {
39  return $this->_dataSource;
40  }
41 
46  function setDataSource(&$dataSource) {
47  $this->_dataSource =& $dataSource;
48  }
49 
53  function concat() {
54  $args = func_get_args();
55  return call_user_func_array(array($this->getDataSource(), 'Concat'), $args);
56  }
57 
62  function DAO($dataSource = null, $callHooks = true) {
63  if ($callHooks === true) {
64  // Call hooks based on the object name. Results
65  // in hook calls named e.g. "sessiondao::_Constructor"
66  if (HookRegistry::call(strtolower_codesafe(get_class($this)) . '::_Constructor', array($this, &$dataSource))) {
67  return;
68  }
69  }
70 
71  if (!isset($dataSource)) {
73  } else {
74  $this->setDataSource($dataSource);
75  }
76  }
77 
84  function &retrieve($sql, $params = false, $callHooks = true) {
85  if ($callHooks === true) {
86  $trace = debug_backtrace();
87  // Call hooks based on the calling entity, assuming
88  // this method is only called by a subclass. Results
89  // in hook calls named e.g. "sessiondao::_getsession"
90  // (always lower case).
91  $value = null;
92  if (HookRegistry::call(strtolower_codesafe($trace[1]['class'] . '::_' . $trace[1]['function']), array(&$sql, &$params, &$value))) {
93  return $value;
94  }
95  }
96 
97  $start = Core::microtime();
98  $dataSource = $this->getDataSource();
99  $result = $dataSource->execute($sql, $params !== false && !is_array($params) ? array($params) : $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) {
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  if ($dataSource->errorNo()) {
132  // FIXME Handle errors more elegantly.
133  fatalError('DB Error: ' . $dataSource->errorMsg());
134  }
135  return $result;
136  }
137 
146  function &retrieveLimit($sql, $params = false, $numRows = false, $offset = false, $callHooks = true) {
147  if ($callHooks === true) {
148  $trace = debug_backtrace();
149  // Call hooks based on the calling entity, assuming
150  // this method is only called by a subclass. Results
151  // in hook calls named e.g. "sessiondao::_getsession"
152  // (all lowercase).
153  $value = null;
154  if (HookRegistry::call(strtolower_codesafe($trace[1]['class'] . '::_' . $trace[1]['function']), array(&$sql, &$params, &$numRows, &$offset, &$value))) {
155  return $value;
156  }
157  }
158 
159  $start = Core::microtime();
160  $dataSource = $this->getDataSource();
161  $result = $dataSource->selectLimit($sql, $numRows === false ? -1 : $numRows, $offset === false ? -1 : $offset, $params !== false && !is_array($params) ? array($params) : $params);
162  if ($dataSource->errorNo()) {
163  fatalError('DB Error: ' . $dataSource->errorMsg());
164  }
165  return $result;
166  }
167 
174  function &retrieveRange($sql, $params = false, $dbResultRange = null, $callHooks = true) {
175  if ($callHooks === true) {
176  $trace = debug_backtrace();
177  // Call hooks based on the calling entity, assuming
178  // this method is only called by a subclass. Results
179  // in hook calls named e.g. "sessiondao::_getsession"
180  $value = null;
181  if (HookRegistry::call(strtolower_codesafe($trace[1]['class'] . '::_' . $trace[1]['function']), array(&$sql, &$params, &$dbResultRange, &$value))) {
182  return $value;
183  }
184  }
185 
186  if (isset($dbResultRange) && $dbResultRange->isValid()) {
187  $start = Core::microtime();
188  $dataSource = $this->getDataSource();
189  $result = $dataSource->PageExecute($sql, $dbResultRange->getCount(), $dbResultRange->getPage(), $params);
190  if ($dataSource->errorNo()) {
191  fatalError('DB Error: ' . $dataSource->errorMsg());
192  }
193  }
194  else {
195  $result = $this->retrieve($sql, $params, false);
196  }
197  return $result;
198  }
199 
208  function update($sql, $params = false, $callHooks = true, $dieOnError = true) {
209  if ($callHooks === true) {
210  $trace = debug_backtrace();
211  // Call hooks based on the calling entity, assuming
212  // this method is only called by a subclass. Results
213  // in hook calls named e.g. "sessiondao::_updateobject"
214  // (all lowercase)
215  $value = null;
216  if (HookRegistry::call(strtolower_codesafe($trace[1]['class'] . '::_' . $trace[1]['function']), array(&$sql, &$params, &$value))) {
217  return $value;
218  }
219  }
220 
221  $start = Core::microtime();
222  $dataSource = $this->getDataSource();
223  $dataSource->execute($sql, $params !== false && !is_array($params) ? array($params) : $params);
224  if ($dieOnError && $dataSource->errorNo()) {
225  fatalError('DB Error: ' . $dataSource->errorMsg());
226  }
227  return $dataSource->errorNo() == 0 ? true : false;
228  }
229 
237  function replace($table, $arrFields, $keyCols) {
238  $dataSource = $this->getDataSource();
239  $arrFields = array_map(array($dataSource, 'qstr'), $arrFields);
240  return $dataSource->Replace($table, $arrFields, $keyCols, false);
241  }
242 
249  protected function _getInsertId($table = '', $id = '') {
250  $dataSource = $this->getDataSource();
251  return $dataSource->po_insert_id($table, $id);
252  }
253 
258  function getAffectedRows() {
259  $dataSource = $this->getDataSource();
260  return $dataSource->Affected_Rows();
261  }
262 
268  function setCacheDir() {
269  static $cacheDir;
270  if (!isset($cacheDir)) {
271  global $ADODB_CACHE_DIR;
272 
273  $cacheDir = CacheManager::getFileCachePath() . '/_db';
274 
275  $ADODB_CACHE_DIR = $cacheDir;
276  }
277  }
278 
282  function flushCache() {
283  $this->setCacheDir();
284  $dataSource = $this->getDataSource();
285  $dataSource->CacheFlush();
286  }
287 
293  function datetimeToDB($dt) {
294  $dataSource = $this->getDataSource();
295  return $dataSource->DBTimeStamp($dt);
296  }
297 
303  function dateToDB($d) {
304  $dataSource = $this->getDataSource();
305  return $dataSource->DBDate($d);
306  }
307 
313  function datetimeFromDB($dt) {
314  if ($dt === null) return null;
315  $dataSource = $this->getDataSource();
316  return $dataSource->UserTimeStamp($dt, 'Y-m-d H:i:s');
317  }
323  function dateFromDB($d) {
324  if ($d === null) return null;
325  $dataSource = $this->getDataSource();
326  return $dataSource->UserDate($d, 'Y-m-d');
327  }
328 
335  function convertFromDB($value, $type) {
336  switch ($type) {
337  case 'bool':
338  $value = (bool) $value;
339  break;
340  case 'int':
341  $value = (int) $value;
342  break;
343  case 'float':
344  $value = (float) $value;
345  break;
346  case 'object':
347  $value = unserialize($value);
348  break;
349  case 'date':
350  if ($value !== null) $value = strtotime($value);
351  break;
352  case 'string':
353  default:
354  // Nothing required.
355  break;
356  }
357  return $value;
358  }
359 
365  function getType($value) {
366  switch (gettype($value)) {
367  case 'boolean':
368  case 'bool':
369  return 'bool';
370  case 'integer':
371  case 'int':
372  return 'int';
373  case 'double':
374  case 'float':
375  return 'float';
376  case 'array':
377  case 'object':
378  return 'object';
379  case 'string':
380  default:
381  return 'string';
382  }
383  }
384 
391  function convertToDB($value, &$type) {
392  if ($type == null) {
393  $type = $this->getType($value);
394  }
395 
396  switch ($type) {
397  case 'object':
398  $value = serialize($value);
399  break;
400  case 'bool':
401  // Cast to boolean, ensuring that string
402  // "false" evaluates to boolean false
403  $value = ($value && $value !== 'false') ? 1 : 0;
404  break;
405  case 'int':
406  $value = (int) $value;
407  break;
408  case 'float':
409  $value = (float) $value;
410  break;
411  case 'date':
412  if ($value !== null) {
413  if (!is_numeric($value)) $value = strtotime($value);
414  $value = strftime('%Y-%m-%d %H:%M:%S', $value);
415  }
416  break;
417  case 'string':
418  default:
419  // do nothing.
420  }
421 
422  return $value;
423  }
424 
430  function nullOrInt($value) {
431  return (empty($value)?null:(int) $value);
432  }
433 
441  $returner = array();
442  // Call hooks based on the calling entity, assuming
443  // this method is only called by a subclass. Results
444  // in hook calls named e.g. "sessiondao::getAdditionalFieldNames"
445  // (class names lowercase)
446  HookRegistry::call(strtolower_codesafe(get_class($this)) . '::getAdditionalFieldNames', array($this, &$returner));
447 
448  return $returner;
449  }
450 
457  function getLocaleFieldNames() {
458  $returner = array();
459  // Call hooks based on the calling entity, assuming
460  // this method is only called by a subclass. Results
461  // in hook calls named e.g. "sessiondao::getLocaleFieldNames"
462  // (class names lowercase)
463  HookRegistry::call(strtolower_codesafe(get_class($this)) . '::getLocaleFieldNames', array($this, &$returner));
464 
465  return $returner;
466  }
467 
474  function updateDataObjectSettings($tableName, &$dataObject, $idArray) {
475  // Initialize variables
476  $idFields = array_keys($idArray);
477  $idFields[] = 'locale';
478  $idFields[] = 'setting_name';
479 
480  // Build a data structure that we can process efficiently.
481  $translated = $metadata = 1;
482  $settings = !$metadata;
483  $settingFields = array(
484  // Translated data
485  $translated => array(
486  $settings => $this->getLocaleFieldNames(),
487  $metadata => $dataObject->getLocaleMetadataFieldNames()
488  ),
489  // Shared data
490  !$translated => array(
491  $settings => $this->getAdditionalFieldNames(),
492  $metadata => $dataObject->getAdditionalMetadataFieldNames()
493  )
494  );
495 
496  // Loop over all fields and update them in the settings table
497  $updateArray = $idArray;
498  $noLocale = 0;
499  $staleMetadataSettings = array();
500  foreach ($settingFields as $isTranslated => $fieldTypes) {
501  foreach ($fieldTypes as $isMetadata => $fieldNames) {
502  foreach ($fieldNames as $fieldName) {
503  // Now we have the following control data:
504  // - $isTranslated: true for translated data, false data shared between locales
505  // - $isMetadata: true for metadata fields, false for normal settings
506  // - $fieldName: the field in the data object to be updated
507  if ($dataObject->hasData($fieldName)) {
508  if ($isTranslated) {
509  // Translated data comes in as an array
510  // with the locale as the key.
511  $values = $dataObject->getData($fieldName);
512  if (!is_array($values)) {
513  // Inconsistent data: should have been an array
514  assert(false);
515  continue;
516  }
517  } else {
518  // Transform shared data into an array so that
519  // we can handle them the same way as translated data.
520  $values = array(
521  $noLocale => $dataObject->getData($fieldName)
522  );
523  }
524 
525  // Loop over the values and update them in the database
526  foreach ($values as $locale => $value) {
527  $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
528  $updateArray['setting_name'] = $fieldName;
529  $updateArray['setting_type'] = null;
530  // Convert the data value and implicitly set the setting type.
531  $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
532  $this->replace($tableName, $updateArray, $idFields);
533  }
534  } else {
535  // Meta-data fields are maintained "sparsly". Only set fields will be
536  // recorded in the settings table. Fields that are not explicity set
537  // in the data object will be deleted.
538  if ($isMetadata) $staleMetadataSettings[] = $fieldName;
539  }
540  }
541  }
542  }
543 
544  // Remove stale meta-data
545  if (count($staleMetadataSettings)) {
546  $removeWhere = '';
547  $removeParams = array();
548  foreach ($idArray as $idField => $idValue) {
549  if (!empty($removeWhere)) $removeWhere .= ' AND ';
550  $removeWhere .= $idField.' = ?';
551  $removeParams[] = $idValue;
552  }
553  $removeWhere .= rtrim(' AND setting_name IN ( '.str_repeat('? ,', count($staleMetadataSettings)), ',').')';
554  $removeParams = array_merge($removeParams, $staleMetadataSettings);
555  $removeSql = 'DELETE FROM '.$tableName.' WHERE '.$removeWhere;
556  $this->update($removeSql, $removeParams);
557  }
558  }
559 
567  function getDataObjectSettings($tableName, $idFieldName, $idFieldValue, &$dataObject) {
568  if ($idFieldName !== null) {
569  $sql = "SELECT * FROM $tableName WHERE $idFieldName = ?";
570  $params = array($idFieldValue);
571  } else {
572  $sql = "SELECT * FROM $tableName";
573  $params = false;
574  }
575  $result = $this->retrieve($sql, $params);
576 
577  while (!$result->EOF) {
578  $row = $result->getRowAssoc(false);
579  $dataObject->setData(
580  $row['setting_name'],
581  $this->convertFromDB(
582  $row['setting_value'],
583  $row['setting_type']
584  ),
585  empty($row['locale'])?null:$row['locale']
586  );
587  $result->MoveNext();
588  }
589  $result->Close();
590  }
591 
596  function getDriver() {
597  $conn =& DBConnection::getInstance();
598  return $conn->getDriver();
599  }
600 
606  function getDirectionMapping($direction) {
607  switch ($direction) {
608  case SORT_DIRECTION_ASC:
609  return 'ASC';
610  case SORT_DIRECTION_DESC:
611  return 'DESC';
612  default:
613  return 'ASC';
614  }
615  }
616 
632  static function getDataChangedEvent($elementId = null, $parentElementId = null, $content = '') {
633  // Create the event data.
634  $eventData = null;
635  if ($elementId) {
636  $eventData = array($elementId);
637  if ($parentElementId) {
638  $eventData['parentElementId'] = $parentElementId;
639  }
640  }
641 
642  // Create and render the JSON message with the
643  // event to be triggered on the client side.
644  import('lib.pkp.classes.core.JSONMessage');
645  $json = new JSONMessage(true, $content);
646  $json->setEvent('dataChanged', $eventData);
647  return $json->getString();
648  }
649 
661  function formatDateToDB($date, $defaultNumWeeks = null, $acceptPastDate = true) {
662  $today = getDate();
663  $todayTimestamp = mktime(0, 0, 0, $today['mon'], $today['mday'], $today['year']);
664  if ($date != null) {
665  $dueDateParts = explode('-', $date);
666 
667  // If we don't accept past dates...
668  if (!$acceptPastDate && $todayTimestamp > strtotime($date)) {
669  // ... return today.
670  return date('Y-m-d H:i:s', $todayTimestamp);
671  } else {
672  // Return the passed date.
673  return date('Y-m-d H:i:s', mktime(0, 0, 0, $dueDateParts[1], $dueDateParts[2], $dueDateParts[0]));
674  }
675  } elseif (isset($defaultNumWeeks)) {
676  // Add the equivalent of $numWeeks weeks, measured in seconds, to $todaysTimestamp.
677  $numWeeks = max((int) $defaultNumWeeks, 2);
678  $newDueDateTimestamp = $todayTimestamp + ($numWeeks * 7 * 24 * 60 * 60);
679  return date('Y-m-d H:i:s', $newDueDateTimestamp);
680  } else {
681  // Either the date or the defaultNumWeeks must be set
682  assert(false);
683  return null;
684  }
685  }
686 }
687 
688 ?>
formatDateToDB($date, $defaultNumWeeks=null, $acceptPastDate=true)
Definition: DAO.inc.php:661
Operations for retrieving and modifying objects from a database.
Definition: DAO.inc.php:30
getLocaleFieldNames()
Definition: DAO.inc.php:457
DAO($dataSource=null, $callHooks=true)
Definition: DAO.inc.php:62
& retrieveLimit($sql, $params=false, $numRows=false, $offset=false, $callHooks=true)
Definition: DAO.inc.php:146
& retrieve($sql, $params=false, $callHooks=true)
Definition: DAO.inc.php:84
static microtime()
Definition: Core.inc.php:103
updateDataObjectSettings($tableName, &$dataObject, $idArray)
Definition: DAO.inc.php:474
dateToDB($d)
Definition: DAO.inc.php:303
& retrieveRange($sql, $params=false, $dbResultRange=null, $callHooks=true)
Definition: DAO.inc.php:174
Class to represent a JSON (Javascript Object Notation) message.
_getInsertId($table= '', $id= '')
Definition: DAO.inc.php:249
static & getConn()
nullOrInt($value)
Definition: DAO.inc.php:430
concat()
Definition: DAO.inc.php:53
datetimeFromDB($dt)
Definition: DAO.inc.php:313
getAffectedRows()
Definition: DAO.inc.php:258
static call($hookName, $args=null)
static getInstance($setInstance=null)
getAdditionalFieldNames()
Definition: DAO.inc.php:440
& getDataSource()
Definition: DAO.inc.php:38
$_dataSource
Definition: DAO.inc.php:32
convertFromDB($value, $type)
Definition: DAO.inc.php:335
& retrieveCached($sql, $params=false, $secsToCache=3600, $callHooks=true)
Definition: DAO.inc.php:113
convertToDB($value, &$type)
Definition: DAO.inc.php:391
setCacheDir()
Definition: DAO.inc.php:268
flushCache()
Definition: DAO.inc.php:282
getDriver()
Definition: DAO.inc.php:596
getDataObjectSettings($tableName, $idFieldName, $idFieldValue, &$dataObject)
Definition: DAO.inc.php:567
replace($table, $arrFields, $keyCols)
Definition: DAO.inc.php:237
datetimeToDB($dt)
Definition: DAO.inc.php:293
getType($value)
Definition: DAO.inc.php:365
update($sql, $params=false, $callHooks=true, $dieOnError=true)
Definition: DAO.inc.php:208
getDirectionMapping($direction)
Definition: DAO.inc.php:606
setDataSource(&$dataSource)
Definition: DAO.inc.php:46
static getDataChangedEvent($elementId=null, $parentElementId=null, $content= '')
Definition: DAO.inc.php:632
dateFromDB($d)
Definition: DAO.inc.php:323