wCMF  3.6
 All Classes Namespaces Files Functions Variables Groups Pages
class.RDBMapper.php
Go to the documentation of this file.
1 <?php
2 /**
3  * wCMF - wemove Content Management Framework
4  * Copyright (C) 2005-2014 wemove digital solutions GmbH
5  *
6  * Licensed under the terms of any of the following licenses
7  * at your choice:
8  *
9  * - GNU Lesser General Public License (LGPL)
10  * http://www.gnu.org/licenses/lgpl.html
11  * - Eclipse Public License (EPL)
12  * http://www.eclipse.org/org/documents/epl-v10.php
13  *
14  * See the license.txt file distributed with this work for
15  * additional information.
16  *
17  * $Id: class.RDBMapper.php 1462 2014-02-04 23:52:27Z iherwig $
18  */
19 require_once(BASE."wcmf/lib/core/class.WCMFException.php");
20 require_once(BASE."wcmf/lib/util/class.Log.php");
21 require_once(BASE."wcmf/lib/persistence/class.PersistenceMapper.php");
22 require_once(BASE."wcmf/lib/persistence/class.PersistenceFacade.php");
23 require_once(BASE."wcmf/lib/persistence/converter/class.DataConverter.php");
24 require_once(BASE."wcmf/lib/persistence/pdo/class.PDOConnection.php");
25 require_once(BASE."wcmf/lib/util/class.InifileParser.php");
26 
27 /**
28  * @class RDBMapper
29  * @ingroup Mapper
30  * @brief RDBMapper maps objects of one type to a relational database schema.
31  * It defines a persistence mechanism that specialized mappers customize by overriding
32  * the given template methods.
33  *
34  * @author ingo herwig <ingo@wemove.com>
35  */
37 {
38  var $_connParams = null; // database connection parameters
39  var $_conn = null; // database connection
40  var $_dbPrefix = ''; // database prefix (if given in the configuration file)
41 
42  // prepared statements
43  var $_idSelectStmt = null;
44  var $_idInsertStmt = null;
45  var $_idUpdateStmt = null;
46 
47  /**
48  * Constructor.
49  * @param params Initialization data given in an assoziative array with the following keys:
50  * dbType, dbHostName, dbUserName, dbPassword, dbName OR dbConnection
51  * if dbPrefix is given it will be appended to every table string, which is
52  * usefull if different cms operate on the same database
53  */
54  function RDBMapper($params)
55  {
56  // store connection parameters to allow lazy connect
57  $this->_connParams = &$params;
58  $this->_dataConverter = new DataConverter();
59  }
60  /**
61  * Select data to be stored in the session.
62  * PDO throws an excetption if tried to be (un-)serialized.
63  */
64  function __sleep()
65  {
66  unset($this->_connParams['dbConnection']);
67  return array('_connParams', '_dbPrefix');
68  }
69  /**
70  * Actually connect to the database using the parameters given to the constructor.
71  */
72  function connect()
73  {
74  if (isset($this->_connParams['dbType']) && isset($this->_connParams['dbHostName']) &&
75  isset($this->_connParams['dbUserName']) && isset($this->_connParams['dbPassword']) &&
76  isset($this->_connParams['dbName']))
77  {
78  $dns = $this->_connParams['dbType'].':host='.$this->_connParams['dbHostName'].
79  ((!empty($this->_connParams['dbPort'])) ? (';port='.$this->_connParams['dbPort']) : '').
80  ';dbname='.$this->_connParams['dbName'];
81  $charSet = isset($this->_connParams['dbCharSet']) ? $this->_connParams['dbCharSet'] : 'utf8';
82 
83  // set driver specific options
84  $driverOptions = array();
85  if (strtolower($this->_connParams['dbType']) == 'mysql') {
86  $driverOptions[PDO::MYSQL_ATTR_INIT_COMMAND] = "SET NAMES ".$charSet;
87  }
88  try {
89  // create new connection
90  $this->_conn = new PDOConnection($dns, $this->_connParams['dbUserName'], $this->_connParams['dbPassword'], $driverOptions);
91  $this->_conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
92  }
93  catch(PDOException $ex) {
94  WCMFException::throwEx("Connection to ".$this->_connParams['dbHostName'].".".$this->_connParams['dbName']." failed: ".
95  $ex->getMessage(), __FILE__, __LINE__);
96  }
97 
98  // get database prefix if defined
99  $this->_dbPrefix = $this->_connParams['dbPrefix'];
100 
101  // store connection for reuse
102  $persistenceFacade = &PersistenceFacade::getInstance();
103  $persistenceFacade->storeConnection($this->_connParams, $this->_conn);
104  }
105  elseif (isset($this->_connParams['dbConnection']))
106  {
107  // use existing connection
108  $this->_conn = &$this->_connParams['dbConnection'];
109  }
110  else
111  WCMFException::throwEx("Wrong parameters for constructor.", __FILE__, __LINE__);
112  }
113  /**
114  * Get a new id for inserting into the database
115  * @return An id value.
116  */
117  function getNextId()
118  {
119  try {
120  $id = 0;
121  if ($this->_idSelectStmt == null || $this->_idInsertStmt == null || $this->_idUpdateStmt == null) {
122  // get the connection from the sequence mapper
123  $persistenceFacade = PersistenceFacade::getInstance();
124  $mapper = $persistenceFacade->getMapper("Adodbseq");
125  if ($mapper instanceof RDBMapper) {
126  $connection = $mapper->getConnection();
127  $this->_idSelectStmt = $connection->prepare("SELECT id FROM adodbseq");
128  $this->_idInsertStmt = $connection->prepare("INSERT INTO adodbseq (id) VALUES (0)");
129  $this->_idUpdateStmt = $connection->prepare("UPDATE adodbseq SET id=LAST_INSERT_ID(id+1);");
130  }
131  }
132  $this->_idSelectStmt->execute();
133  $rows = $this->_idSelectStmt->fetchAll(PDO::FETCH_ASSOC);
134  if (sizeof($rows) == 0) {
135  $this->_idInsertStmt->execute();
136  $this->_idInsertStmt->closeCursor();
137  $row = array(array('id' => 0));
138  }
139  $id = $rows[0]['id'];
140  $this->_idUpdateStmt->execute();
141  $this->_idUpdateStmt->closeCursor();
142  $this->_idSelectStmt->closeCursor();
143  return $id;
144  }
145  catch (Exception $ex) {
146  Log::error("The query: ".$sql."\ncaused the following exception:\n".$ex->getMessage(), __CLASS__);
147  WCMFException::throwEx("Error in persistent operation. See log file for details.", __FILE__, __LINE__);
148  }
149  }
150  /**
151  * Get the name of the sequence table
152  * @return The name.
153  */
155  {
156  $persistenceFacade = &PersistenceFacade::getInstance();
157  $mapper = &$persistenceFacade->getMapper('Adodbseq');
158  // TODO: check if the mapper implements getTableName
159  return $mapper->getTableName();
160  }
161  /**
162  * Execute a query on the connection.
163  * @param sql The sql command
164  * @param isSelect True/False wether the statement is a select statement (default: false)
165  * @return If isSelect is true, an array as the result of PDOStatement::fetchAll(PDO::FETCH_ASSOC),
166  * the number of affected rows else
167  */
168  function executeSql($sql, $isSelect=false)
169  {
170  if ($this->_conn == null)
171  $this->connect();
172 
173  if (Log::isDebugEnabled(__CLASS__)) {
174  Log::debug($sql."\n".WCMFException::getStackTrace(), __CLASS__);
175  }
176  try {
177  if ($isSelect) {
178  $stmt = $this->_conn->prepare($sql);
179  $stmt->execute();
180  $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
181  $stmt->closeCursor();
182  return $result;
183  }
184  else {
185  return $this->_conn->exec($sql);
186  }
187  }
188  catch (Exception $ex) {
189  Log::error("The query: ".$sql."\ncaused the following exception:\n".$ex->getMessage(), __CLASS__);
190  WCMFException::throwEx("Error in persistent operation. See log file for details.", __FILE__, __LINE__);
191  }
192  }
193  /**
194  * Execute a select query on the connection.
195  * @param sql The sql command
196  * @param pagingInfo An PagingInfo instance describing which page to load
197  * @return An array as the result of PDOStatement::fetchAll(PDO::FETCH_ASSOC)
198  */
199  function select($sql, &$pagingInfo)
200  {
201  if ($this->_conn == null)
202  $this->connect();
203 
204  try {
205  if ($pagingInfo != null && $pagingInfo->getPageSize() > 0) {
206  // make a count query
207  $countSql = preg_replace('/^\s*SELECT\s+(DISTINCT\s+|)([^,]+)\s.+\sFROM\s/Uis', 'SELECT COUNT($1 $2) AS nRows FROM ', $sql);
208  $countSql = preg_replace('/\s*ORDER BY.*$/Uis', '', $countSql);
209  $result = $this->executeSql($countSql, true);
210  $nRows = $result[0]['nRows'];
211  // update pagingInfo
212  $pagingInfo->setTotalCount($nRows);
213  // set the limit on the query (NOTE: not supported by all databases)
214  $limit = $pagingInfo->getPageSize();
215  $offset = $pagingInfo->getIndex();
216  $sql = preg_replace('/;$/', '', $sql);
217  $sql .= ' LIMIT '.$limit;
218  if ($offset > 0) {
219  $sql .= ' OFFSET '.$offset;
220  }
221  $sql .= ';';
222  }
223  return $this->executeSql($sql, true);
224  }
225  catch (Exception $ex) {
226  Log::error("The query: ".$sql."\ncaused the following exception:\n".$ex->getMessage(), __CLASS__);
227  WCMFException::throwEx("Error in persistent operation. See log file for details.", __FILE__, __LINE__);
228  }
229  }
230  /**
231  * Check if a value is a primary key value
232  * @param name The name of the value
233  * @param dataType The datatype of the value
234  * @return True/False
235  */
236  function isPkValue($name, $dataType)
237  {
238  $pkNames = $this->getPKNames();
239  return (in_array($name, array_keys($pkNames)) && $pkNames[$name] == $dataType);
240  }
241  /**
242  * Construct an object id from given row data
243  * @param type The type of object
244  * @param data An associative array with the pk column names as keys and pk values as values
245  * @return The oid
246  */
247  function constructOID($type, $data)
248  {
249  $pkNames = $this->getPKNamesForType($type);
250  $params = array('type' => $type, 'id' => array());
251  foreach ($pkNames as $pkName)
252  array_push($params['id'], $data[$pkName]);
253  return PersistenceFacade::composeOID($params);
254 
255  }
256  /**
257  * Get the pk column names for a given type
258  * @param type The type of object
259  * @return An array of column names
260  */
261  function getPKNamesForType($type)
262  {
263  $persistenceFacade = &PersistenceFacade::getInstance();
264  $mapper = &$persistenceFacade->getMapper($type);
265  return array_keys($mapper->getPkNames());
266 
267  }
268  /**
269  * @see PersistenceMapper::loadImpl()
270  */
271  function &loadImpl($oid, $buildDepth, $buildAttribs=null, $buildTypes=null)
272  {
273  $persistenceFacade = &PersistenceFacade::getInstance();
274 
275  // delegate to loadObjects
276  $criteria = $this->createPKCondition($oid);
277  $pagingInfo = null;
278  $objects = $persistenceFacade->loadObjects(PersistenceFacade::getOIDParameter($oid, 'type'), $buildDepth, $criteria, null,
279  $pagingInfo, $buildAttribs, $buildTypes);
280  if (sizeof($objects) > 0)
281  return $objects[0];
282  else
283  return null;
284  }
285  /**
286  * @see PersistenceMapper::createImpl()
287  * @note The type parameter is not used here because this class only constructs one type
288  */
289  function &createImpl($type, $buildDepth, $buildAttribs=null)
290  {
291  if ($buildDepth < 0 && !in_array($buildDepth, array(BUILDDEPTH_INFINITE, BUILDDEPTH_SINGLE, BUILDDEPTH_REQUIRED)))
292  WCMFException::throwEx("Build depth not supported: $buildDepth", __FILE__, __LINE__);
293 
294  $persistenceFacade = &PersistenceFacade::getInstance();
295 
296  // get attributes to load
297  $attribs = null;
298  if ($buildAttribs != null && isset($buildAttribs[$this->getType()]))
299  $attribs = $buildAttribs[$this->getType()];
300 
301  // create the object
302  $object = &$this->createObjectFromData($attribs);
303 
304  // recalculate build depth for the next generation
305  if ($buildDepth != BUILDDEPTH_REQUIRED && $buildDepth != BUILDDEPTH_SINGLE && $buildDepth != BUILDDEPTH_INFINITE && $buildDepth > 0)
306  $newBuildDepth = $buildDepth-1;
307  else
308  $newBuildDepth = $buildDepth;
309 
310  // prevent infinite recursion
311  if ($buildDepth < BUILDDEPTH_MAX)
312  {
313  // get object definition (default values, properties)
314  $objectData = $this->getObjectDefinition();
315 
316  // set dependend objects of this object
317  foreach ($objectData['_children'] as $childData)
318  {
319  // set 'minOccurs', 'maxOccurs'
320  if (!isset($childData['minOccurs']))
321  $childData['minOccurs'] = 0; // default value
322  if (!isset($childData['maxOccurs']))
323  $childData['maxOccurs'] = 1; // default value
324 
325  if ( ($buildDepth != BUILDDEPTH_SINGLE) && (($buildDepth > 0) || ($buildDepth == BUILDDEPTH_INFINITE) ||
326  (($buildDepth == BUILDDEPTH_REQUIRED) && $childData['minOccurs'] > 0 && $childData['aggregation'] == true)) )
327  {
328  $childObject = &$persistenceFacade->create($childData['type'], $newBuildDepth, $buildAttribs);
329  $childObject->setProperty('minOccurs', $childData['minOccurs']);
330  $childObject->setProperty('maxOccurs', $childData['maxOccurs']);
331  $childObject->setProperty('aggregation', $childData['aggregation']);
332  $childObject->setProperty('composition', $childData['composition']);
333  $this->appendObject($object, $childObject);
334  }
335  }
336  }
337 
338  return $object;
339  }
340  /**
341  * @see PersistenceMapper::saveImpl()
342  */
343  function saveImpl(&$object)
344  {
345  if ($this->_conn == null)
346  $this->connect();
347 
348  // prepare object data
349  // convert all values (except for primary key values)
350  $appValues = array();
351  foreach ($object->getDataTypes() as $type)
352  {
353  foreach ($object->getValueNames($type) as $valueName) {
354  if (!$this->isPkValue($valueName, $type))
355  {
356  if (!isset($appValues[$type])) {
357  $appValues[$type] = array();
358  }
359  $properties = $object->getValueProperties($valueName, $type);
360  $value = $object->getValue($valueName, $type);
361  $appValues[$type][$valueName] = $value;
362  $convertedValue = $this->_dataConverter->convertApplicationToStorage($value, $properties['db_data_type'], $valueName);
363  $object->setValue($valueName, $convertedValue, $type, true);
364  }
365  }
366  }
367 
368  $persistenceFacade = &PersistenceFacade::getInstance();
369  if ($object->getState() == STATE_NEW)
370  {
371  // insert new object
372  $this->prepareInsert($object);
373  $sqlArray = $this->getInsertSQL($object);
374  foreach($sqlArray as $sqlStr)
375  $this->executeSql($sqlStr);
376 
377  // log action
378  $this->logAction($object);
379  }
380  else if ($object->getState() == STATE_DIRTY)
381  {
382  // save existing object
383  // precondition: the object exists in the database
384 
385  // log action
386  $this->logAction($object);
387 
388  // save object
389  $sqlArray = $this->getUpdateSQL($object);
390  foreach($sqlArray as $sqlStr)
391  $this->executeSql($sqlStr);
392  }
393 
394  // set converted values back to application values
395  foreach ($object->getDataTypes() as $type)
396  {
397  foreach ($object->getValueNames($type) as $valueName) {
398  if (!$this->isPkValue($valueName, $type)) {
399  $object->setValue($valueName, $appValues[$type][$valueName], $type);
400  }
401  }
402  }
403 
404  $object->setState(STATE_CLEAN, false);
405  // postcondition: the object is saved to the db
406  // the object state is STATE_CLEAN
407  // attributes are only inserted if their values differ from ''
408  return true;
409  }
410  /**
411  * @see PersistenceMapper::deleteImpl()
412  */
413  function deleteImpl($oid, $recursive=true)
414  {
415  if ($this->_conn == null)
416  $this->connect();
417 
418  $persistenceFacade = &PersistenceFacade::getInstance();
419 
420  // log action
421  if ($this->isLogging())
422  {
423  $obj = &$persistenceFacade->load($oid, BUILDDEPTH_SINGLE);
424  if ($obj)
425  $this->logAction($obj);
426  }
427 
428  // delete object
429  $sqlArray = $this->getDeleteSQL($oid);
430  foreach($sqlArray as $sqlStr)
431  $this->executeSql($sqlStr);
432 
433  // delete children
434  if ($recursive)
435  {
436  // make sure that we only delete the composition children...
437  $sqlArray = $this->getChildrenSelectSQL($oid, true);
438  foreach($sqlArray as $childType => $criteria)
439  {
440  $attribs = $this->getPkNamesForType($childType);
441  $childoids = $persistenceFacade->getOIDs($childType, $criteria);
442  foreach($childoids as $childoid)
443  $persistenceFacade->delete($childoid, $recursive);
444  }
445  // ...for the others we have to break the foreign key relation
446  $sqlArray = $this->getChildrenDisassociateSQL($oid, true);
447  foreach($sqlArray as $sqlStr)
448  $this->executeSql($sqlStr);
449  }
450  // postcondition: the object and all dependend objects are deleted from db
451  return true;
452  }
453  /**
454  * Get the database connection.
455  * @return A reference to the ADONewConnection object
456  */
457  function &getConnection()
458  {
459  if ($this->_conn == null)
460  $this->connect();
461 
462  return $this->_conn;
463  }
464  /**
465  * @see PersistenceMapper::getOIDs()
466  * @note The type parameter is not used here because this class only constructs one type
467  */
468  function getOIDs($type, $criteria=null, $orderby=null, &$pagingInfo)
469  {
470  $oids = array();
471 
472  // create query (load only pk columns and no children oids)
473  $type = $this->getType();
474  $data = $this->loadObjects($type, BUILDDEPTH_SINGLE, $criteria, $orderby, $pagingInfo, array($type => array()), array($type), false, true);
475 
476  // collect oids
477  for ($i=0, $count=sizeof($data); $i<$count; $i++) {
478  $objectData['_data'] = $data[$i];
479  $oid = $this->constructOID($this->getType(), $objectData['_data']);
480  array_push($oids, $oid);
481  }
482 
483  return $oids;
484  }
485  /**
486  * @see PersistenceFacade::loadObjects()
487  * @note The additional parameter selectChildOIDs indicates wether to load the oids of the children or not and is
488  * used internally only
489  */
490  function loadObjects($type, $buildDepth, $criteria=null, $orderby=null, &$pagingInfo, $buildAttribs=null, $buildTypes=null, $selectChildOIDs=true, $omitObjects=false)
491  {
492  if ($buildDepth < 0 && !in_array($buildDepth, array(BUILDDEPTH_INFINITE, BUILDDEPTH_SINGLE)))
493  WCMFException::throwEx("Build depth not supported: $buildDepth", __FILE__, __LINE__);
494 
495  if ($this->_conn == null)
496  $this->connect();
497 
498  $objects = array();
499 
500  // check buildTypes
501  if (is_array($buildTypes) && !in_array($type, $buildTypes))
502  return $objects;
503 
504  // get attributes to load
505  $attribs = null;
506  if ($buildAttribs != null && isset($buildAttribs[$this->getType()]))
507  $attribs = $buildAttribs[$this->getType()];
508 
509  // create condition
510  $attribCondStr = "";
511  if ($criteria != null)
512  {
513  // criteria is an array
514  if (is_array($criteria))
515  {
516  foreach($criteria as $name => $value)
517  $attribCondStr .= $name."=".$this->_conn->quote($value)." AND ";
518  $attribCondStr = substr($attribCondStr, 0, strlen($attribCondStr)-strlen(" AND "));
519  }
520  else
521  $attribCondStr = $criteria;
522  }
523 
524  // create order
525  $orderbyStr = "";
526  if ($orderby != null)
527  $orderbyStr = join(', ', $orderby);
528 
529  // create query
530  $data = array();
531  $sqlStr = $this->getSelectSQL($attribCondStr, $orderbyStr, $attribs);
532  $data = $this->select($sqlStr, $pagingInfo);
533  if (sizeof($data) == 0)
534  return $objects;
535 
536  if ($omitObjects) {
537  return $data;
538  }
539 
540  $numObjects = sizeof($data);
541  for ($i=0; $i<$numObjects; $i++)
542  {
543  // create the object
544  $object = &$this->createObjectFromData($attribs, $data[$i]);
545 
546  // append child data (childoids or real children depending on the buildDepth)
547  if ($selectChildOIDs)
548  $this->appendChildData($object, $buildDepth, $buildAttribs, $buildTypes);
549 
550  $object->setState(STATE_CLEAN, false);
551  $objects[] = &$object;
552  }
553  return $objects;
554  }
555  /**
556  * Create an object of the mapper's type with the given attributes from the given data
557  * @param attribs An array of attributes to create
558  * @param data The '_data' array contained in the array returned by getObjectDefinition @see RDBMapper::applyDataOnLoad()
559  * @return A reference to the object
560  */
561  function &createObjectFromData($attribs, $data=null)
562  {
563  // determin if we are loading or creating
564  $createFromLoadedData = false;
565  if (is_array($data))
566  $createFromLoadedData = true;
567 
568  // get object definition (default values, properties)
569  $objectData = $this->getObjectDefinition();
570 
571  // initialize data and oid
572  if ($createFromLoadedData)
573  {
574  // fill the given data into the definition
575  $objectData['_data'] = $data;
576  $oid = $this->constructOID($this->getType(), $objectData['_data']);
577  }
578  else
579  $oid = null;
580 
581  // construct object
582  $object = &$this->createObject($oid);
583 
584  // set object properties
585  foreach($objectData['_properties'] as $property)
586  $object->setProperty($property['name'], $property['value']);
587 
588  // apply data to the created object
589  if ($createFromLoadedData)
590  $this->applyDataOnLoad($object, $objectData, $attribs);
591  else
592  $this->applyDataOnCreate($object, $objectData, $attribs);
593 
594  return $object;
595  }
596  /**
597  * Append the child data to an object. If the buildDepth does not determine to load a
598  * child generation, only the oids of the children will be loaded.
599  * @param object A reference to the object to append the children to
600  * @param buildDepth @see PersistenceFacade::loadObjects()
601  * @param buildAttribs @see PersistenceFacade::loadObjects()
602  * @param buildTypes @see PersistenceFacade::loadObjects()
603  */
604  function appendChildData(&$object, $buildDepth, $buildAttribs=null, $buildTypes=null)
605  {
606  $persistenceFacade = &PersistenceFacade::getInstance();
607 
608  // recalculate build depth for the next generation
609  if ($buildDepth != BUILDDEPTH_SINGLE && $buildDepth != BUILDDEPTH_INFINITE && $buildDepth > 0)
610  $newBuildDepth = $buildDepth-1;
611  else
612  $newBuildDepth = $buildDepth;
613 
614  // get dependend objects of this object
615  $childoids = array();
616  $sqlArray = $this->getChildrenSelectSQL($object->getOID());
617  foreach($sqlArray as $childType => $childCriteria)
618  {
619  $childoids = array_merge($childoids, $persistenceFacade->getOIDs($childType, $childCriteria));
620 
621  // load dependend objects if build depth is not satisfied already
622  if (($buildDepth != BUILDDEPTH_SINGLE) && ($buildDepth > 0 || $buildDepth == BUILDDEPTH_INFINITE))
623  {
624  $childPagingInfo = null;
625  $children = $persistenceFacade->loadObjects($childType, $newBuildDepth, $childCriteria, null, $childPagingInfo, $buildAttribs, $buildTypes);
626  for ($j=0; $j<sizeof($children); $j++)
627  $this->appendObject($object, $children[$j]);
628  }
629  $object->setProperty('childoids', $childoids);
630  }
631  }
632  /**
633  * @see PersistenceMapper::startTransaction()
634  */
635  function startTransaction()
636  {
637  if ($this->_conn == null)
638  $this->connect();
639 
640  $this->_conn->beginTransaction();
641  }
642  /**
643  * @see PersistenceMapper::commitTransaction()
644  */
645  function commitTransaction()
646  {
647  if ($this->_conn == null)
648  $this->connect();
649 
650  $this->_conn->commit();
651  }
652  /**
653  * @see PersistenceMapper::rollbackTransaction()
654  * @note Rollbacks have to be supported by the database.
655  */
657  {
658  if ($this->_conn == null)
659  $this->connect();
660 
661  $this->_conn->rollBack();
662  }
663 
664  /**
665  * TEMPLATE METHODS
666  * Subclasses must implement this method to define their object type.
667  */
668 
669  /**
670  * Factory method for the supported object type.
671  * @note Subclasses must implement this method to define their object type.
672  * @param oid The object id (maybe null)
673  * @return A reference to the created object.
674  */
675  function &createObject($oid=null)
676  {
677  WCMFException::throwEx("createObject() must be implemented by derived class: ".get_class($this), __FILE__, __LINE__);
678  }
679  /**
680  * Add a dependend object (child) to an object.
681  * @note Subclasses must implement this method to define their object type.
682  * @param object The object to add to.
683  * @param dependendObject The object to add.
684  */
685  function appendObject(&$object, &$dependendObject)
686  {
687  WCMFException::throwEx("appendObject() must be implemented by derived class: ".get_class($this), __FILE__, __LINE__);
688  }
689  /**
690  * Apply the loaded object data to the object.
691  * @note Subclasses must implement this method to define their object type.
692  * @param object A reference to the object created with createObject method to which the data should be applied
693  * @param objectData The array returned by the getObjectDefinition method with the '_data' array filled with
694  * the rows returned by execution of the database select statement (given by getSelectSQL). These
695  * rows hold the loaded data.
696  * @param attribs The build attributes for the type of object (given in the buildAttribs parameter of the loadImpl method)
697  */
698  function applyDataOnLoad(&$object, $objectData, $attribs)
699  {
700  WCMFException::throwEx("applyDataOnLoad() must be implemented by derived class: ".get_class($this), __FILE__, __LINE__);
701  }
702  /**
703  * Apply the default data to the object.
704  * @note Subclasses must implement this method to define their object type.
705  * @param object A reference to the object created with createObject method to which the data should be applied
706  * @param objectData The array returned by the getObjectDefinition method. The default data are extracted from this.
707  * @param attribs The build attributes for the type of object (given in the buildAttribs parameter of the loadImpl method).
708  */
709  function applyDataOnCreate(&$object, $objectData, $attribs)
710  {
711  WCMFException::throwEx("applyDataOnCreate() must be implemented by derived class: ".get_class($this), __FILE__, __LINE__);
712  }
713  /**
714  * Set the object primary key values for inserting the object to the database.
715  * @note Subclasses must implement this method to define their object type.
716  * @param object A reference to the object to insert.
717  * @note The object does not have the final object id set. If a new id value for a primary key column is needed
718  * for the insert statement, use RDBMapper::getNextId().
719  */
720  function prepareInsert(&$object)
721  {
722  WCMFException::throwEx("prepareInsert() must be implemented by derived class: ".get_class($this), __FILE__, __LINE__);
723  }
724  /**
725  * Get the object type this mapper supports.
726  * @note Subclasses must implement this method to define their object type.
727  * @return The name of the supported object type.
728  */
729  function getType()
730  {
731  WCMFException::throwEx("getType() must be implemented by derived class: ".get_class($this), __FILE__, __LINE__);
732  }
733  /**
734  * Get the object type definition.
735  * @note Subclasses must implement this method to define their object type.
736  * @return An assoziative array with unchangeable keys '_properties', '_datadef', '_children', '_data' plus application specific keys.
737  * The predefined keys hold the following structures:
738  *
739  * - @em _properties: An array of assoziative arrays with the keys 'name', 'value' for every property
740  * (e.g. array('name' => 'display_value', 'value' => 'name'))
741  * - @em _datadef: The structure of this key must be defined in the subclasses!
742  * - @em _parents: An array of assoziative arrays with the key 'type' for every parent
743  * (e.g. array('type' => 'author'))
744  * - @em _children: An array of assoziative arrays with the keys 'type', 'minOccurs', 'maxOccurs', 'aggregation', 'composition' for every child
745  * (e.g. array('type' => 'textblock', 'minOccurs' => 0, 'maxOccurs' => 'unbounded', 'aggregation' => true, 'composition' => false))
746  * - @em _data: An assoziative array where the keys are the data item names defined in the @em _datadef array
747  * (e.g. array('title' => 'Hello User!'))
748  * @note The @em _data array will be overridden with data provided by the db select. No need for definition at this point!
749  */
751  {
752  WCMFException::throwEx("getObjectDefinition() must be implemented by derived class: ".get_class($this), __FILE__, __LINE__);
753  }
754  /**
755  * Get the SQL command to select object data from the database.
756  * @note Subclasses must implement this method to define their object type.
757  * @param condStr The condition string (without 'WHERE').
758  * @param orderStr The order string (without 'ORDER BY') (default: null uses default order).
759  * @param attribs An array listing the attributes to load (default: null loads all attributes).
760  * @param asArray True to get an associative array with keys 'attributeStr', 'tableStr', 'conditionStr', 'orderStr'
761  * and the appropriate query parts as value. [default: false]
762  * @return A SQL command that selects all object data that match the condition or an array with the query parts.
763  * Additionally columns 'ptype0' ('ptype1', ...) and 'pid0' ('pid1', ...) that define the objects parent are expected.
764  * @note The names of the data item columns MUST match the data item names provided in the '_datadef' array from RDBMapper::getObjectDefinition()
765  * Use alias names if not! The selected data will be put into the '_data' array of the object definition.
766  */
767  function getSelectSQL($condStr, $orderStr=null, $attribs=null, $asArray=false)
768  {
769  WCMFException::throwEx("getSelectSQL() must be implemented by derived class: ".get_class($this), __FILE__, __LINE__);
770  }
771  /**
772  * Get the SQL commands to select the objects children from the database.
773  * @note Subclasses must implement this method to define their object type.
774  * @param oid The object id of the object to load the children for.
775  * @param compositionOnly True/False indicates wether to only select composition children or all [default: false].
776  * @return An associative array with the children types as keys and the corresponding SQL conditions to be used as criteria parameter
777  * in PersistenceFacade::loadObjects().
778  * @note We need to return an array because MySql doesn't support unions prior to version 4
779  */
780  function getChildrenSelectSQL($oid, $compositionOnly=false)
781  {
782  WCMFException::throwEx("getChildrenSelectSQL() must be implemented by derived class: ".get_class($this), __FILE__, __LINE__);
783  }
784  /**
785  * Get the SQL command to disassociate the objects children from the object (e.g. setting the foreign key to null).
786  * @note Subclasses must implement this method to define their object type.
787  * @param oid The object id of the object to disassociate the children from.
788  * @param sharedOnly True/False indicates wether to only disassociate shared children or all [default: false].
789  * @return An array of SQL commands that disassociate all object children.
790  */
791  function getChildrenDisassociateSQL($oid, $sharedOnly=false)
792  {
793  WCMFException::throwEx("getChildrenDisassociateSQL() must be implemented by derived class: ".get_class($this), __FILE__, __LINE__);
794  }
795  /**
796  * Get the SQL command to insert a object into the database.
797  * @note Subclasses must implement this method to define their object type.
798  * @param object A reference to the object to insert.
799  * @return An array of SQL commands that insert a new object.
800  */
801  function getInsertSQL(&$object)
802  {
803  WCMFException::throwEx("getInsertSQL() must be implemented by derived class: ".get_class($this), __FILE__, __LINE__);
804  }
805  /**
806  * Get the SQL command to update a object in the database.
807  * @note Subclasses must implement this method to define their object type.
808  * @param object A reference to the object to update.
809  * @return An array of SQL commands that update an existing object.
810  */
811  function getUpdateSQL(&$object)
812  {
813  WCMFException::throwEx("getUpdateSQL() must be implemented by derived class: ".get_class($this), __FILE__, __LINE__);
814  }
815  /**
816  * Get the SQL command to delete a object from the database.
817  * @note Subclasses must implement this method to define their object type.
818  * @param oid The object id of the object to delete.
819  * @return An array of SQL commands that delete an existing object.
820  */
821  function getDeleteSQL($oid)
822  {
823  WCMFException::throwEx("getDeleteSQL() must be implemented by derived class: ".get_class($this), __FILE__, __LINE__);
824  }
825  /**
826  * Create a condition string for the primary key values of the form id1=val1, id2=val2, ...
827  * @note Subclasses must implement this method to define their object type.
828  * @param oid The object id that defines the primary key values
829  * @return The string
830  */
831  function createPKCondition($oid)
832  {
833  WCMFException::throwEx("createPKCondition() must be implemented by derived class: ".get_class($this), __FILE__, __LINE__);
834  }
835 }
836 ?>
RDBMapper maps objects of one type to a relational database schema. It defines a persistence mechanis...
const STATE_DIRTY
error($message, $category)
Definition: class.Log.php:69
constructOID($type, $data)
debug($message, $category)
Definition: class.Log.php:39
getDeleteSQL($oid)
saveImpl(&$object)
RDBMapper($params)
getPKNamesForType($type)
appendObject(&$object, &$dependendObject)
getInsertSQL(&$object)
prepareInsert(&$object)
executeSql($sql, $isSelect=false)
deleteImpl($oid, $recursive=true)
throwEx($message, $file='', $line='')
appendChildData(&$object, $buildDepth, $buildAttribs=null, $buildTypes=null)
const STATE_CLEAN
applyDataOnCreate(&$object, $objectData, $attribs)
getChildrenSelectSQL($oid, $compositionOnly=false)
getUpdateSQL(&$object)
const BUILDDEPTH_INFINITE
& createObject($oid=null)
& createObjectFromData($attribs, $data=null)
applyDataOnLoad(&$object, $objectData, $attribs)
const STATE_NEW
const BUILDDEPTH_MAX
isPkValue($name, $dataType)
& createImpl($type, $buildDepth, $buildAttribs=null)
select($sql, &$pagingInfo)
isDebugEnabled($category)
Definition: class.Log.php:89
PDOConnection extends PDO.
const BUILDDEPTH_REQUIRED
DataConverter is the base class for all converter classes. It defines the interface for converting da...
loadObjects($type, $buildDepth, $criteria=null, $orderby=null, &$pagingInfo, $buildAttribs=null, $buildTypes=null, $selectChildOIDs=true, $omitObjects=false)
getOIDParameter($oid, $param, $validate=true)
& loadImpl($oid, $buildDepth, $buildAttribs=null, $buildTypes=null)
PersistenceMapper is the base class for all mapper classes.
getOIDs($type, $criteria=null, $orderby=null, &$pagingInfo)
createPKCondition($oid)
const BUILDDEPTH_SINGLE
getChildrenDisassociateSQL($oid, $sharedOnly=false)
getSelectSQL($condStr, $orderStr=null, $attribs=null, $asArray=false)