NodeUnifiedRDBMapper.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 
32 
33 /**
34  * NodeUnifiedRDBMapper maps Node objects to a relational database schema where each Node
35  * type has its own table.
36  * The wCMFGenerator uses this class as base class for all mappers.
37  *
38  * @author ingo herwig <ingo@wemove.com>
39  */
40 abstract class NodeUnifiedRDBMapper extends RDBMapper {
41 
42  const CACHE_KEY = 'mapper';
43 
44  private $_fkRelations = null;
45 
46  /**
47  * @see RDBMapper::prepareForStorage()
48  */
49  protected function prepareForStorage(PersistentObject $object) {
50  $oldState = $object->getState();
51 
52  // set primary key values
53  $oid = $object->getOID();
54  $ids = $oid->getId();
55  $pkNames = $this->getPkNames();
56  for($i=0, $count=sizeof($pkNames); $i<$count; $i++) {
57  // foreign keys don't get a new id
58  $pkName = $pkNames[$i];
59  if (!$this->isForeignKey($pkName)) {
60  $pkValue = $ids[$i];
61  // replace dummy ids with ids provided by the database sequence
62  if (ObjectId::isDummyId($pkValue)) {
63  $nextId = $this->getNextId();
64  $object->setValue($pkName, $nextId);
65  }
66  }
67  }
68 
69  if ($oldState == PersistentObject::STATE_NEW) {
70  // set the sortkeys to the id value
71  if ($this->isSortable()) {
72  $value = join('', $object->getOID()->getId());
73  foreach ($this->getRelations() as $curRelationDesc) {
74  $sortkeyDef = $this->getSortkey($curRelationDesc->getOtherRole());
75  if ($sortkeyDef != null) {
76  $object->setValue($sortkeyDef['sortFieldName'], $value);
77  }
78  }
79  $sortkeyDef = $this->getSortkey();
80  if ($sortkeyDef != null) {
81  $object->setValue($sortkeyDef['sortFieldName'], $value);
82  }
83  }
84  }
85 
86  // handle relations
87  if ($object instanceof Node) {
88  // added nodes
89  $addedNodes = $object->getAddedNodes();
90  foreach ($addedNodes as $role => $addedNodes) {
91  $relationDesc = $this->getRelation($role);
92  // for a many to one relation, we need to update the appropriate
93  // foreign key in the object
94  if ($relationDesc instanceof RDBManyToOneRelationDescription) {
95  // in a many to one only one parent is possible
96  // so we take the last oid
97  $parent = array_pop($addedNodes);
98  $poid = $parent->getOID();
99  if (ObjectId::isValid($poid)) {
100  // set the foreign key to the parent id value
101  $fkAttr = $this->getAttribute($relationDesc->getFkName());
102  $object->setValue($fkAttr->getName(), $poid->getFirstId());
103  }
104  }
105  elseif ($relationDesc instanceof RDBManyToManyRelationDescription) {
106  // in a many to many relation we have to create the relation object
107  // if it does not exist
108  $relatives = $object->getChildrenEx(null, $relationDesc->getOtherRole());
109  foreach ($relatives as $relative) {
110  // check if the relation already exists
111  $nmObjects = $this->loadRelationObjects(PersistentObjectProxy::fromObject($object),
112  PersistentObjectProxy::fromObject($relative), $relationDesc, true);
113  if (sizeof($nmObjects) == 0) {
114  $thisEndRelation = $relationDesc->getThisEndRelation();
115  $otherEndRelation = $relationDesc->getOtherEndRelation();
116  $nmType = $thisEndRelation->getOtherType();
117  $nmObj = $this->_persistenceFacade->create($nmType);
118  // add the parent nodes to the many to many object, don't
119  // update the other side of the relation, because there may be no
120  // relation defined to the many to many object
121  $nmObj->addNode($object, $thisEndRelation->getThisRole(), true, false, false);
122  $nmObj->addNode($relative, $otherEndRelation->getOtherRole(), true, false, false);
123  }
124  }
125  }
126  }
127 
128  // deleted nodes
129  $deletedNodes = $object->getDeletedNodes();
130  foreach ($deletedNodes as $role => $oids) {
131  $relationDesc = $this->getRelation($role);
132  // for a many to one relation, we need to update the appropriate
133  // foreign key in the object
134  if ($relationDesc instanceof RDBManyToOneRelationDescription) {
135  // in a many to one only one parent is possible
136  // so we take the last oid
137  $poid = array_pop($oids);
138  if (ObjectId::isValid($poid)) {
139  // set the foreign key to null
140  $fkAttr = $this->getAttribute($relationDesc->getFkName());
141  $object->setValue($fkAttr->getName(), null);
142  }
143  }
144  elseif ($relationDesc instanceof RDBManyToManyRelationDescription) {
145  // in a many to many relation we have to delete the relation object
146  // if it does exist
147  foreach ($oids as $relativeOid) {
148  // check if the relation exists
149  $nmObjects = $this->loadRelationObjects(PersistentObjectProxy::fromObject($object),
150  new PersistentObjectProxy($relativeOid), $relationDesc);
151  foreach ($nmObjects as $nmObj) {
152  // delete the relation
153  $nmObj->delete();
154  }
155  }
156  }
157  }
158 
159  // changed order
160  $orderedNodes = $object->getNodeOrder();
161  // order changes only make sense for arrays with more than one element
162  if (sizeof($orderedNodes) > 1) {
163  $relatives = array();
164  $sortkeyValues = array();
165  $orderDirection = null;
166  foreach ($orderedNodes as $curNode) {
167  // find the role of the node
168  $curRelationDesc = $object->getNodeRelation($curNode);
169  if ($curRelationDesc != null) {
170  // find the sortkey
171  $otherMapper = $curRelationDesc->getOtherMapper();
172  $sortkeyDef = $otherMapper->getSortkey($curRelationDesc->getThisRole());
173  if ($sortkeyDef != null) {
174  $sortkeyName = $sortkeyDef['sortFieldName'];
175  // take the order direction from the first node
176  // (different order directions do not make sense)
177  $orderDirection = $orderDirection == null ? $sortkeyDef['sortDirection'] : $orderDirection;
178  // in a many to many relation, we have to modify the order of the relation objects
179  if ($curRelationDesc instanceof RDBManyToManyRelationDescription) {
180  $nmObjects = $this->loadRelationObjects(PersistentObjectProxy::fromObject($object),
181  PersistentObjectProxy::fromObject($curNode), $curRelationDesc);
182  $curNode = $nmObjects[0];
183  }
184  // collect the objects and sortkey definitions
185  $relatives[] = array('object' => $curNode, 'sortFieldName' => $sortkeyName);
186  // collect the sortkey values
187  $sortkeyValues[] = $curNode->getValue($sortkeyName);
188  }
189  }
190  }
191  // sort the values
192  $sortFuntion = $orderDirection == 'DESC' ? 'rsort' : 'sort';
193  $sortFuntion($sortkeyValues);
194  // set the values on the objects
195  for ($i=0, $count=sizeof($relatives); $i<$count; $i++) {
196  $curRelative = $relatives[$i];
197  $curRelative['object']->setValue($curRelative['sortFieldName'], $sortkeyValues[$i]);
198  }
199  }
200  }
201  $object->setState($oldState);
202  }
203 
204  /**
205  * @see RDBMapper::getSelectSQL()
206  */
207  public function getSelectSQL($criteria=null, $alias=null, $orderby=null, PagingInfo $pagingInfo=null, $queryId=null) {
208  // use own query id, if none is given
209  $queryId = $queryId == null ? $this->getCacheKey($alias, $criteria, $orderby, $pagingInfo) : $queryId;
210 
211  $selectStmt = SelectStatement::get($this, $queryId);
212  if (!$selectStmt->isCached()) {
213  // initialize the statement
214 
215  // table
216  $tableName = $this->getRealTableName();
217  if ($alias != null) {
218  $selectStmt->from(array($alias => $tableName), '');
219  $tableName = $alias;
220  }
221  else {
222  $selectStmt->from($tableName, '');
223  }
224 
225  // columns
226  $this->addColumns($selectStmt, $tableName);
227 
228  // condition
229  $bind = $this->addCriteria($selectStmt, $criteria, $tableName);
230 
231  // order
232  $this->addOrderBy($selectStmt, $orderby, $tableName, $this->getDefaultOrder());
233 
234  // limit
235  if ($pagingInfo != null) {
236  $selectStmt->limit($pagingInfo->getPageSize(), $pagingInfo->getOffset());
237  }
238  }
239  else {
240  // on used statements only set bind
241  $tableName = $alias != null ? $alias : $this->getRealTableName();
242  $bind = $this->getBind($criteria, $tableName);
243  }
244 
245  // set parameters
246  $selectStmt->bind($bind);
247  return $selectStmt;
248  }
249 
250  /**
251  * @see RDBMapper::getRelationSelectSQL()
252  */
253  protected function getRelationSelectSQL(array $otherObjectProxies,
254  $otherRole, $criteria=null, $orderby=null, PagingInfo $pagingInfo=null) {
255  $relationDescription = $this->getRelationImpl($otherRole, true);
256  if ($relationDescription instanceof RDBManyToOneRelationDescription) {
257  return $this->getManyToOneRelationSelectSQL($relationDescription,
258  $otherObjectProxies, $otherRole, $criteria, $orderby, $pagingInfo);
259  }
260  elseif ($relationDescription instanceof RDBOneToManyRelationDescription) {
261  return $this->getOneToManyRelationSelectSQL($relationDescription,
262  $otherObjectProxies, $otherRole, $criteria, $orderby, $pagingInfo);
263  }
264  elseif ($relationDescription instanceof RDBManyToManyRelationDescription) {
265  return $this->getManyToManyRelationSelectSQL($relationDescription,
266  $otherObjectProxies, $otherRole, $criteria, $orderby, $pagingInfo);
267  }
268  throw new IllegalArgumentException("Unknown RelationDescription for role: ".$otherRole);
269  }
270 
271  /**
272  * Get the statement for selecting a many to one relation
273  * @see RDBMapper::getRelationSelectSQL()
274  */
275  protected function getManyToOneRelationSelectSQL(RelationDescription $relationDescription,
276  array $otherObjectProxies, $otherRole, $criteria=null, $orderby=null,
277  PagingInfo $pagingInfo=null) {
278  $thisAttr = $this->getAttribute($relationDescription->getFkName());
279  $tableName = $this->getRealTableName();
280 
281  // id bind parameters
282  $bind = array();
283  $idPlaceholder = ':'.$tableName.'_'.$thisAttr->getName();
284  for ($i=0, $count=sizeof($otherObjectProxies); $i<$count; $i++) {
285  $dbid = $otherObjectProxies[$i]->getValue($relationDescription->getIdName());
286  if ($dbid === null) {
287  $dbid = SQLConst::NULL();
288  }
289  $bind[$idPlaceholder.$i] = $dbid;
290  }
291 
292  // statement
293  $queryId = $this->getCacheKey($otherRole.sizeof($otherObjectProxies), $criteria, $orderby, $pagingInfo);
294  $selectStmt = SelectStatement::get($this, $queryId);
295  if (!$selectStmt->isCached()) {
296  // initialize the statement
297 
298  $selectStmt->from($tableName, '');
299  $this->addColumns($selectStmt, $tableName);
300  $selectStmt->where($this->quoteIdentifier($tableName).'.'.
301  $this->quoteIdentifier($thisAttr->getName()).' IN('.join(',', array_keys($bind)).')');
302  // order
303  $this->addOrderBy($selectStmt, $orderby, $tableName, $this->getDefaultOrder($otherRole));
304  // additional conditions
305  $bind = array_merge($bind, $this->addCriteria($selectStmt, $criteria, $tableName));
306  // limit
307  if ($pagingInfo != null) {
308  $selectStmt->limit($pagingInfo->getPageSize(), $pagingInfo->getOffset());
309  }
310  }
311  else {
312  // on used statements only set bind
313  $bind = array_merge($bind, $this->getBind($criteria, $tableName));
314  }
315 
316  // set parameters
317  $selectStmt->bind($bind);
318  return array($selectStmt, $relationDescription->getIdName(), $relationDescription->getFkName());
319  }
320 
321  /**
322  * Get the statement for selecting a one to many relation
323  * @see RDBMapper::getRelationSelectSQL()
324  */
325  protected function getOneToManyRelationSelectSQL(RelationDescription $relationDescription,
326  array $otherObjectProxies, $otherRole, $criteria=null, $orderby=null,
327  PagingInfo $pagingInfo=null) {
328  $thisAttr = $this->getAttribute($relationDescription->getIdName());
329  $tableName = $this->getRealTableName();
330 
331  // id bind parameters
332  $bind = array();
333  $idPlaceholder = ':'.$tableName.'_'.$thisAttr->getName();
334  for ($i=0, $count=sizeof($otherObjectProxies); $i<$count; $i++) {
335  $fkValue = $otherObjectProxies[$i]->getValue($relationDescription->getFkName());
336  if ($fkValue === null) {
337  $fkValue = SQLConst::NULL();
338  }
339  $bind[$idPlaceholder.$i] = $fkValue;
340  }
341 
342  // statement
343  $queryId = $this->getCacheKey($otherRole.sizeof($otherObjectProxies), $criteria, $orderby, $pagingInfo);
344  $selectStmt = SelectStatement::get($this, $queryId);
345  if (!$selectStmt->isCached()) {
346  // initialize the statement
347 
348  $selectStmt->from($tableName, '');
349  $this->addColumns($selectStmt, $tableName);
350  $selectStmt->where($this->quoteIdentifier($tableName).'.'.
351  $this->quoteIdentifier($thisAttr->getName()).' IN('.join(',', array_keys($bind)).')');
352  // order
353  $this->addOrderBy($selectStmt, $orderby, $tableName, $this->getDefaultOrder($otherRole));
354  // additional conditions
355  $bind = array_merge($bind, $this->addCriteria($selectStmt, $criteria, $tableName));
356  // limit
357  if ($pagingInfo != null) {
358  $selectStmt->limit($pagingInfo->getPageSize(), $pagingInfo->getOffset());
359  }
360  }
361  else {
362  // on used statements only set bind
363  $bind = array_merge($bind, $this->getBind($criteria, $tableName));
364  }
365 
366  // set parameters
367  $selectStmt->bind($bind);
368  return array($selectStmt, $relationDescription->getFkName(), $relationDescription->getIdName());
369  }
370 
371  /**
372  * Get the statement for selecting a many to many relation
373  * @see RDBMapper::getRelationSelectSQL()
374  */
375  protected function getManyToManyRelationSelectSQL(RelationDescription $relationDescription,
376  array $otherObjectProxies, $otherRole, $criteria=null, $orderby=null,
377  PagingInfo $pagingInfo=null) {
378  $thisRelationDesc = $relationDescription->getThisEndRelation();
379  $otherRelationDesc = $relationDescription->getOtherEndRelation();
380  $nmMapper = $this->_persistenceFacade->getMapper($thisRelationDesc->getOtherType());
381  $otherFkAttr = $nmMapper->getAttribute($otherRelationDesc->getFkName());
382  $nmTablename = $nmMapper->getRealTableName();
383 
384  // id bind parameters
385  $bind = array();
386  $idPlaceholder = ':'.$nmTablename.'_'.$otherFkAttr->getName();
387  for ($i=0, $count=sizeof($otherObjectProxies); $i<$count; $i++) {
388  $dbid = $otherObjectProxies[$i]->getValue($thisRelationDesc->getIdName());
389  if ($dbid === null) {
390  $dbid = SQLConst::NULL();
391  }
392  $bind[$idPlaceholder.$i] = $dbid;
393  }
394 
395  // statement
396  $queryId = $this->getCacheKey($otherRole.sizeof($otherObjectProxies), $criteria, $orderby, $pagingInfo);
397  $selectStmt = SelectStatement::get($this, $queryId);
398  if (!$selectStmt->isCached()) {
399  // initialize the statement
400 
401  $thisFkAttr = $nmMapper->getAttribute($thisRelationDesc->getFkName());
402  $thisIdAttr = $this->getAttribute($thisRelationDesc->getIdName());
403 
404  $tableName = $this->getRealTableName();
405  $selectStmt->from($tableName, '');
406  $this->addColumns($selectStmt, $tableName);
407  $joinCond = $this->quoteIdentifier($nmTablename).'.'.$this->quoteIdentifier($thisFkAttr->getName()).'='.
408  $this->quoteIdentifier($tableName).'.'.$this->quoteIdentifier($thisIdAttr->getName());
409  $selectStmt->join($nmTablename, $joinCond, array());
410  $selectStmt->where($this->quoteIdentifier($nmTablename).'.'.
411  $this->quoteIdentifier($otherFkAttr->getName()).' IN('.join(',', array_keys($bind)).')');
412  // order (in this case we use the order of the many to many objects)
413  $nmSortDefs = $nmMapper->getDefaultOrder($otherRole);
414  $this->addOrderBy($selectStmt, $orderby, $nmTablename, $nmSortDefs);
415  foreach($nmSortDefs as $nmSortDef) {
416  // add the sort attribute from the many to many object
417  $nmSortAttributeDesc = $nmMapper->getAttribute($nmSortDef['sortFieldName']);
418  $selectStmt->columns(array($nmSortAttributeDesc->getName() => $nmSortAttributeDesc->getColumn()), $nmTablename);
419  }
420  // add proxy id
421  $selectStmt->columns(array(self::INTERNAL_VALUE_PREFIX.'id' => $otherFkAttr->getName()), $nmTablename);
422  // additional conditions
423  $bind = array_merge($bind, $this->addCriteria($selectStmt, $criteria, $nmTablename));
424  // limit
425  if ($pagingInfo != null) {
426  $selectStmt->limit($pagingInfo->getPageSize(), $pagingInfo->getOffset());
427  }
428  }
429  else {
430  // on used statements only set bind
431  $bind = array_merge($bind, $this->getBind($criteria, $nmTablename));
432  }
433 
434  // set parameters
435  $selectStmt->bind($bind);
436  return array($selectStmt, $thisRelationDesc->getIdName(), self::INTERNAL_VALUE_PREFIX.'id');
437  }
438 
439  /**
440  * @see RDBMapper::getInsertSQL()
441  */
442  protected function getInsertSQL(PersistentObject $object) {
443  // get the attributes to store
444  $values = $this->convertValuesForStorage($this->getPersistentValues($object));
445 
446  // operations
447  $insertOp = new InsertOperation($this->getType(), $values);
448  $operations = array(
449  $insertOp
450  );
451  return $operations;
452  }
453 
454  /**
455  * @see RDBMapper::getUpdateSQL()
456  */
457  protected function getUpdateSQL(PersistentObject $object) {
458  // get the attributes to store
459  $values = $this->convertValuesForStorage($this->getPersistentValues($object));
460 
461  // primary key definition
462  $pkCriteria = $this->createPKCondition($object->getOID());
463 
464  // operations
465  $updateOp = new UpdateOperation($this->getType(), $values, $pkCriteria);
466  $operations = array(
467  $updateOp
468  );
469  return $operations;
470  }
471 
472  /**
473  * @see RDBMapper::getDeleteSQL()
474  */
475  protected function getDeleteSQL(ObjectId $oid) {
476  // primary key definition
477  $pkCriteria = $this->createPKCondition($oid);
478 
479  // operations
480  $deleteOp = new DeleteOperation($this->getType(), $pkCriteria);
481  $operations = array(
482  $deleteOp
483  );
484  return $operations;
485  }
486 
487  /**
488  * Add the columns to a given select statement.
489  * @param $selectStmt The select statement (instance of SelectStatement)
490  * @param $tableName The table name
491  * @return SelectStatement
492  */
493  protected function addColumns(SelectStatement $selectStmt, $tableName) {
494  // columns
495  $attributeDescs = $this->getAttributes();
496  foreach($attributeDescs as $curAttributeDesc) {
497  if (!($curAttributeDesc instanceof ReferenceDescription)) {
498  $selectStmt->columns(array($curAttributeDesc->getName() => $curAttributeDesc->getColumn()), $tableName);
499  }
500  }
501 
502  // references
503  $selectStmt = $this->addReferences($selectStmt, $tableName);
504  return $selectStmt;
505  }
506 
507  /**
508  * Add the columns and joins to select references to a given select statement.
509  * @param $selectStmt The select statement (instance of SelectStatement)
510  * @param $tableName The name for this table (the alias, if used).
511  * @return SelectStatement
512  */
513  protected function addReferences(SelectStatement $selectStmt, $tableName) {
514  // collect all references first
515  $references = array();
516  foreach($this->getReferences() as $curReferenceDesc) {
517  $referencedType = $curReferenceDesc->getOtherType();
518  $referencedValue = $curReferenceDesc->getOtherName();
519  $relationDesc = $this->getRelation($referencedType);
520  $otherMapper = $this->_persistenceFacade->getMapper($relationDesc->getOtherType());
521  if ($otherMapper) {
522  $otherTable = $otherMapper->getRealTableName();
523  $otherAttributeDesc = $otherMapper->getAttribute($referencedValue);
524  if ($otherAttributeDesc instanceof RDBAttributeDescription) {
525  // set up the join definition if not already defined
526  if (!isset($references[$otherTable])) {
527  $references[$otherTable] = array();
528  $references[$otherTable]['attributes'] = array();
529 
530  $tableNameQ = $this->quoteIdentifier($tableName);
531  $otherTableQ = $this->quoteIdentifier($otherTable);
532 
533  // determine the join condition
534  if ($relationDesc instanceof RDBManyToOneRelationDescription) {
535  // reference from parent
536  $thisAttrNameQ = $this->quoteIdentifier($this->getAttribute($relationDesc->getFkName())->getColumn());
537  $otherAttrNameQ = $this->quoteIdentifier($otherMapper->getAttribute($relationDesc->getIdName())->getColumn());
538  $additionalCond = "";
539  }
540  else if ($relationDesc instanceof RDBOneToManyRelationDescription) {
541  // reference from child
542  $thisAttrNameQ = $this->quoteIdentifier($this->getAttribute($relationDesc->getIdName())->getColumn());
543  $otherAttrNameQ = $this->quoteIdentifier($otherMapper->getAttribute($relationDesc->getFkName())->getColumn());
544  $otherPkNames = $otherMapper->getPkNames();
545  $otherPkNameQ = $this->quoteIdentifier($otherMapper->getAttribute($otherPkNames[0])->getColumn());
546  $additionalCond = " AND ".$otherTableQ.".".$otherPkNameQ.
547  " = (SELECT MIN(".$otherTableQ.".".$otherPkNameQ.") FROM ".$otherTableQ.
548  " WHERE ".$otherTableQ.".".$otherAttrNameQ."=".$tableNameQ.".".$thisAttrNameQ.")";
549  }
550  $joinCond = $tableNameQ.".".$thisAttrNameQ."=".$otherTableQ.".".$otherAttrNameQ;
551  if (strlen($additionalCond) > 0) {
552  $joinCond = "(".$joinCond.$additionalCond.")";
553  }
554  $references[$otherTable]['joinCond'] = $joinCond;
555  }
556 
557  // add the attributes
558  $references[$otherTable]['attributes'][$curReferenceDesc->getName()] = $otherAttributeDesc->getColumn();
559  }
560  }
561  }
562  // add references from each referenced table
563  foreach($references as $otherTable => $curReference) {
564  $selectStmt->joinLeft($otherTable, $curReference['joinCond'], $curReference['attributes']);
565  }
566  return $selectStmt;
567  }
568 
569  /**
570  * Add the given criteria to the select statement
571  * @param $selectStmt The select statement (instance of SelectStatement)
572  * @param $criteria An array of Criteria instances that define conditions on the object's attributes (maybe null)
573  * @param $tableName The table name
574  * @return Array of placeholder value pairs for bind
575  */
576  protected function addCriteria(SelectStatement $selectStmt, $criteria, $tableName) {
577  $bind = array();
578  if ($criteria != null) {
579  foreach ($criteria as $criterion) {
580  if ($criterion instanceof Criteria) {
581  $placeholder = ':'.$tableName.'_'.$criterion->getAttribute();
582  $condition = $this->renderCriteria($criterion, $placeholder, $tableName);
583  if ($criterion->getCombineOperator() == Criteria::OPERATOR_AND) {
584  $selectStmt->where($condition);
585  }
586  else {
587  $selectStmt->orWhere($condition);
588  }
589  $bind[$placeholder] = $criterion->getValue();
590  }
591  else {
592  throw new IllegalArgumentException("The select condition must be an instance of Criteria");
593  }
594  }
595  }
596  return $bind;
597  }
598 
599  /**
600  * Add the given order to the select statement
601  * @param $selectStmt The select statement (instance of SelectStatement)
602  * @param $orderby An array holding names of attributes to order by, maybe appended with 'ASC', 'DESC' (maybe null)
603  * @param $tableName The table name
604  * @param $defaultOrder The default order definition to use, if orderby is null (@see PersistenceMapper::getDefaultOrder())
605  */
606  protected function addOrderBy(SelectStatement $selectStmt, $orderby, $tableName, $defaultOrder) {
607  $orderbyFinal = array();
608  if ($orderby == null) {
609  $orderby = array();
610  // use default ordering
611  if ($defaultOrder && sizeof($defaultOrder) > 0) {
612  foreach ($defaultOrder as $orderDef) {
613  $orderby[] = $orderDef['sortFieldName']." ".$orderDef['sortDirection'];
614  }
615  }
616  }
617  foreach($orderby as $orderExpression) {
618  $orderbyFinal[] = $this->ensureTablePrefix($orderExpression, $tableName);
619  }
620  if (sizeof($orderby) > 0) {
621  $selectStmt->order($orderbyFinal);
622  }
623  }
624 
625  /**
626  * Get an array of placeholder value pairs for bind
627  * @param $criteria An array of Criteria instances that define conditions on the object's attributes (maybe null)
628  * @param $tableName The table name
629  * @return Array of placeholder value pairs for bind
630  */
631  protected function getBind($criteria, $tableName) {
632  $bind = array();
633  if ($criteria != null) {
634  foreach ($criteria as $criterion) {
635  if ($criterion instanceof Criteria) {
636  $placeholder = ':'.$tableName.'_'.$criterion->getAttribute();
637  $bind[$placeholder] = $criterion->getValue();
638  }
639  else {
640  throw new IllegalArgumentException("The select condition must be an instance of Criteria");
641  }
642  }
643  }
644  return $bind;
645  }
646 
647  /**
648  * Get an associative array of attribute name-value pairs to be stored for a
649  * given oject (references are not included)
650  * @param $object The PeristentObject.
651  * @return Associative array
652  */
653  protected function getPersistentValues(PersistentObject $object) {
654  $values = array();
655 
656  // attribute definitions
657  $attributeDescs = $this->getAttributes();
658  foreach($attributeDescs as $curAttributeDesc) {
659  if (!($curAttributeDesc instanceof ReferenceDescription)) {
660  // add only attributes that are defined in the object
661  $attribName = $curAttributeDesc->getName();
662  //if ($object->hasValue($attribName)) {
663  $values[$attribName] = $object->getValue($attribName);
664  //}
665  }
666  }
667  return $values;
668  }
669 
670  /**
671  * Convert values for before storage
672  * @param $values Associative Array
673  * @return Associative Array
674  */
675  protected function convertValuesForStorage($values) {
676  // filter values according to type
677  foreach($values as $valueName => $value) {
678  $type = $this->getAttribute($valueName)->getType();
679  // integer
680  if (strpos(strtolower($type), 'int') === 0) {
681  $value = (strlen($value) == 0) ? null : intval($value);
682  $values[$valueName] = $value;
683  }
684  // null values
685  if ($value === null) {
686  $values[$valueName] = SQLConst::NULL();
687  }
688  }
689  return $values;
690  }
691 
692  /**
693  * Load the relation objects in a many to many relation from the database.
694  * @param $objectProxy The proxy at this end of the relation.
695  * @param $relativeProxy The proxy at the other end of the relation.
696  * @param $relationDesc The RDBManyToManyRelationDescription instance describing the relation.
697  * @param $includeTransaction Boolean whether to also search in the current transaction (default: false)
698  * @return Array of PersistentObject instances
699  */
700  protected function loadRelationObjects(PersistentObjectProxy $objectProxy,
701  PersistentObjectProxy $relativeProxy, RDBManyToManyRelationDescription $relationDesc,
702  $includeTransaction=false) {
703  $nmMapper = $this->_persistenceFacade->getMapper($relationDesc->getThisEndRelation()->getOtherType());
704  $nmType = $nmMapper->getType();
705 
706  $thisId = $objectProxy->getOID()->getFirstId();
707  $otherId = $relativeProxy->getOID()->getFirstId();
708  $thisEndRelation = $relationDesc->getThisEndRelation();
709  $otherEndRelation = $relationDesc->getOtherEndRelation();
710  $thisFkAttr = $nmMapper->getAttribute($thisEndRelation->getFkName());
711  $otherFkAttr = $nmMapper->getAttribute($otherEndRelation->getFkName());
712 
713  $criteria1 = new Criteria($nmType, $thisFkAttr->getName(), "=", $thisId);
714  $criteria2 = new Criteria($nmType, $otherFkAttr->getName(), "=", $otherId);
715  $criteria = array($criteria1, $criteria2);
716  $nmObjects = $nmMapper->loadObjects($nmType, BuildDepth::SINGLE, $criteria);
717 
718  if ($includeTransaction) {
719  $transaction = $this->_persistenceFacade->getTransaction();
720  $objects = $transaction->getObjects();
721  foreach ($objects as $object) {
722  if ($object->getType() == $nmType && $object instanceof Node) {
723  // we expect single valued relation ends
724  $thisEndObject = $object->getValue($thisEndRelation->getThisRole());
725  $otherEndObject = $object->getValue($otherEndRelation->getOtherRole());
726  if ($objectProxy->getRealSubject() == $thisEndObject &&
727  $relativeProxy->getRealSubject() == $otherEndObject) {
728  $nmObjects[] = $object;
729  }
730  }
731  }
732  }
733  return $nmObjects;
734  }
735 
736  /**
737  * @see RDBMapper::createPKCondition()
738  */
739  protected function createPKCondition(ObjectId $oid) {
740  $criterias = array();
741  $type = $this->getType();
742  $pkNames = $this->getPKNames();
743  $ids = $oid->getId();
744  for ($i=0, $count=sizeof($pkNames); $i<$count; $i++) {
745  $pkValue = $ids[$i];
746  $criterias[] = new Criteria($type, $pkNames[$i], "=", $pkValue);
747  }
748  return $criterias;
749  }
750 
751  /**
752  * Get all foreign key relations (used to reference a parent)
753  * @return An array of RDBManyToOneRelationDescription instances
754  */
755  protected function getForeignKeyRelations() {
756  if ($this->_fkRelations == null) {
757  $this->_fkRelations = array();
758  $relationDescs = $this->getRelations();
759  foreach($relationDescs as $relationDesc) {
760  if ($relationDesc instanceof RDBManyToOneRelationDescription) {
761  $this->_fkRelations[] = $relationDesc;
762  }
763  }
764  }
765  return $this->_fkRelations;
766  }
767 
768  /**
769  * Check if a given attribute is a foreign key (used to reference a parent)
770  * @param $name The attribute name
771  * @return Boolean
772  * @note Public in order to be callable by ObjectQuery
773  */
774  public function isForeignKey($name) {
775  $fkDescs = $this->getForeignKeyRelations();
776  foreach($fkDescs as $fkDesc) {
777  if ($fkDesc->getFkName() == $name) {
778  return true;
779  }
780  }
781  return false;
782  }
783 
784  /**
785  * Make sure that the given table name is prefixed before the given expression
786  * and return the modified expression.
787  * @param $expression The expression
788  * @param $tableName The table name
789  * @return String
790  */
791  protected function ensureTablePrefix($expression, $tableName) {
792  if (strpos($expression, '.') === false) {
793  $expression = $tableName.".".$expression;
794  }
795  return $expression;
796  }
797 
798  /**
799  * Get a unique string for the given parameter values
800  * @param $string
801  * @param $criteriaArray
802  * @param $stringArray
803  * @param $pagingInfo
804  * @return String
805  */
806  protected function getCacheKey($string, $criteriaArray, $stringArray, PagingInfo $pagingInfo=null) {
807  $result = $this->getRealTableName().','.$string.',';
808  if ($criteriaArray != null) {
809  foreach ($criteriaArray as $c) {
810  $result .= $c->getId();
811  }
812  }
813  if ($stringArray != null) {
814  $result .= join(',', $stringArray);
815  }
816  if ($pagingInfo != null) {
817  $result .= $pagingInfo->getOffset().','.$pagingInfo->getPageSize();
818  }
819  return $result;
820  }
821 }
822 ?>
getDefaultOrder($roleName=null)
Definition: RDBMapper.php:605
static fromObject($object)
Create a PersistenceProxy instance from a PersistentObject.
setState($state)
Set the state of the object to one of the STATE constants.
Instances of RDBAttributeDescription describe attributes of PersistentObjects in a relational databas...
getOID()
Get the object id of the PersistentObject.
UpdateOperation instances hold data necessary to accomplish an update operation on the persistent sto...
getOtherEndRelation()
Get the RDBManyToOneRelationDescription describing the relation between the connecting type and the '...
convertValuesForStorage($values)
Convert values for before storage.
getThisEndRelation()
Get the RDBOneToManyRelationDescription describing the relation between 'this' end and the connecting...
getManyToOneRelationSelectSQL(RelationDescription $relationDescription, array $otherObjectProxies, $otherRole, $criteria=null, $orderby=null, PagingInfo $pagingInfo=null)
Get the statement for selecting a many to one relation.
addReferences(SelectStatement $selectStmt, $tableName)
Add the columns and joins to select references to a given select statement.
RDBMapper maps objects of one type to a relational database schema.
Definition: RDBMapper.php:49
static NULL()
Get the NULL expression.
Definition: SQLConst.php:29
IllegalArgumentException signals an exception in method arguments.
InsertOperation holds data necessary to accomplish an insert operation on the persistent store...
isForeignKey($name)
Check if a given attribute is a foreign key (used to reference a parent)
getRealTableName()
Get the table name with the dbprefix added.
Definition: RDBMapper.php:306
getOID()
Get the object id of the PersistentObject.
getSelectSQL($criteria=null, $alias=null, $orderby=null, PagingInfo $pagingInfo=null, $queryId=null)
getNextId()
Get a new id for inserting into the database.
Definition: RDBMapper.php:234
static get(RDBMapper $mapper, $id=self::NO_CACHE)
Get the SelectStatement instance with the given id.
ObjectId is the unique identifier of an object.
Definition: ObjectId.php:27
getAttributes(array $tags=array(), $matchMode='all')
Definition: RDBMapper.php:530
getRealSubject()
Get the PersistentObject instance.
getPersistentValues(PersistentObject $object)
Get an associative array of attribute name-value pairs to be stored for a given oject (references are...
Criteria defines a condition on a PersistentObject's attribute used to select specific instances...
Definition: Criteria.php:21
PagingInfo contains information about a paged list.
Definition: PagingInfo.php:18
getRelationImpl($roleName, $includeManyToMany)
Internal implementation of PersistenceMapper::getRelation()
Definition: RDBMapper.php:474
addColumns(SelectStatement $selectStmt, $tableName)
Add the columns to a given select statement.
addOrderBy(SelectStatement $selectStmt, $orderby, $tableName, $defaultOrder)
Add the given order to the select statement.
getForeignKeyRelations()
Get all foreign key relations (used to reference a parent)
static isDummyId($id)
Check if a given id is a dummy id.
Definition: ObjectId.php:228
Instances of RDBManyToOneRelationDescription describe a many to one relation from 'this' end (many) t...
PersistentObjectProxy is proxy for an PersistentObject instance.
getManyToManyRelationSelectSQL(RelationDescription $relationDescription, array $otherObjectProxies, $otherRole, $criteria=null, $orderby=null, PagingInfo $pagingInfo=null)
Get the statement for selecting a many to many relation.
getType()
Get the entity type that this mapper handles.
getValue($name)
Get the value of a named item.
DeleteOperation holds data necessary to accomplish an delete operation on the persistent store...
addCriteria(SelectStatement $selectStmt, $criteria, $tableName)
Add the given criteria to the select statement.
getBind($criteria, $tableName)
Get an array of placeholder value pairs for bind.
renderCriteria(Criteria $criteria, $placeholder=null, $tableName=null, $columnName=null)
Render a Criteria instance as string.
Definition: RDBMapper.php:661
Instances of RDBManyToManyRelationDescription describe a many to many relation from 'this' end to 'ot...
getRelations($hierarchyType='all')
Definition: RDBMapper.php:437
Instances of RelationDescription describe relations between different types of PersistentObjects.
NodeUnifiedRDBMapper maps Node objects to a relational database schema where each Node type has its o...
static isValid($oid)
Check if a serialized ObjectId has a valid syntax, the type is known and if the number of primary key...
Definition: ObjectId.php:132
setValue($name, $value, $forceSet=false, $trackChange=true)
Set the value of a named item if it exists.
ensureTablePrefix($expression, $tableName)
Make sure that the given table name is prefixed before the given expression and return the modified e...
getOneToManyRelationSelectSQL(RelationDescription $relationDescription, array $otherObjectProxies, $otherRole, $criteria=null, $orderby=null, PagingInfo $pagingInfo=null)
Get the statement for selecting a one to many relation.
loadRelationObjects(PersistentObjectProxy $objectProxy, PersistentObjectProxy $relativeProxy, RDBManyToManyRelationDescription $relationDesc, $includeTransaction=false)
Load the relation objects in a many to many relation from the database.
Instances of ReferenceDescription describe reference attributes of PersistentObjects.
getReferences()
Get the references to other entities.
Definition: RDBMapper.php:563
Instances of RDBOneToManyRelationDescription describe a one to many relation from 'this' end (one) to...
getState()
Get the object's state:
getCacheKey($string, $criteriaArray, $stringArray, PagingInfo $pagingInfo=null)
Get a unique string for the given parameter values.
getRelationSelectSQL(array $otherObjectProxies, $otherRole, $criteria=null, $orderby=null, PagingInfo $pagingInfo=null)
Node adds the concept of relations to PersistentObject.
Definition: Node.php:34
getPkNames()
Get the names of the primary key values.
PersistentObject defines the interface of all persistent objects.