RDBMapper.php
1 <?php
2 /**
3  * wCMF - wemove Content Management Framework
4  * Copyright (C) 2005-2015 wemove digital solutions GmbH
5  *
6  * Licensed under the terms of the MIT License.
7  *
8  * See the LICENSE file distributed with this work for
9  * additional information.
10  */
11 namespace wcmf\lib\model\mapper;
12 
13 use PDO;
40 use Zend_Db;
41 
42 /**
43  * RDBMapper maps objects of one type to a relational database schema.
44  * It defines a persistence mechanism that specialized mappers customize by overriding
45  * the given template methods.
46  *
47  * @author ingo herwig <ingo@wemove.com>
48  */
49 abstract class RDBMapper extends AbstractMapper implements PersistenceMapper {
50 
51  private static $SEQUENCE_CLASS = 'DBSequence';
52  private static $_connections = array(); // registry for connections, key: connId
53  private static $_inTransaction = array(); // registry for transaction status (boolean), key: connId
54  private static $_isDebugEnabled = false;
55  private static $_logger = null;
56 
57  private $_connectionParams = null; // database connection parameters
58  private $_connId = null; // a connection identifier composed of the connection parameters
59  private $_conn = null; // database connection
60  private $_dbPrefix = ''; // database prefix (if given in the configuration file)
61 
62  private $_relations = null;
63  private $_attributes = null;
64 
65  // prepared statements
66  private $_idSelectStmt = null;
67  private $_idInsertStmt = null;
68  private $_idUpdateStmt = null;
69 
70  // keeps track of currently loading relations to avoid circular loading
71  private $_loadingRelations = array();
72 
73  const INTERNAL_VALUE_PREFIX = '_mapper_internal_';
74 
75  /**
76  * Constructor
77  * @param $persistenceFacade
78  * @param $permissionManager
79  * @param $concurrencyManager
80  * @param $eventManager
81  * @param $message
82  */
83  public function __construct(PersistenceFacade $persistenceFacade,
84  PermissionManager $permissionManager,
85  ConcurrencyManager $concurrencyManager,
86  EventManager $eventManager,
87  Message $message) {
88  parent::__construct($persistenceFacade, $permissionManager,
89  $concurrencyManager, $eventManager, $message);
90  if (self::$_logger == null) {
91  self::$_logger = LogManager::getLogger(__CLASS__);
92  }
93  self::$_isDebugEnabled = self::$_logger->isDebugEnabled();
94  }
95 
96  /**
97  * Select data to be stored in the session.
98  * PDO throws an excetption if tried to be (un-)serialized.
99  */
100  public function __sleep() {
101  return array('_connectionParams', '_dbPrefix');
102  }
103 
104  /**
105  * Set the connection parameters.
106  * @param $params Initialization data given in an assoziative array with the following keys:
107  * dbType, dbHostName, dbUserName, dbPassword, dbName
108  * if dbPrefix is given it will be appended to every table string, which is
109  * usefull if different cms operate on the same database
110  */
111  public function setConnectionParams($params) {
112  $this->_connectionParams = $params;
113  if (isset($this->_connectionParams['dbPrefix'])) {
114  $this->_dbPrefix = $this->_connectionParams['dbPrefix'];
115  }
116  }
117 
118  /**
119  * Get the connection parameters.
120  * @return Assoziative array with the following keys:
121  * dbType, dbHostName, dbUserName, dbPassword, dbName, dbPrefix
122  */
123  public function getConnectionParams() {
124  return $this->_connectionParams;
125  }
126 
127  /**
128  * Actually connect to the database using the configuration parameters given
129  * to the constructor. The implementation ensures that only one connection is
130  * used for all RDBMappers with the same configuration parameters.
131  */
132  private function connect() {
133  // connect
134  if (isset($this->_connectionParams['dbType']) && isset($this->_connectionParams['dbHostName']) &&
135  isset($this->_connectionParams['dbUserName']) && isset($this->_connectionParams['dbPassword']) &&
136  isset($this->_connectionParams['dbName'])) {
137 
138  $this->_connId = join(',', array($this->_connectionParams['dbType'], $this->_connectionParams['dbHostName'],
139  $this->_connectionParams['dbUserName'], $this->_connectionParams['dbPassword'], $this->_connectionParams['dbName']));
140 
141  // reuse an existing connection if possible
142  if (isset(self::$_connections[$this->_connId])) {
143  $this->_conn = self::$_connections[$this->_connId];
144  }
145  else {
146  try {
147  // create new connection
148  $pdoParams = array(
149  PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
150  );
151  // mysql specific
152  if (strtolower($this->_connectionParams['dbType']) == 'mysql') {
153  $pdoParams[PDO::MYSQL_ATTR_USE_BUFFERED_QUERY] = true;
154  $charSet = isset($this->_connectionParams['dbCharSet']) ?
155  $this->_connectionParams['dbCharSet'] : 'utf8';
156  $pdoParams[PDO::MYSQL_ATTR_INIT_COMMAND] = "SET NAMES ".$charSet;
157  }
158  // sqlite specific
159  if (strtolower($this->_connectionParams['dbType']) == 'sqlite') {
160  if (strtolower($this->_connectionParams['dbName']) == ':memory:') {
161  $pdoParams[PDO::ATTR_PERSISTENT] = true;
162  }
163  else {
164  $this->_connectionParams['dbName'] = FileUtil::realpath(WCMF_BASE.$this->_connectionParams['dbName']);
165  }
166  }
167  $params = array(
168  'host' => $this->_connectionParams['dbHostName'],
169  'username' => $this->_connectionParams['dbUserName'],
170  'password' => $this->_connectionParams['dbPassword'],
171  'dbname' => $this->_connectionParams['dbName'],
172  'driver_options' => $pdoParams,
173  'profiler' => false
174  );
175  if (!empty($this->_connectionParams['dbPort'])) {
176  $params['port'] = $this->_connectionParams['dbPort'];
177  }
178  $this->_conn = Zend_Db::factory('Pdo_'.ucfirst($this->_connectionParams['dbType']), $params);
179  $this->_conn->setFetchMode(Zend_Db::FETCH_ASSOC);
180 
181  // store the connection for reuse
182  self::$_connections[$this->_connId] = $this->_conn;
183  }
184  catch(\Exception $ex) {
185  throw new PersistenceException("Connection to ".$this->_connectionParams['dbHostName'].".".
186  $this->_connectionParams['dbName']." failed: ".$ex->getMessage());
187  }
188  }
189  // get database prefix if defined
190  if (isset($this->_connectionParams['dbPrefix'])) {
191  $this->_dbPrefix = $this->_connectionParams['dbPrefix'];
192  }
193  }
194  else {
195  throw new IllegalArgumentException("Wrong parameters for constructor.");
196  }
197  }
198 
199  /**
200  * Enable profiling
201  */
202  public function enableProfiler() {
203  if ($this->_conn == null) {
204  $this->connect();
205  }
206  $this->_conn->getProfiler()->setEnabled(true);
207  }
208 
209  /**
210  * Disable profiling
211  */
212  public function disableProfiler() {
213  if ($this->_conn == null) {
214  $this->connect();
215  }
216  $this->_conn->getProfiler()->setEnabled(false);
217  }
218 
219  /**
220  * Get the profiler
221  * @return Zend_Db_Profiler
222  */
223  public function getProfiler() {
224  if ($this->_conn == null) {
225  $this->connect();
226  }
227  return $this->_conn->getProfiler();
228  }
229 
230  /**
231  * Get a new id for inserting into the database
232  * @return An id value.
233  */
234  protected function getNextId() {
235  try {
236  // get sequence table mapper
237  $sequenceMapper = $this->_persistenceFacade->getMapper(self::$SEQUENCE_CLASS);
238  if (!($sequenceMapper instanceof RDBMapper)) {
239  throw new PersistenceException(self::$SEQUENCE_CLASS." is not mapped by RDBMapper.");
240  }
241  $sequenceTable = $sequenceMapper->getTableName();
242  $sequenceConn = $sequenceMapper->getConnection();
243 
244  if ($this->_idSelectStmt == null) {
245  $this->_idSelectStmt = $sequenceConn->prepare("SELECT id FROM ".$sequenceTable);
246  }
247  if ($this->_idInsertStmt == null) {
248  $this->_idInsertStmt = $sequenceConn->prepare("INSERT INTO ".$sequenceTable." (id) VALUES (0)");
249  }
250  if ($this->_idUpdateStmt == null) {
251  $this->_idUpdateStmt = $sequenceConn->prepare("UPDATE ".$sequenceTable." SET id=(id+1);");
252  }
253  $this->_idSelectStmt->execute();
254  $rows = $this->_idSelectStmt->fetchAll(PDO::FETCH_ASSOC);
255  if (sizeof($rows) == 0) {
256  $this->_idInsertStmt->execute();
257  $this->_idInsertStmt->closeCursor();
258  $rows = array(array('id' => 0));
259  }
260  $id = $rows[0]['id'];
261  $this->_idUpdateStmt->execute();
262  $this->_idUpdateStmt->closeCursor();
263  $this->_idSelectStmt->closeCursor();
264  return $id;
265  }
266  catch (\Exception $ex) {
267  self::$_logger->error("The next id query caused the following exception:\n".$ex->getMessage());
268  throw new PersistenceException("Error in persistent operation. See log file for details.");
269  }
270  }
271 
272  /**
273  * @see PersistenceMapper::getQuoteIdentifierSymbol
274  */
275  public function getQuoteIdentifierSymbol() {
276  if ($this->_conn == null) {
277  $this->connect();
278  }
279  return $this->_conn->getQuoteIdentifierSymbol();
280  }
281 
282  /**
283  * @see PersistenceMapper::quoteIdentifier
284  */
285  public function quoteIdentifier($identifier) {
286  if ($this->_conn == null) {
287  $this->connect();
288  }
289  return $this->_conn->quoteIdentifier($identifier);
290  }
291 
292  /**
293  * @see PersistenceMapper::quoteValue
294  */
295  public function quoteValue($value) {
296  if ($this->_conn == null) {
297  $this->connect();
298  }
299  return $this->_conn->quote($value);
300  }
301 
302  /**
303  * Get the table name with the dbprefix added
304  * @return The table name
305  */
306  public function getRealTableName() {
307  return $this->_dbPrefix.$this->getTableName();
308  }
309 
310  /**
311  * Execute a query on the connection.
312  * @param $sql The SQL statement as string
313  * @param $isSelect Boolean whether the statement is a select statement (optional, default: _false_)
314  * @param $bindValues An array of data to bind to the placeholders (optional, default: empty array)
315  * @return If isSelect is true, an array as the result of PDOStatement::fetchAll(PDO::FETCH_ASSOC),
316  * the number of affected rows else
317  */
318  public function executeSql($sql, $isSelect=false, $bindValues=array()) {
319  if ($this->_conn == null) {
320  $this->connect();
321  }
322  try {
323  if ($isSelect) {
324  $stmt = $this->_conn->prepare($sql);
325  $stmt->execute($bindValues);
326  $result = $stmt->fetchAll();
327  $stmt->closeCursor();
328  return $result;
329  }
330  else {
331  return $this->_conn->exec($sql);
332  }
333  }
334  catch (\Exception $ex) {
335  self::$_logger->error("The query: ".$sql."\ncaused the following exception:\n".$ex->getMessage());
336  throw new PersistenceException("Error in persistent operation. See log file for details.");
337  }
338  }
339 
340  /**
341  * Execute a select query on the connection.
342  * @param $selectStmt A SelectStatement instance
343  * @param $pagingInfo An PagingInfo instance describing which page to load (optional, default: _null_)
344  * @return An array as the result of PDOStatement::fetchAll(PDO::FETCH_ASSOC)
345  */
346  protected function select(SelectStatement $selectStmt, PagingInfo $pagingInfo=null) {
347  if ($this->_conn == null) {
348  $this->connect();
349  }
350  try {
351  if ($pagingInfo != null) {
352  // make a count query if requested
353  if (!$pagingInfo->isIgnoringTotalCount()) {
354  $pagingInfo->setTotalCount($selectStmt->getRowCount());
355  }
356  // return empty array, if page size <= 0
357  if ($pagingInfo->getPageSize() <= 0) {
358  return array();
359  }
360  }
361  if (self::$_isDebugEnabled) {
362  self::$_logger->debug("Execute statement: ".$selectStmt->__toString());
363  self::$_logger->debug($selectStmt->getBind());
364  }
365  $result = $selectStmt->query();
366  // save statement on success
367  $selectStmt->save();
368  $rows = $result->fetchAll();
369  if (self::$_isDebugEnabled) {
370  self::$_logger->debug("Result: ".sizeof($rows)." row(s)");
371  }
372  return $rows;
373  }
374  catch (\Exception $ex) {
375  self::$_logger->error("The query: ".$selectStmt."\ncaused the following exception:\n".$ex->getMessage());
376  throw new PersistenceException("Error in persistent operation. See log file for details.");
377  }
378  }
379 
380  /**
381  * @see PersistenceMapper::executeOperation()
382  */
383  public function executeOperation(PersistenceOperation $operation) {
384  if ($operation->getType() != $this->getType()) {
385  throw new IllegalArgumentException("Operation: ".$operation.
386  " can't be executed by ".get_class($this));
387  }
388  if ($this->_conn == null) {
389  $this->connect();
390  }
391 
392  // transform table name
393  $tableName = $this->getRealTableName();
394 
395  // translate value names to columns
396  $translatedValues = array();
397  foreach($operation->getValues() as $name => $value) {
398  $attrDesc = $this->getAttribute($name);
399  if ($attrDesc) {
400  $translatedValues[$attrDesc->getColumn()] = $value;
401  }
402  }
403 
404  // transform criteria
405  $where = array();
406  foreach ($operation->getCriteria() as $criterion) {
407  $condition = $this->renderCriteria($criterion, '?', $tableName);
408  $where[$condition] = $criterion->getValue();
409  }
410 
411  // execute the statement
412  $affectedRows = 0;
413  try {
414  if ($operation instanceof InsertOperation) {
415  $affectedRows = $this->_conn->insert($tableName, $translatedValues);
416  }
417  elseif ($operation instanceof UpdateOperation) {
418  $affectedRows = $this->_conn->update($tableName, $translatedValues, $where);
419  }
420  elseif ($operation instanceof DeleteOperation) {
421  $affectedRows = $this->_conn->delete($tableName, $where);
422  }
423  else {
424  throw new IllegalArgumentException("Unsupported Operation: ".$operation);
425  }
426  }
427  catch (\Exception $ex) {
428  self::$_logger->error("The operation: ".$operation."\ncaused the following exception:\n".$ex->getMessage());
429  throw new PersistenceException("Error in persistent operation. See log file for details.");
430  }
431  return $affectedRows;
432  }
433 
434  /**
435  * @see PersistenceMapper::getRelations()
436  */
437  public function getRelations($hierarchyType='all') {
438  $this->initRelations();
439  if ($hierarchyType == 'all') {
440  return array_values($this->_relations['byrole']);
441  }
442  else {
443  return $this->_relations[$hierarchyType];
444  }
445  }
446 
447  /**
448  * @see PersistenceMapper::getRelation()
449  */
450  public function getRelation($roleName) {
451  return $this->getRelationImpl($roleName, false);
452  }
453 
454  /**
455  * @see PersistenceMapper::getRelationsByType()
456  */
457  public function getRelationsByType($type) {
458  $this->initRelations();
459  if (isset($this->_relations['bytype'][$type])) {
460  return $this->_relations['bytype'][$type];
461  }
462  else {
463  throw new PersistenceException("No relation to '".$type."' exists in '".$this->getType()."'");
464  }
465  }
466 
467  /**
468  * Internal implementation of PersistenceMapper::getRelation()
469  * @param $roleName The role name of the relation
470  * @param $includeManyToMany Boolean whether to also search in relations to many to many
471  * objects or not
472  * @return RelationDescription
473  */
474  protected function getRelationImpl($roleName, $includeManyToMany) {
475  $this->initRelations();
476  if (isset($this->_relations['byrole'][$roleName])) {
477  return $this->_relations['byrole'][$roleName];
478  }
479  elseif ($includeManyToMany && isset($this->_relations['nm'][$roleName])) {
480  return $this->_relations['nm'][$roleName];
481  }
482  else {
483  throw new PersistenceException("No relation to '".$roleName."' exists in '".$this->getType()."'");
484  }
485  }
486 
487  /**
488  * Get the relation descriptions defined in the subclass and add them to internal arrays.
489  */
490  private function initRelations() {
491  if ($this->_relations == null) {
492  $this->_relations = array();
493  $this->_relations['byrole'] = $this->getRelationDescriptions();
494  $this->_relations['bytype'] = array();
495  $this->_relations['parent'] = array();
496  $this->_relations['child'] = array();
497  $this->_relations['undefined'] = array();
498  $this->_relations['nm'] = array();
499 
500  foreach ($this->_relations['byrole'] as $role => $desc) {
501  $otherType = $desc->getOtherType();
502  if (!isset($this->_relations['bytype'][$otherType])) {
503  $this->_relations['bytype'][$otherType] = array();
504  }
505  $this->_relations['bytype'][$otherType][] = $desc;
506 
507  $hierarchyType = $desc->getHierarchyType();
508  if ($hierarchyType == 'parent') {
509  $this->_relations['parent'][] = $desc;
510  }
511  elseif ($hierarchyType == 'child') {
512  $this->_relations['child'][] = $desc;
513  }
514  else {
515  $this->_relations['undefined'][] = $desc;
516  }
517  // also store relations to many to many objects, because
518  // they would be invisible otherwise
519  if ($desc instanceof RDBManyToManyRelationDescription) {
520  $nmDesc = $desc->getThisEndRelation();
521  $this->_relations['nm'][$nmDesc->getOtherRole()] = $nmDesc;
522  }
523  }
524  }
525  }
526 
527  /**
528  * @see PersistenceMapper::getAttributes()
529  */
530  public function getAttributes(array $tags=array(), $matchMode='all') {
531  $this->initAttributes();
532  $result = array();
533  if (sizeof($tags) == 0) {
534  $result = array_values($this->_attributes['byname']);
535  }
536  else {
537  foreach ($this->_attributes['byname'] as $name => $desc) {
538  if ($desc->matchTags($tags, $matchMode)) {
539  $result[] = $desc;
540  }
541  }
542  }
543  return $result;
544  }
545 
546  /**
547  * @see PersistenceMapper::getAttribute()
548  */
549  public function getAttribute($name) {
550  $this->initAttributes();
551  if (isset($this->_attributes['byname'][$name])) {
552  return $this->_attributes['byname'][$name];
553  }
554  else {
555  throw new PersistenceException("No attribute '".$name."' exists in '".$this->getType()."'");
556  }
557  }
558 
559  /**
560  * Get the references to other entities
561  * @return Array of AttributeDescription instances
562  */
563  protected function getReferences() {
564  $this->initAttributes();
565  return $this->_attributes['refs'];
566  }
567 
568  /**
569  * Get the relation descriptions defined in the subclass and add them to internal arrays.
570  */
571  private function initAttributes() {
572  if ($this->_attributes == null) {
573  $this->_attributes = array();
574  $this->_attributes['byname'] = $this->getAttributeDescriptions();
575  $this->_attributes['refs'] = array();
576  foreach ($this->_attributes['byname'] as $name => $attrDesc) {
577  if ($attrDesc instanceof ReferenceDescription) {
578  $this->_attributes['refs'][] = $attrDesc;
579  }
580  }
581  }
582  }
583 
584  /**
585  * @see PersistenceMapper::isSortable()
586  */
587  public function isSortable($roleName=null) {
588  return $this->getSortkey($roleName) != null;
589  }
590 
591  /**
592  * @see PersistenceMapper::getSortkey()
593  */
594  public function getSortkey($roleName=null) {
595  $sortDefs = $this->getDefaultOrder($roleName);
596  if (sizeof($sortDefs) > 0 && $sortDefs[0]['isSortkey'] == true) {
597  return $sortDefs[0];
598  }
599  return null;
600  }
601 
602  /**
603  * @see PersistenceMapper::getDefaultOrder()
604  */
605  public function getDefaultOrder($roleName=null) {
606  $sortDef = null;
607  $sortType = null;
608  if ($roleName != null && $this->hasRelation($roleName) &&
609  ($relationDesc = $this->getRelation($roleName)) instanceof RDBManyToManyRelationDescription) {
610 
611  // the order may be overriden by the many to many relation class
612  $thisRelationDesc = $relationDesc->getThisEndRelation();
613  $nmMapper = $thisRelationDesc->getOtherMapper($thisRelationDesc->getOtherType());
614  $sortDef = $nmMapper->getOwnDefaultOrder($roleName);
615  $sortType = $nmMapper->getType();
616  }
617  else {
618  // default: the order is defined in this mapper
619  $sortDef = $this->getOwnDefaultOrder($roleName);
620  $sortType = $this->getType();
621  }
622  // add the sortType parameter to the result
623  for ($i=0, $count=sizeof($sortDef); $i<$count; $i++) {
624  $sortDef[$i]['sortType'] = $sortType;
625  }
626  return $sortDef;
627  }
628 
629  /**
630  * Check if a value is a primary key value
631  * @param $name The name of the value
632  * @return Boolean
633  */
634  protected function isPkValue($name) {
635  $pkNames = $this->getPKNames();
636  return in_array($name, $pkNames);
637  }
638 
639  /**
640  * Construct an object id from given row data
641  * @param $data An associative array with the pk column names as keys and pk values as values
642  * @return The oid
643  */
644  protected function constructOID($data) {
645  $pkNames = $this->getPkNames();
646  $ids = array();
647  foreach ($pkNames as $pkName) {
648  $ids[] = $data[$pkName];
649  }
650  return new ObjectId($this->getType(), $ids);
651  }
652 
653  /**
654  * Render a Criteria instance as string.
655  * @param $criteria The Criteria instance
656  * @param $placeholder Placeholder (':columnName', '?') used instead of the value (optional, default: _null_)
657  * @param $tableName The table name to use (may differ from criteria's type attribute) (optional)
658  * @param $columnName The column name to use (may differ from criteria's attribute attribute) (optional)
659  * @return String
660  */
661  public function renderCriteria(Criteria $criteria, $placeholder=null, $tableName=null, $columnName=null) {
662  $type = $criteria->getType();
663  if (!$this->_persistenceFacade->isKnownType($type)) {
664  throw new IllegalArgumentException("Unknown type referenced in Criteria: $type");
665  }
666 
667  // map type and attribute, if necessary
668  $mapper = $this->_persistenceFacade->getMapper($type);
669  if ($tableName === null) {
670  $tableName = $mapper->getRealTableName();
671  }
672  if ($columnName === null) {
673  $attrDesc = $mapper->getAttribute($criteria->getAttribute());
674  $columnName = $attrDesc->getColumn();
675  }
676 
677  $result = $mapper->quoteIdentifier($tableName).".".$mapper->quoteIdentifier($columnName);
678  $operator = $criteria->getOperator();
679  $value = $criteria->getValue();
680  if ($operator == '=' && $value === null) {
681  // handle null values
682  $result .= " IS NULL";
683  }
684  else {
685  $result .= " ".$criteria->getOperator()." ";
686  $valueStr = !$placeholder ? $mapper->quoteValue($value) : $placeholder;
687  if (is_array($value)) {
688  $result .= "(".$valueStr.")";
689  }
690  else {
691  $result .= $valueStr;
692  }
693  }
694  return $result;
695  }
696 
697  /**
698  * @see AbstractMapper::loadImpl()
699  */
700  protected function loadImpl(ObjectId $oid, $buildDepth=BuildDepth::SINGLE) {
701  if (self::$_isDebugEnabled) {
702  self::$_logger->debug("Load object: ".$oid->__toString());
703  }
704  // delegate to loadObjects
705  $criteria = $this->createPKCondition($oid);
706  $pagingInfo = new PagingInfo(1, true);
707  $objects = $this->loadObjects($oid->getType(), $buildDepth, $criteria, null, $pagingInfo);
708  if (sizeof($objects) > 0) {
709  return $objects[0];
710  }
711  else {
712  return null;
713  }
714  }
715 
716  /**
717  * @see AbstractMapper::createImpl()
718  * @note The type parameter is not used here because this class only constructs one type
719  */
720  protected function createImpl($type, $buildDepth=BuildDepth::SINGLE) {
721  if ($buildDepth < 0 && !in_array($buildDepth, array(BuildDepth::SINGLE, BuildDepth::REQUIRED))) {
722  throw new IllegalArgumentException("Build depth not supported: $buildDepth");
723  }
724  // create the object
725  $object = $this->createObjectFromData(array());
726 
727  // recalculate build depth for the next generation
728  $newBuildDepth = $buildDepth;
729  if ($buildDepth != BuildDepth::REQUIRED && $buildDepth != BuildDepth::SINGLE && $buildDepth > 0) {
730  $newBuildDepth = $buildDepth-1;
731  }
732 
733  // prevent infinite recursion
734  if ($buildDepth < BuildDepth::MAX) {
735  $relationDescs = $this->getRelations();
736 
737  // set dependend objects of this object
738  foreach ($relationDescs as $curRelationDesc) {
739  if ( ($curRelationDesc->getHierarchyType() == 'child' && ($buildDepth > 0 ||
740  // if BuildDepth::REQUIRED only construct shared/composite children with min multiplicity > 0
741  ($buildDepth == BuildDepth::REQUIRED && $curRelationDesc->getOtherMinMultiplicity() > 0 && $curRelationDesc->getOtherAggregationKind() != 'none')
742  )) ) {
743  $childObject = null;
744  if ($curRelationDesc instanceof RDBManyToManyRelationDescription) {
745  $childObject = $this->_persistenceFacade->create($curRelationDesc->getOtherType(), BuildDepth::SINGLE);
746  }
747  else {
748  $childObject = $this->_persistenceFacade->create($curRelationDesc->getOtherType(), $newBuildDepth);
749  }
750  $object->setValue($curRelationDesc->getOtherRole(), array($childObject), true, false);
751  }
752  }
753  }
754  return $object;
755  }
756 
757  /**
758  * @see AbstractMapper::saveImpl()
759  */
760  protected function saveImpl(PersistentObject $object) {
761  if ($this->_conn == null) {
762  $this->connect();
763  }
764 
765  // set all missing attributes
766  $this->prepareForStorage($object);
767 
768  if ($object->getState() == PersistentObject::STATE_NEW) {
769  // insert new object
770  $operations = $this->getInsertSQL($object);
771  foreach($operations as $operation) {
772  $mapper = $this->_persistenceFacade->getMapper($operation->getType());
773  $mapper->executeOperation($operation);
774  }
775  // log action
776  $this->logAction($object);
777  }
778  else if ($object->getState() == PersistentObject::STATE_DIRTY) {
779  // save existing object
780  // precondition: the object exists in the database
781 
782  // log action
783  $this->logAction($object);
784 
785  // save object
786  $operations = $this->getUpdateSQL($object);
787  foreach($operations as $operation) {
788  $mapper = $this->_persistenceFacade->getMapper($operation->getType());
789  $mapper->executeOperation($operation);
790  }
791  }
792 
794 
795  // postcondition: the object is saved to the db
796  // the object state is STATE_CLEAN
797  // attributes are only inserted if their values differ from ''
798  return true;
799  }
800 
801  /**
802  * @see AbstractMapper::deleteImpl()
803  */
804  protected function deleteImpl(PersistentObject $object) {
805  if ($this->_conn == null) {
806  $this->connect();
807  }
808 
809  // log action
810  $this->logAction($object);
811 
812  // delete object
813  $oid = $object->getOID();
814  $affectedRows = 0;
815  $operations = $this->getDeleteSQL($oid);
816  foreach($operations as $operation) {
817  $mapper = $this->_persistenceFacade->getMapper($operation->getType());
818  $affectedRows += $mapper->executeOperation($operation);
819  }
820  // only delete children if the object was deleted
821  if ($affectedRows > 0) {
822  $proxy = new PersistentObjectProxy($oid);
823  $relationDescs = $this->getRelations('child');
824  foreach($relationDescs as $relationDesc) {
825  $isManyToMany = ($relationDesc instanceof RDBManyToManyRelationDescription);
826  $isComposite = ($relationDesc->getOtherAggregationKind() == 'composite' ||
827  $isManyToMany);
828  if ($isManyToMany) {
829  // in a many to many relation we only use the relation description
830  // that points to relation objects
831  $relationDesc = $relationDesc->getThisEndRelation();
832  }
833 
834  // load related objects
835  $otherType = $relationDesc->getOtherType();
836  $otherMapper = $this->_persistenceFacade->getMapper($otherType);
837  $allObjects = $this->loadRelationImpl(array($proxy), $relationDesc->getOtherRole());
838  $oidStr = $proxy->getOID()->__toString();
839  if (isset($allObjects[$oidStr])) {
840  foreach($allObjects[$oidStr] as $object) {
841  if ($isManyToMany) {
842  // delete the many to many object immediatly
843  $otherMapper->delete($object);
844  }
845  elseif ($isComposite) {
846  // delete composite and relation object children
847  $object->delete();
848  }
849  else {
850  // unlink shared children
851  $object->setValue($relationDesc->getThisRole(), null, true, false);
852  }
853  }
854  }
855  }
856  }
857  // postcondition: the object and all dependend objects are deleted from db
858  return true;
859  }
860 
861  /**
862  * Get the database connection.
863  * @return A reference to the PDOConnection object
864  */
865  public function getConnection() {
866  if ($this->_conn == null) {
867  $this->connect();
868  }
869  return $this->_conn;
870  }
871 
872  /**
873  * @see PersistenceMapper::getOIDsImpl()
874  * @note The type parameter is not used here because this class only constructs one type
875  */
876  protected function getOIDsImpl($type, $criteria=null, $orderby=null, PagingInfo $pagingInfo=null) {
877  $oids = array();
878 
879  // create query (load only pk columns and no children oids)
880  $type = $this->getType();
881  $objects = $this->loadObjectsFromQueryParts($type, BuildDepth::SINGLE, $criteria, $orderby,
882  $pagingInfo);
883 
884  // collect oids
885  for ($i=0; $i<sizeof($objects); $i++) {
886  $oids[] = $objects[$i]->getOID();
887  }
888  return $oids;
889  }
890 
891  /**
892  * @see PersistenceFacade::loadObjectsImpl()
893  */
894  protected function loadObjectsImpl($type, $buildDepth=BuildDepth::SINGLE, $criteria=null, $orderby=null, PagingInfo $pagingInfo=null) {
895  if (self::$_isDebugEnabled) {
896  self::$_logger->debug("Load objects: ".$type);
897  }
898  $objects = $this->loadObjectsFromQueryParts($type, $buildDepth, $criteria, $orderby, $pagingInfo);
899  return $objects;
900  }
901 
902  /**
903  * Load objects defined by several query parts.
904  * @param $type The type of the object
905  * @param $buildDepth One of the BUILDDEPTH constants or a number describing the number of generations to build
906  * (except BuildDepth::REQUIRED, BuildDepth::PROXIES_ONLY) (default: BuildDepth::SINGLE)
907  * @param $criteria An array of Criteria instances that define conditions on the type's attributes (optional, default: _null_)
908  * @param $orderby An array holding names of attributes to order by, maybe appended with 'ASC', 'DESC' (optional, default: _null_)
909  * @param $pagingInfo A reference PagingInfo instance (optional, default: _null_)
910  * @return Array of PersistentObject instances
911  */
912  protected function loadObjectsFromQueryParts($type, $buildDepth=BuildDepth::SINGLE, $criteria=null, $orderby=null, PagingInfo $pagingInfo=null) {
913  if ($buildDepth < 0 && !in_array($buildDepth, array(BuildDepth::INFINITE, BuildDepth::SINGLE))) {
914  throw new IllegalArgumentException("Build depth not supported: $buildDepth");
915  }
916 
917  // create query
918  $selectStmt = $this->getSelectSQL($criteria, null, $orderby, $pagingInfo);
919 
920  $objects = $this->loadObjectsFromSQL($selectStmt, $buildDepth, $pagingInfo);
921  return $objects;
922  }
923 
924  /**
925  * Load objects defined by a select statement.
926  * @param $selectStmt A SelectStatement instance
927  * @param $buildDepth One of the BUILDDEPTH constants or a number describing the number of generations to build
928  * (except BuildDepth::REQUIRED, BuildDepth::PROXIES_ONLY) (default: BuildDepth::SINGLE)
929  * @param $pagingInfo A reference PagingInfo instance (optional, default: _null_)
930  * @return Array of PersistentObject instances
931  */
932  public function loadObjectsFromSQL(SelectStatement $selectStmt, $buildDepth=BuildDepth::SINGLE, PagingInfo $pagingInfo=null) {
933  if ($this->_conn == null) {
934  $this->connect();
935  }
936  $objects = array();
937 
938  $data = $this->select($selectStmt, $pagingInfo);
939  if (sizeof($data) == 0) {
940  return $objects;
941  }
942 
943  $tx = $this->_persistenceFacade->getTransaction();
944  for ($i=0, $count=sizeof($data); $i<$count; $i++) {
945  // create the object
946  $object = $this->createObjectFromData($data[$i]);
947 
948  // don't set the state recursive, because otherwise relations would be initialized
949  $object->setState(PersistentObject::STATE_CLEAN);
950 
951  $objects[] = $object;
952  }
953  // add related objects
954  $this->addRelatedObjects($objects, $buildDepth);
955 
956  // register objects with the transaction
957  $registeredObjects = array();
958  for ($i=0, $count=sizeof($objects); $i<$count; $i++) {
959  $registeredObject = $tx->registerLoaded($objects[$i]);
960  // don't return objects that are to be deleted by the current transaction
961  if ($registeredObject->getState() != PersistentObject::STATE_DELETED) {
962  $registeredObjects[] = $registeredObject;
963  }
964  }
965  return $registeredObjects;
966  }
967 
968  /**
969  * Create an object of the mapper's type with the given attributes from the given data
970  * @param $data An associative array with the attribute names as keys and the attribute values as values
971  * @return PersistentObject
972  */
973  protected function createObjectFromData(array $data) {
974  // determine if we are loading or creating
975  $createFromLoadedData = (sizeof($data) > 0) ? true : false;
976 
977  // initialize data and oid
978  $oid = null;
979  if ($createFromLoadedData) {
980  $oid = $this->constructOID($data);
981  }
982 
983  // construct object
984  $object = $this->createObject($oid);
985 
986  // apply data to the created object
987  if ($createFromLoadedData) {
988  $this->applyDataOnLoad($object, $data);
989  }
990  else {
991  $this->applyDataOnCreate($object);
992  }
993  return $object;
994  }
995 
996  /**
997  * Apply the loaded object data to the object.
998  * @note Subclasses must implement this method to define their object type.
999  * @param $object A reference to the object created with createObject method to which the data should be applied
1000  * @param $objectData An associative array with the data returned by execution of the database select statement
1001  * (given by getSelectSQL).
1002  */
1003  protected function applyDataOnLoad(PersistentObject $object, array $objectData) {
1004  // set object data
1005  $values = array();
1006  foreach($objectData as $name => $value) {
1007  if ($this->hasAttribute($name) || strpos($name, self::INTERNAL_VALUE_PREFIX) === 0) {
1008  $values[$name] = $value;
1009  }
1010  }
1011  $object->initialize($values);
1012  }
1013 
1014  /**
1015  * Apply the default data to the object.
1016  * @note Subclasses must implement this method to define their object type.
1017  * @param $object A reference to the object created with createObject method to which the data should be applied
1018  */
1019  protected function applyDataOnCreate(PersistentObject $object) {
1020  // set object data
1021  $values = array();
1022  $attributeDescriptions = $this->getAttributes();
1023  foreach($attributeDescriptions as $curAttributeDesc) {
1024  $name = $curAttributeDesc->getName();
1025  $values[$name] = $curAttributeDesc->getDefaultValue();
1026  }
1027  $object->initialize($values);
1028  }
1029 
1030  /**
1031  * Append the child data to a list of object. If the buildDepth does not determine to load a
1032  * child generation, only the oids of the children will be loaded.
1033  * @param $objects Array of PersistentObject instances to append the children to
1034  * @param $buildDepth @see PersistenceFacade::loadObjects()
1035  */
1036  protected function addRelatedObjects(array $objects, $buildDepth=BuildDepth::SINGLE) {
1037 
1038  // recalculate build depth for the next generation
1039  $newBuildDepth = $buildDepth;
1040  if ($buildDepth != BuildDepth::SINGLE && $buildDepth != BuildDepth::INFINITE && $buildDepth > 0) {
1041  $newBuildDepth = $buildDepth-1;
1042  }
1043  $loadNextGeneration = (($buildDepth != BuildDepth::SINGLE) && ($buildDepth > 0 || $buildDepth == BuildDepth::INFINITE));
1044 
1045  // get dependend objects of this object
1046  $relationDescs = $this->getRelations();
1047  foreach($relationDescs as $relationDesc) {
1048  $role = $relationDesc->getOtherRole();
1049 
1050  $relationId = $role.$relationDesc->getThisRole();
1051  // if the build depth is not satisfied already and the relation is not
1052  // currently loading, we load the complete objects and add them
1053  if ($loadNextGeneration && !isset($this->_loadingRelations[$relationId])) {
1054  $this->_loadingRelations[$relationId] = true;
1055  $relatives = $this->loadRelation($objects, $role, $newBuildDepth);
1056  // set the values
1057  foreach ($objects as $object) {
1058  $oidStr = $object->getOID()->__toString();
1059  $object->setValue($role, isset($relatives[$oidStr]) ? $relatives[$oidStr] : null, true, false);
1060  }
1061  unset($this->_loadingRelations[$relationId]);
1062  }
1063  // otherwise set the value to not initialized.
1064  // the Node will initialize it with the proxies for the relation objects
1065  // on first access
1066  else {
1067  foreach ($objects as $object) {
1068  if ($object instanceof Node) {
1069  $object->addRelation($role);
1070  }
1071  }
1072  }
1073  }
1074  }
1075 
1076  /**
1077  * @see AbstractMapper::loadRelationImpl()
1078  */
1079  protected function loadRelationImpl(array $objects, $role, $buildDepth=BuildDepth::SINGLE,
1080  $criteria=null, $orderby=null, PagingInfo $pagingInfo=null) {
1081  if (self::$_isDebugEnabled) {
1082  self::$_logger->debug("Load relation: ".$role);
1083  }
1084  $relatives = array();
1085  if (sizeof($objects) == 0) {
1086  return $relatives;
1087  }
1088  $type = $objects[0]->getType();
1089 
1090  $otherRelationDescription = $this->getRelationImpl($role, true);
1091  if ($otherRelationDescription->getOtherNavigability() == true) {
1092  $otherType = $otherRelationDescription->getOtherType();
1093  $otherMapper = $this->_persistenceFacade->getMapper($otherType);
1094  if (!($otherMapper instanceof RDBMapper)) {
1095  throw new PersistenceException("Can only load related objects, if they are mapped by an RDBMapper instance.");
1096  }
1097 
1098  // load related objects from other mapper
1099  $relatedObjects = array();
1100  $thisRole = $otherRelationDescription->getThisRole();
1101  $thisRelationDescription = $otherMapper->getRelationImpl($thisRole, true);
1102  if ($thisRelationDescription->getOtherNavigability() == true) {
1103  list($selectStmt, $objValueName, $relValueName) = $otherMapper->getRelationSelectSQL($objects, $thisRole, $criteria, $orderby, $pagingInfo);
1104  $relatedObjects = $otherMapper->loadObjectsFromSQL($selectStmt, ($buildDepth == BuildDepth::PROXIES_ONLY) ? BuildDepth::SINGLE : $buildDepth, $pagingInfo);
1105  }
1106  }
1107  // group relatedObjects by original objects
1108  $relativeMap = array();
1109  foreach ($relatedObjects as $relatedObject) {
1110  $key = $relatedObject->getValue($relValueName);
1111  if (!isset($relativeMap[$key])) {
1112  $relativeMap[$key] = array();
1113  }
1114  $relativeMap[$key][] = ($buildDepth != BuildDepth::PROXIES_ONLY) ? $relatedObject :
1115  new PersistentObjectProxy($relatedObject->getOID());
1116 
1117  // remove internal value after use (important when loading nm relations,
1118  // because if not done, the value will not be updated when loading the relation
1119  // for another object, leading to less objects seen in the relation)
1120  if (strpos($relValueName, self::INTERNAL_VALUE_PREFIX) === 0) {
1121  $relatedObject->removeValue($relValueName);
1122  }
1123  }
1124  foreach ($objects as $object) {
1125  $oidStr = $object->getOID()->__toString();
1126  $key = $object->getValue($objValueName);
1127  $relatives[$oidStr] = isset($relativeMap[$key]) ? $relativeMap[$key] : array();
1128  }
1129  return $relatives;
1130  }
1131 
1132  /**
1133  * @see PersistenceMapper::beginTransaction()
1134  * Since all RDBMapper instances with the same connection parameters share
1135  * one connection, the call will be ignored, if the method was already called
1136  * for another instance.
1137  */
1138  public function beginTransaction() {
1139  if ($this->_conn == null) {
1140  $this->connect();
1141  }
1142  if (!$this->isInTransaction()) {
1143  $this->_conn->beginTransaction();
1144  $this->setIsInTransaction(true);
1145  }
1146  }
1147 
1148  /**
1149  * @see PersistenceMapper::commitTransaction()
1150  * Since all RDBMapper instances with the same connection parameters share
1151  * one connection, the call will be ignored, if the method was already called
1152  * for another instance.
1153  */
1154  public function commitTransaction() {
1155  if ($this->_conn == null) {
1156  $this->connect();
1157  }
1158  if ($this->isInTransaction()) {
1159  $this->_conn->commit();
1160  $this->setIsInTransaction(false);
1161  }
1162  }
1163 
1164  /**
1165  * @see PersistenceMapper::rollbackTransaction()
1166  * @note Rollbacks have to be supported by the database.
1167  * Since all RDBMapper instances with the same connection parameters share
1168  * one connection, the call will be ignored, if the method was already called
1169  * for another instance.
1170  */
1171  public function rollbackTransaction() {
1172  if ($this->_conn == null) {
1173  $this->connect();
1174  }
1175  if ($this->isInTransaction()) {
1176  $this->_conn->rollBack();
1177  $this->setIsInTransaction(false);
1178  }
1179  }
1180 
1181  /**
1182  * Set the transaction state for the connection
1183  * @param $isInTransaction Boolean whether the connection is in a transaction or not
1184  */
1185  protected function setIsInTransaction($isInTransaction) {
1186  self::$_inTransaction[$this->_connId] = $isInTransaction;
1187  }
1188 
1189  /**
1190  * Check if the connection is currently in a transaction
1191  * @return Boolean
1192  */
1193  protected function isInTransaction() {
1194  return isset(self::$_inTransaction[$this->_connId]) && self::$_inTransaction[$this->_connId] === true;
1195  }
1196 
1197  /**
1198  * TEMPLATE METHODS
1199  * Subclasses must implement this method to define their object type.
1200  */
1201 
1202  /**
1203  * Get the names of the attributes in the mapped class to order by default and the sort directions
1204  * (ASC or DESC). The roleName parameter allows to ask for the order with respect to a specific role.
1205  * @param $roleName The role name of the relation (optional, default: _null_)
1206  * @return An array of assciative arrays with the keys sortFieldName and sortDirection (ASC or DESC)
1207  */
1208  abstract protected function getOwnDefaultOrder($roleName=null);
1209 
1210  /**
1211  * Get a list of all RelationDescriptions.
1212  * @return An associative array with the relation names as keys and the RelationDescription instances as values.
1213  */
1214  abstract protected function getRelationDescriptions();
1215 
1216  /**
1217  * Get a list of all AttributeDescriptions.
1218  * @return An associative array with the attribute names as keys and the AttributeDescription instances as values.
1219  */
1220  abstract protected function getAttributeDescriptions();
1221 
1222  /**
1223  * Factory method for the supported object type.
1224  * @param $oid The object id (maybe null)
1225  * @return A reference to the created object.
1226  */
1227  abstract protected function createObject(ObjectId $oid=null);
1228 
1229  /**
1230  * Set the object primary key and foreign key values for storing the object in the database.
1231  * @param $object A reference to the object to insert.
1232  * @note The object does not have the final object id set. If a new id value for a primary key column is needed.
1233  * @note The prepared object will be used in the application afterwards. So values that are only to be modified for
1234  * the storage process should be changed in getInsertSQL() and getUpdateSQL() only!
1235  * for the insert statement, use RDBMapper::getNextId().
1236  */
1237  abstract protected function prepareForStorage(PersistentObject $object);
1238 
1239  /**
1240  * Get the SQL command to select object data from the database.
1241  * @param $criteria An array of Criteria instances that define conditions on the type's attributes (optional, default: _null_)
1242  * @param $alias The alias for the table name (default: _null_)
1243  * @param $orderby An array holding names of attributes to order by, maybe appended with 'ASC', 'DESC' (optional, default: _null_)
1244  * @param $pagingInfo An PagingInfo instance describing which page to load (optional, default: _null_))
1245  * @param $queryId Identifier for the query cache (maybe null to let implementers handle it). (default: _null_)
1246  * @return SelectStatement instance that selects all object data that match the condition or an array with the query parts.
1247  * @note The names of the data item columns MUST match the data item names provided in the '_datadef' array from RDBMapper::getObjectDefinition()
1248  * Use alias names if not! The selected data will be put into the '_data' array of the object definition.
1249  */
1250  abstract public function getSelectSQL($criteria=null, $alias=null, $orderby=null, PagingInfo $pagingInfo=null, $queryId=null);
1251 
1252  /**
1253  * Get the SQL command to select those objects from the database that are related to the given object.
1254  * @note Navigability may not be checked in this method
1255  * @note In case of a sortable many to many relation, the sortkey value must also be selected
1256  * @param $otherObjectProxies Array of PersistentObjectProxy instances for the objects to load the relatives for.
1257  * @param $otherRole The role of the other object in relation to the objects to load.
1258  * @param $criteria An array of Criteria instances that define conditions on the object's attributes (optional, default: _null_)
1259  * @param $orderby An array holding names of attributes to order by, maybe appended with 'ASC', 'DESC' (optional, default: _null_)
1260  * @param $pagingInfo An PagingInfo instance describing which page to load (optional, default: _null_)
1261  * @return Array with SelectStatement instance and the attribute names which establish the relation between
1262  * the loaded objects and the proxies (proxies's attribute name first)
1263  */
1264  abstract protected function getRelationSelectSQL(array $otherObjectProxies, $otherRole,
1265  $criteria=null, $orderby=null, PagingInfo $pagingInfo=null);
1266 
1267  /**
1268  * Get the SQL command to insert a object into the database.
1269  * @param $object A reference to the object to insert.
1270  * @return Array of PersistenceOperation instances that insert a new object.
1271  */
1272  abstract protected function getInsertSQL(PersistentObject $object);
1273 
1274  /**
1275  * Get the SQL command to update a object in the database.
1276  * @param $object A reference to the object to update.
1277  * @return Array of PersistenceOperation instances that update an existing object.
1278  */
1279  abstract protected function getUpdateSQL(PersistentObject $object);
1280 
1281  /**
1282  * Get the SQL command to delete a object from the database.
1283  * @param $oid The object id of the object to delete.
1284  * @return Array of PersistenceOperation instances that delete an existing object.
1285  */
1286  abstract protected function getDeleteSQL(ObjectId $oid);
1287 
1288  /**
1289  * Create an array of condition Criteria instances for the primary key values
1290  * @param $oid The object id that defines the primary key values
1291  * @return Array of Criteria instances
1292  */
1293  abstract protected function createPKCondition(ObjectId $oid);
1294 
1295  /**
1296  * Get the name of the database table, where this type is mapped to
1297  * @return String
1298  */
1299  abstract protected function getTableName();
1300 
1301  /**
1302  * Determine if an attribute is a foreign key
1303  * @return Boolean
1304  */
1305  abstract public function isForeignKey($name);
1306 }
1307 ?>
setIsInTransaction($isInTransaction)
Set the transaction state for the connection.
Definition: RDBMapper.php:1185
getDefaultOrder($roleName=null)
Definition: RDBMapper.php:605
setState($state)
Set the state of the object to one of the STATE constants.
constructOID($data)
Construct an object id from given row data.
Definition: RDBMapper.php:644
getOID()
Get the object id of the PersistentObject.
prepareForStorage(PersistentObject $object)
Set the object primary key and foreign key values for storing the object in the database.
getOwnDefaultOrder($roleName=null)
TEMPLATE METHODS Subclasses must implement this method to define their object type.
UpdateOperation instances hold data necessary to accomplish an update operation on the persistent sto...
disableProfiler()
Disable profiling.
Definition: RDBMapper.php:212
query($fetchMode=null, $bind=array())
__construct(PersistenceFacade $persistenceFacade, PermissionManager $permissionManager, ConcurrencyManager $concurrencyManager, EventManager $eventManager, Message $message)
Constructor.
Definition: RDBMapper.php:83
getSelectSQL($criteria=null, $alias=null, $orderby=null, PagingInfo $pagingInfo=null, $queryId=null)
Get the SQL command to select object data from the database.
getAttributeDescriptions()
Get a list of all AttributeDescriptions.
enableProfiler()
Enable profiling.
Definition: RDBMapper.php:202
setConnectionParams($params)
Set the connection parameters.
Definition: RDBMapper.php:111
RDBMapper maps objects of one type to a relational database schema.
Definition: RDBMapper.php:49
isForeignKey($name)
Determine if an attribute is a foreign key.
EventManager is responsible for dispatching events to registered listeners.
getOIDsImpl($type, $criteria=null, $orderby=null, PagingInfo $pagingInfo=null)
Definition: RDBMapper.php:876
loadRelationImpl(array $objects, $role, $buildDepth=BuildDepth::SINGLE, $criteria=null, $orderby=null, PagingInfo $pagingInfo=null)
Definition: RDBMapper.php:1079
loadObjectsFromSQL(SelectStatement $selectStmt, $buildDepth=BuildDepth::SINGLE, PagingInfo $pagingInfo=null)
Load objects defined by a select statement.
Definition: RDBMapper.php:932
IllegalArgumentException signals an exception in method arguments.
InsertOperation holds data necessary to accomplish an insert operation on the persistent store...
getType()
Get the PersistentObject type that has the attribute.
Definition: Criteria.php:74
getValue()
Get the value to compare the object with.
Definition: Criteria.php:124
createObjectFromData(array $data)
Create an object of the mapper's type with the given attributes from the given data.
Definition: RDBMapper.php:973
getRealTableName()
Get the table name with the dbprefix added.
Definition: RDBMapper.php:306
__sleep()
Select data to be stored in the session.
Definition: RDBMapper.php:100
getNextId()
Get a new id for inserting into the database.
Definition: RDBMapper.php:234
getRelationSelectSQL(array $otherObjectProxies, $otherRole, $criteria=null, $orderby=null, PagingInfo $pagingInfo=null)
Get the SQL command to select those objects from the database that are related to the given object...
logAction(PersistentObject $obj)
Log the state of the given object.
ObjectId is the unique identifier of an object.
Definition: ObjectId.php:27
PersistenceMapper defines the interface for all mapper classes.
getUpdateSQL(PersistentObject $object)
Get the SQL command to update a object in the database.
getAttributes(array $tags=array(), $matchMode='all')
Definition: RDBMapper.php:530
static getLogger($name)
Get the logger with the given name.
Definition: LogManager.php:35
ConcurrencyManager is used to handle concurrency for objects.
getConnection()
Get the database connection.
Definition: RDBMapper.php:865
getOperator()
Get the comparison operator used to compare the given value with the attribute's value.
Definition: Criteria.php:108
loadObjectsImpl($type, $buildDepth=BuildDepth::SINGLE, $criteria=null, $orderby=null, PagingInfo $pagingInfo=null)
Definition: RDBMapper.php:894
Criteria defines a condition on a PersistentObject's attribute used to select specific instances...
Definition: Criteria.php:21
applyDataOnLoad(PersistentObject $object, array $objectData)
Apply the loaded object data to the object.
Definition: RDBMapper.php:1003
createPKCondition(ObjectId $oid)
Create an array of condition Criteria instances for the primary key values.
PagingInfo contains information about a paged list.
Definition: PagingInfo.php:18
getRelationImpl($roleName, $includeManyToMany)
Internal implementation of PersistenceMapper::getRelation()
Definition: RDBMapper.php:474
loadObjects($type, $buildDepth=BuildDepth::SINGLE, $criteria=null, $orderby=null, PagingInfo $pagingInfo=null)
getAttribute()
Get the name of the attribute.
Definition: Criteria.php:90
Message is used to get localized messages to be used in the user interface.
Definition: Message.php:23
AbstractMapper provides a basic implementation for other mapper classes.
getInsertSQL(PersistentObject $object)
Get the SQL command to insert a object into the database.
PersistentObjectProxy is proxy for an PersistentObject instance.
getType()
Get the entity type that this mapper handles.
getTableName()
Get the name of the database table, where this type is mapped to.
addRelatedObjects(array $objects, $buildDepth=BuildDepth::SINGLE)
Append the child data to a list of object.
Definition: RDBMapper.php:1036
PersistenceException signals an exception in the persistence service.
DeleteOperation holds data necessary to accomplish an delete operation on the persistent store...
initialize(array $data)
Initialize the object with a set of data.
getRelationDescriptions()
Get a list of all RelationDescriptions.
getType()
Get the type of PersistentObject on which the operation should be executed.
loadObjectsFromQueryParts($type, $buildDepth=BuildDepth::SINGLE, $criteria=null, $orderby=null, PagingInfo $pagingInfo=null)
Load objects defined by several query parts.
Definition: RDBMapper.php:912
renderCriteria(Criteria $criteria, $placeholder=null, $tableName=null, $columnName=null)
Render a Criteria instance as string.
Definition: RDBMapper.php:661
select(SelectStatement $selectStmt, PagingInfo $pagingInfo=null)
Execute a select query on the connection.
Definition: RDBMapper.php:346
PermissionManager implementations are used to handle all authorization requests.
Instances of RDBManyToManyRelationDescription describe a many to many relation from 'this' end to 'ot...
__toString()
Get a string representation of the object id.
Definition: ObjectId.php:204
static realpath($path)
Realpath function that also works for non existing paths code from http://www.php.net/manual/en/function.realpath.php.
Definition: FileUtil.php:225
getConnectionParams()
Get the connection parameters.
Definition: RDBMapper.php:123
save()
Put the statement into the cache.
getRelations($hierarchyType='all')
Definition: RDBMapper.php:437
getProfiler()
Get the profiler.
Definition: RDBMapper.php:223
isPkValue($name)
Check if a value is a primary key value.
Definition: RDBMapper.php:634
createImpl($type, $buildDepth=BuildDepth::SINGLE)
Definition: RDBMapper.php:720
Instances of ReferenceDescription describe reference attributes of PersistentObjects.
A PersistenceOperation instance holds data necessary to accomplish an operation on the persistent sto...
PersistenceFacade defines the interface for PersistenceFacade implementations.
createObject(ObjectId $oid=null)
Factory method for the supported object type.
executeSql($sql, $isSelect=false, $bindValues=array())
Execute a query on the connection.
Definition: RDBMapper.php:318
getReferences()
Get the references to other entities.
Definition: RDBMapper.php:563
getRowCount()
Execute a count query and return the row count.
getDeleteSQL(ObjectId $oid)
Get the SQL command to delete a object from the database.
applyDataOnCreate(PersistentObject $object)
Apply the default data to the object.
Definition: RDBMapper.php:1019
getType()
Get the type (including namespace)
Definition: ObjectId.php:106
loadRelation(array $objects, $role, $buildDepth=BuildDepth::SINGLE, $criteria=null, $orderby=null, PagingInfo $pagingInfo=null)
getState()
Get the object's state:
isInTransaction()
Check if the connection is currently in a transaction.
Definition: RDBMapper.php:1193
saveImpl(PersistentObject $object)
Definition: RDBMapper.php:760
Node adds the concept of relations to PersistentObject.
Definition: Node.php:34
loadImpl(ObjectId $oid, $buildDepth=BuildDepth::SINGLE)
Definition: RDBMapper.php:700
executeOperation(PersistenceOperation $operation)
Definition: RDBMapper.php:383
deleteImpl(PersistentObject $object)
Definition: RDBMapper.php:804
getPkNames()
Get the names of the primary key values.
PersistentObject defines the interface of all persistent objects.