wCMF  3.6
 All Classes Namespaces Files Functions Variables Groups Pages
class.NodeUnifiedRDBMapper.php
Go to the documentation of this file.
1 <?php
2 /**
3  * wCMF - wemove Content Management Framework
4  * Copyright (C) 2005-2014 wemove digital solutions GmbH
5  *
6  * Licensed under the terms of any of the following licenses
7  * at your choice:
8  *
9  * - GNU Lesser General Public License (LGPL)
10  * http://www.gnu.org/licenses/lgpl.html
11  * - Eclipse Public License (EPL)
12  * http://www.eclipse.org/org/documents/epl-v10.php
13  *
14  * See the license.txt file distributed with this work for
15  * additional information.
16  *
17  * $Id: class.NodeUnifiedRDBMapper.php 1462 2014-02-04 23:52:27Z iherwig $
18  */
19 require_once(BASE."wcmf/lib/util/class.Message.php");
20 require_once(BASE."wcmf/lib/util/class.StringUtil.php");
21 require_once(BASE."wcmf/lib/model/class.Node.php");
22 require_once(BASE."wcmf/lib/model/mapper/class.NodeRDBMapper.php");
23 
24 /**
25  * @class NodeUnifiedRDBMapper
26  * @ingroup Mapper
27  * @brief NodeUnifiedRDBMapper maps Node objects to a relational database schema where each Node
28  * type has its own table.
29  * In comparison to NodeRDBMapper it implements almost all template methods from the
30  * base classes and defines the sql queries. The newly defined template methods make it easier
31  * for application developers to implement own mapper subclasses. The wCMFGenerator uses this class
32  * as base class for all mappers.
33  * Besides some methods that define table/column names, the main effort lies in implementing the
34  * getObjectDefinitionImpl() method wich needs the following extra definitions:
35  * - extra key _refs see NodeUnifiedRDBMapper::getObjectDefinitionImpl()
36  * - in _datadef array the following keys are added:
37  * - column_name The name of the database column
38  * - in _parents array the following keys are added:
39  * - is_navigable True/False wether the parent is navigable from this object or not
40  * - table_name The name of the database table of the parent
41  * - pk_columns An array of the name(s) of the primary key columns of the parent table
42  * - fk_columns The name of the foreign key column that point from the object's table to the parent table (compound foreign keys are not supported yet)
43  * - in _children array the following keys are added:
44  * - is_navigable True/False wether the child is navigable from this object or not
45  * - table_name The name of the database table
46  * - pk_columns An array of the name(s) of the primary key columns of the child table
47  * - fk_column The name of the foreign key column that point from the child table to the object's table (compound foreign keys are not supported yet)
48  * - order_by An array of columns to sort by default
49  *
50  * @author ingo herwig <ingo@wemove.com>
51  */
53 {
54  /**
55  * @see PersistenceMapper::initialize()
56  */
57  function initialize(&$object)
58  {
59  // add this as ChangeListener
60  if ($object != null) {
61  $object->addChangeListener($this);
62  }
63  }
64 
65  /**
66  * @see RDBMapper::prepareInsert()
67  */
68  function prepareInsert(&$object)
69  {
70  $oid = $object->getOID();
71  $oidParts = PersistenceFacade::decomposeOID($oid);
72  $pkValues = $this->getPKNames();
73  $pkColumns = $this->getPKColumnNames();
74  for($i=0; $i<sizeof($pkColumns); $i++)
75  {
76  // foreign keys don't get a new id
77  $pkColumn = $pkColumns[$i];
78  if (!$this->isForeignKey($pkColumn))
79  {
80  $pkValue = $oidParts['id'][$i];
81  // replace dummy ids with ids provided by the database sequence
82  if (PersistenceFacade::isDummyId($pkValue))
83  {
84  $nextId = $this->getNextId();
85  $object->setValue($pkColumn, $nextId, $pkValues[pkColumn]);
86  }
87  }
88  }
89  }
90  /**
91  * @see NodeRDBMapper::getObjectDefinition()
92  */
94  {
95  static $result = null;
96  if ($result == null)
97  {
98  $nodeDef = $this->getObjectDefinitionImpl();
99  $result = array();
100 
101  $result['_properties'] = $nodeDef['_properties'];
102  $result['_datadef'] = $nodeDef['_datadef'];
103 
104  // add the reference definitions to the attribute definitions to be readable by the application
105  foreach ($nodeDef['_ref'] as $reference)
106  {
107  $reference['is_editable'] = false;
108  $reference['app_data_type'] = DATATYPE_ATTRIBUTE;
109  array_push($result['_datadef'], $reference);
110  }
111 
112  // copy only navigable relatives
113  $result['_parents'] = array();
114  for ($i=0; $i<sizeof($nodeDef['_parents']); $i++)
115  {
116  if (!isset($nodeDef['_parents'][$i]['is_navigable']) || $nodeDef['_parents'][$i]['is_navigable']) {
117  array_push($result['_parents'], $nodeDef['_parents'][$i]);
118  }
119  }
120  $result['_children'] = array();
121  for ($i=0; $i<sizeof($nodeDef['_children']); $i++)
122  {
123  if (!isset($nodeDef['_children'][$i]['is_navigable']) || $nodeDef['_children'][$i]['is_navigable']) {
124  array_push($result['_children'], $nodeDef['_children'][$i]);
125  }
126  }
127  }
128  return $result;
129  }
130  /**
131  * @see NodeRDBMapper::getSelectSQL()
132  */
133  function getSelectSQL($condStr, $orderStr=null, $attribs=null, $asArray=false)
134  {
135  $nodeDef = $this->getObjectDefinitionImpl();
136 
137  // replace application attribute/table names with sql names
138  $condStr = $this->translateAppToDatabase($condStr);
139  $orderStr = $this->translateAppToDatabase($orderStr);
140 
141  // parents
142  $parentStr = '';
143  $i=0;
144  foreach($nodeDef['_parents'] as $curParent)
145  {
146  if (!isset($curParent['is_navigable']) || $curParent['is_navigable'])
147  {
148  $parentStr .= $this->quote($curParent['type'])." AS ptype$i, ".$this->getTableName().".".$curParent['fk_columns']." AS pid$i, ";
149  $i++;
150  }
151  }
152  if (strlen($parentStr) > 0) {
153  $parentStr = StringUtil::removeTrailingComma($parentStr);
154  }
155  else {
156  $parentStr = "'' AS ptype0, null AS pid0";
157  }
158 
159  // construct query parts
160  $attribStr = '';
161  $tableName = $this->getTableName();
162  $tableStr = $tableName;
163 
164  // attributes (make sure pk columns are selected)
165  if ($attribs !== null) {
166  $attribs = array_merge($attribs, $this->getPKColumnNames());
167  }
168  foreach($nodeDef['_datadef'] as $curDef) {
169  if ($attribs === null || in_array($curDef['name'], $attribs)) {
170  $attribStr .= $tableName.".".$curDef['column_name']." AS ".$this->quote($curDef['name']).", ";
171  }
172  }
173 
174  // references
175  $refStrings = $this->getSQLForRefs($nodeDef, $attribStr, $tableStr, $condStr, $orderStr, $attribs);
176  $attribStr = $refStrings['attribStr'];
177  $tableStr = $refStrings['tableStr'];
178  $condStr = $refStrings['condStr'];
179  $orderStr = $refStrings['orderStr'];
180 
181  if (strlen($attribStr) > 0) {
182  $attribStr .= ",";
183  }
184  if (strlen($condStr) == 0) {
185  $condStr = "1";
186  }
187  $completeOrderStr = $orderStr;
188  if (strlen($orderStr) > 0)
189  $completeOrderStr = " ORDER BY ".$orderStr;
190  else
191  {
192  // use default ordering
193  $orderByCols = $this->getOrderBy();
194  if (is_array($orderByCols))
195  {
196  $completeOrderStr = '';
197  foreach($orderByCols as $orderByCol) {
198  if (strlen(trim($orderByCol)) > 0) {
199  $completeOrderStr .= $this->getTableName().".".$this->translateAppToDatabase($orderByCol).", ";
200  }
201  }
202  if (strlen($completeOrderStr) > 0) {
203  $completeOrderStr = ' ORDER BY '.StringUtil::removeTrailingComma($completeOrderStr);
204  }
205  }
206  }
207 
208  if ($asArray) {
209  return array(
210  'attributeStr' => $attribStr." ".$parentStr,
211  'tableStr' => $this->_dbPrefix.$tableStr,
212  'conditionStr' => $condStr,
213  'orderStr' => $orderStr
214  );
215  }
216  else {
217  return "SELECT ".$attribStr." ".$parentStr." FROM ".$this->_dbPrefix.$tableStr." WHERE ".$condStr.$completeOrderStr.";";
218  }
219  }
220  /**
221  * @see NodeRDBMapper::getChildrenSelectSQL()
222  */
223  function getChildrenSelectSQL($oid, $compositionOnly=false)
224  {
225  $sqlArray = array();
226  $dbid = PersistenceFacade::getOIDParameter($oid, 'id');
227  $nodeDef = $this->getObjectDefinitionImpl();
228  foreach($nodeDef['_children'] as $childDef)
229  {
230  if (!isset($childDef['is_navigable']) || $childDef['is_navigable'])
231  {
232  if (!$compositionOnly || ($compositionOnly && $childDef['composition'] == true))
233  {
234  $sqlStr = $childDef['table_name'].".".$childDef['fk_columns']."=".$this->quote($dbid[0]);
235  $sqlArray[$childDef['type']] = $sqlStr;
236  }
237  }
238  }
239  return $sqlArray;
240  }
241  /**
242  * @see NodeRDBMapper::getChildrenDisassociateSQL()
243  */
244  function getChildrenDisassociateSQL($oid, $sharedOnly=false)
245  {
246  $sqlArray = array();
247  $dbid = PersistenceFacade::getOIDParameter($oid, 'id');
248  $nodeDef = $this->getObjectDefinitionImpl();
249  foreach($nodeDef['_children'] as $childDef) {
250  if (!$sharedOnly || ($sharedOnly && $childDef['composition'] == false)) {
251  array_push($sqlArray, "UPDATE ".$this->_dbPrefix.$childDef['table_name']." SET ".$childDef['fk_columns']."=NULL WHERE ".
252  $childDef['fk_columns']."=".$this->quote($dbid[0]).";");
253  }
254  }
255  return $sqlArray;
256  }
257  /**
258  * @see NodeRDBMapper::getInsertSQL()
259  */
260  function getInsertSQL(&$object)
261  {
262  $persistenceFacade = &PersistenceFacade::getInstance();
263 
264  $insertedAttributes = array();
265  $attribNameStr = '';
266  $attribValueStr = '';
267  $tableName = $this->getTableName();
268 
269  // primary key definition
270  $oid = $object->getOID();
271  $oidParts = PersistenceFacade::decomposeOID($oid);
272  $pkColumns = $this->getPKColumnNames();
273  for($i=0; $i<sizeof($pkColumns); $i++)
274  {
275  // foreign keys are handled afterwards (they don't get a new id)
276  $pkColumn = $pkColumns[$i];
277  if (!$this->isForeignKey($pkColumn))
278  {
279  $pkValue = $oidParts['id'][$i];
280  $attribNameStr .= $pkColumn.", ";
281  $attribValueStr .= $this->quote($pkValue).", ";
282  array_push($insertedAttributes, $pkColumn);
283  }
284  }
285 
286  // parent definition
287  $parents = &$object->getParents();
288  for ($i=0; $i<sizeof($parents); $i++)
289  {
290  $parent = &$parents[$i];
291  if ($parent != null && PersistenceFacade::isValidOID($parent->getOID()) && PersistenceFacade::isKnownType($parent->getType()))
292  {
293  $parentDef = PersistenceFacade::decomposeOID($parent->getOID());
294  $attribName = $this->getMyFKColumnName($parent->getType());
295 
296  $attribNameStr .= $attribName.", ";
297  $attribValueStr .= $this->quote($parentDef['id'][0]).", ";
298  array_push($insertedAttributes, $attribName);
299  }
300  }
301 
302  // attribute definition
303  $nodeDef = $this->getObjectDefinitionImpl();
304  foreach($nodeDef['_datadef'] as $curDef)
305  {
306  // insert only attributes that are defined in node
307  if (in_array($curDef['name'], $object->getValueNames($curDef['app_data_type'])))
308  {
309  $attribName = $curDef['name'];
310  // don't insert the same attribute twice
311  if (!in_array($attribName, $insertedAttributes))
312  {
313  $attribNameStr .= $tableName.".".$curDef['column_name'].", ";
314  $attribValueStr .= $this->quote($object->getValue($attribName, $curDef['app_data_type'])).", ";
315  array_push($insertedAttributes, $attribName);
316  }
317  }
318  }
319  $attribNameStr = StringUtil::removeTrailingComma($attribNameStr);
320  $attribValueStr = StringUtil::removeTrailingComma($attribValueStr);
321 
322  // query
323  $sqlArray = array
324  (
325  "INSERT INTO ".$this->_dbPrefix.$tableName." (".$attribNameStr.") VALUES (".$attribValueStr.");"
326  );
327  return $sqlArray;
328  }
329  /**
330  * @see NodeRDBMapper::getUpdateSQL()
331  */
332  function getUpdateSQL(&$object)
333  {
334  $updatedAttributes = array();
335  $attribStr = '';
336  $tableName = $this->getTableName();
337 
338  // primary key definition
339  $pkStr = $this->createPKCondition($object->getOID());
340 
341  // parent definition
342  $parents = &$object->getParents();
343  for ($i=0; $i<sizeof($parents); $i++)
344  {
345  $parent = &$parents[$i];
346  if ($parent != null && PersistenceFacade::isValidOID($parent->getOID()) && PersistenceFacade::isKnownType($parent->getType()))
347  {
348  $parentDef = PersistenceFacade::decomposeOID($parent->getOID());
349  $attribName = $this->getMyFKColumnName($parent->getType());
350  $attribStr = $tableName.".".$attribName."=".$this->quote($parentDef['id'][0]).", ";
351  array_push($updatedAttributes, $attribName);
352  }
353  }
354 
355  // attribute definition
356  $nodeDef = $this->getObjectDefinitionImpl();
357  foreach($nodeDef['_datadef'] as $curDef)
358  {
359  // update only attributes that are defined in node
360  if (in_array($curDef['name'], $object->getValueNames($curDef['app_data_type'])))
361  {
362  $attribName = $curDef['name'];
363  // don't insert the same attribute twice
364  if (!in_array($attribName, $updatedAttributes))
365  {
366  $attribStr .= $tableName.".".$curDef['column_name']."=".$this->quote($object->getValue($attribName, $curDef['app_data_type'])).", ";
367  array_push($updatedAttributes, $attribName);
368  }
369  }
370  }
371  $attribStr = StringUtil::removeTrailingComma($attribStr);
372 
373  // query
374  if (strlen($attribStr) > 0) {
375  $sqlArray = array
376  (
377  "UPDATE ".$this->_dbPrefix.$tableName." SET ".$attribStr." WHERE ".$pkStr.";"
378  );
379  }
380  else {
381  $sqlArray = array();
382  }
383  return $sqlArray;
384  }
385  /**
386  * @see NodeRDBMapper::getDeleteSQL()
387  */
388  function getDeleteSQL($oid)
389  {
390  // primary key definition
391  $pkStr = $this->createPKCondition($oid);
392 
393  $sqlArray = array
394  (
395  "DELETE FROM ".$this->_dbPrefix.$this->getTableName()." WHERE ".$pkStr.";"
396  );
397  return $sqlArray;
398  }
399  /**
400  * @see NodeRDBMapper::createPKCondition()
401  */
402  function createPKCondition($oid)
403  {
404  $str = '';
405  $tableName = $this->getTableName();
406  $pkColumns = $this->getPKColumnNames();
407  $ids = PersistenceFacade::getOIDParameter($oid, 'id');
408  for ($i=0; $i<sizeof($pkColumns); $i++)
409  {
410  $pkValue = $ids[$i];
411  $str .= $tableName.".".$pkColumns[$i]."=".$this->quote($pkValue).' AND ';
412  }
413  return substr($str, 0, -5);
414  }
415  /**
416  * Check if a given name is the name of a value
417  * @param name The name to check
418  * @return True/False
419  */
420  function isAttribute($name)
421  {
422  $nodeDef = $this->getObjectDefinition();
423  foreach($nodeDef['_datadef'] as $curDef) {
424  if ($curDef['name'] == $name) {
425  return true;
426  }
427  }
428  return false;
429  }
430  /**
431  * Check if a given type is a child type
432  * @param type The type to check
433  * @param childDef The child array as defined in the _children key (see NodeRDBMapper::getObjectDefinition())
434  * @return True/False
435  */
436  function isChild($type, $childDef)
437  {
438  return NodeUnifiedRDBMapper::getChildDef($type, $childDef) != null;
439  }
440  /**
441  * Get the definition of a given child type
442  * @param type The type to get the definition for
443  * @param childDef The child array as defined in the _children key (see NodeRDBMapper::getObjectDefinition())
444  * @return The child definition or null if not found
445  */
446  function getChildDef($type, $childDef)
447  {
448  foreach($childDef as $curChild) {
449  if ($curChild['type'] == $type) {
450  return $curChild;
451  }
452  }
453  return null;
454  }
455  /**
456  * Check if a given column is a foreign key (used to reference a parent)
457  * @param column The column name
458  * @return True/False
459  */
460  function isForeignKey($column)
461  {
462  $nodeDef = $this->getObjectDefinitionImpl();
463  // search in parents
464  foreach($nodeDef['_parents'] as $parent)
465  {
466  if ($parent['fk_columns'] == $column) {
467  return true;
468  }
469  }
470  return false;
471  }
472  /**
473  * Get the SQL strings for use for the referenced values based on the given strings
474  * @param nodeDef The node definition array (see NodeRDBMapper::getObjectDefinition())
475  * @param attribStr The SQL attribute string to append to
476  * @param tableStr The SQL table string to append to
477  * @param condStr The SQL condition string to append to
478  * @param orderStr The SQL order string to append to
479  * @param attribs The attributes to select or null to select all
480  * @return An assoziative array with the following keys 'attribStr', 'tableStr', 'condStr', 'orderStr' which hold the updated strings
481  */
482  function getSQLForRefs($nodeDef, $attribStr, $tableStr, $condStr, $orderStr, $attribs=null)
483  {
484  // references
485  $joinStr = '';
486  $aliasIndex = 0;
487  $tableName = $this->getTableName();
488  $referencedTables = array();
489  foreach($nodeDef['_ref'] as $curDef)
490  {
491  if ($attribs == null || in_array($curDef['name'], $attribs))
492  {
493  $referencedType = $curDef['ref_type'];
494  $referencedTable = $curDef['ref_table'];
495  $referencedValue = $curDef['ref_value'];
496  $referencedIdColumn = $curDef['id_column'];
497  $referencedFkColumn = $curDef['fk_columns'];
498  $referencedColumn = $curDef['ref_column'];
499 
500  if (strlen($this->getMyFKColumnName($referencedType, false)) > 0)
501  {
502  // reference from parent
503  $attribStr .= $referencedTable.".".$referencedColumn." AS ".$this->quote($curDef['name']).", ";
504  if (!in_array($referencedTable, $referencedTables))
505  {
506  $joinStr .= " LEFT JOIN ".$this->_dbPrefix.$referencedTable." ON ".
507  $referencedTable.".".$referencedIdColumn."=".$this->_dbPrefix.$tableName.".".$this->getMyFKColumnName($referencedType);
508  array_push($referencedTables, $referencedTable);
509  }
510  $condStr = str_replace($curDef['ref_type'].".".$curDef['name'], $referencedTable.".".$referencedColumn, $condStr);
511  }
512  else
513  {
514  $childDef = $this->getChildDef($referencedType, $nodeDef['_children']);
515  if ($childDef != null)
516  {
517  // reference from child
518  $attribStr .= $referencedTable.".".$referencedColumn." AS ".$this->quote($curDef['name']).", ";
519  if (!in_array($referencedTable, $referencedTables))
520  {
521  $joinStr .= " LEFT JOIN ".$this->_dbPrefix.$referencedTable." ON ";
522  $pkColumns = $this->getPKColumnNames();
523  $joinStr .= $referencedTable.".".$referencedFkColumn."=".$this->_dbPrefix.$tableName.".".$pkColumns[0];
524  $joinStr .= " AND ".$referencedTable.".".$referencedIdColumn." = (SELECT MIN(".$referencedTable.".".$referencedIdColumn.") FROM ".$referencedTable." WHERE ".$referencedTable.".".$referencedFkColumn."=".$this->_dbPrefix.$tableName.".".$pkColumns[0].")";
525  array_push($referencedTables, $referencedTable);
526  }
527  $condStr = str_replace($curDef['ref_type'].".".$curDef['name'], $referencedTable.".".$referencedColumn, $condStr);
528 
529  // add the order string to get the first child in order
530  if (sizeof($childDef['order_by']) > 0)
531  {
532  $tmpOrderStr = '';
533  foreach($childDef['order_by'] as $orderBy) {
534  if (strlen($orderBy)) {
535  $tmpOrderStr .= $referencedTable.".".$orderBy.", ";
536  }
537  }
538  $orderStr .= $tmpOrderStr;
539  }
540  }
541  /*
542  else
543  WCMFException::throwEx("Reference ".$referencedType.".".$referencedValue." could not be resolved in type ".$this->getType(), __FILE__, __LINE__);
544  */
545  }
546  }
547  $aliasIndex++;
548  }
549  $attribStr = StringUtil::removeTrailingComma($attribStr);
550  $tableStr .= $joinStr;
551  $orderStr = StringUtil::removeTrailingComma($orderStr);
552  return array('attribStr' => $attribStr, 'tableStr' => $tableStr, 'condStr' => $condStr, 'orderStr' => $orderStr);
553  }
554  /**
555  * Replace all attribute and type occurences to columns and table name
556  * @param str The string to translate (e.g. an orderby clause)
557  * @return The translated string
558  */
559  function translateAppToDatabase($str)
560  {
561  // replace application attribute/table names with sql names
562  $nodeDef = $this->getObjectDefinitionImpl();
563  foreach($nodeDef['_datadef'] as $curDef) {
564  $str = preg_replace('/\b'.$curDef['name'].'\b/', $curDef['column_name'], $str);
565  }
566  $str = preg_replace('/\b'.$this->getType().'\b/', $this->getTableName(), $str);
567  return $str;
568  }
569  /**
570  * Get the name of a column
571  * @param attributeName The name of the corresponding attribute
572  * @param dataType The data type of the attribute
573  * @return The name of the column or null if not existent
574  */
575  function getColumnName($attributeName, $dataType=null)
576  {
577  $nodeDef = $this->getObjectDefinitionImpl();
578  // search in attributes
579  foreach($nodeDef['_datadef'] as $dataItem)
580  {
581  if ($dataItem['name'] == $attributeName && ($dataType == null || ($dataType != null && $dataType == $dataItem['app_data_type']))) {
582  return $dataItem['column_name'];
583  }
584  }
585  // search refs
586  foreach($nodeDef['_ref'] as $dataItem)
587  {
588  if ($dataItem['name'] == $attributeName) {
589  return null;
590  }
591  }
592  WCMFException::throwEx($this->getType()." has no attribute ".$attributeName, __FILE__, __LINE__);
593  }
594  /**
595  * Get the name of the foreign key column defined in a given child type that connects from that type to the primary key column
596  * of this type.
597  * @param childType The child type
598  * @param isRequired True/False wether it is required to find a fk column or not
599  * @return The name of the column
600  */
601  function getChildFKColumnName($childType, $isRequired=true)
602  {
603  $nodeDef = $this->getObjectDefinitionImpl();
604  foreach($nodeDef['_children'] as $childDef)
605  {
606  if ($childDef['type'] == $childType) {
607  return $childDef['fk_columns'];
608  }
609  }
610  if ($isRequired) {
611  WCMFException::throwEx("No foreign key name found for '".$childType."' in '".$this->getType()."'", __FILE__, __LINE__);
612  }
613  }
614  /**
615  * Get the name of the foreign key column defined in this type that connects to the primary key column
616  * of a parent type.
617  * @param parentType The parent type
618  * @param isRequired True/False wether it is required to find a fk column or not
619  * @return The name of the column
620  */
621  function getMyFKColumnName($parentType, $isRequired=true)
622  {
623  // check if parent type is listed
624  $fkName = $this->getMyFKColumnNameImpl($parentType);
625  if (strlen($fkName) > 0) {
626  return $fkName;
627  }
628  // if not check base classes (roles may be modelled as subclasses of a class representing the database table)
629  $nextParentType = get_parent_class($parentType);
630  while ($nextParentType !== false && $nextParentType != 'Node' && $nextParentType != 'node')
631  {
632  $fkName = $this->getMyFKColumnNameImpl($nextParentType);
633  if (strlen($fkName) > 0) {
634  return $fkName;
635  }
636  $nextParentType = get_parent_class($nextParentType);
637  }
638  if ($isRequired) {
639  WCMFException::throwEx("No foreign key name found for '".$parentType."' in '".$this->getType()."'", __FILE__, __LINE__);
640  }
641  }
642  /**
643  * Quote a value to be inserted into the database
644  * @param value The value to quote
645  * @return The quoted value
646  */
647  function quote($value)
648  {
649  if ($value == null) {
650  return 'null';
651  }
652  else {
653  $conn = &$this->getConnection();
654  return $conn->quote($value);
655  }
656  }
657  /**
658  * Get the names of the primary key database columns of this type
659  * @return An array of the names of the columns
660  */
661  function getPKColumnNames()
662  {
663  return array_keys($this->getPkNames());
664  }
665  /**
666  * @see PersistenceMapper::isValidOID()
667  */
668  function isValidOID($oid)
669  {
670  $oidParts = PersistenceFacade::decomposeOID($oid);
671 
672  // check the type parameter
673  if ($oidParts['type'] != $this->getType()) {
674  return false;
675  }
676  // check the id parameter
677  foreach ($oidParts['id'] as $id)
678  {
679  if (!is_numeric($id)) {
680  return false;
681  }
682  }
683  return true;
684  }
685 
686  /**
687  * ChangeListener interface implementation
688  */
689 
690  /**
691  * @see ChangeListener::getId()
692  */
693  function getId()
694  {
695  return __CLASS__;
696  }
697  /**
698  * @see ChangeListener::valueChanged()
699  */
700  function valueChanged(&$object, $name, $type, $oldValue, $newValue) {}
701  /**
702  * @see ChangeListener::propertyChanged()
703  */
704  function propertyChanged(&$object, $name, $oldValue, $newValue)
705  {
706  if ($name == 'parentoids')
707  {
708  $nodeDef = $this->getObjectDefinitionImpl();
709 
710  // update the foreign key column values
711  foreach($newValue as $newOID)
712  {
713  $oidParts = PersistenceFacade::decomposeOID($newOID);
714  foreach($nodeDef['_parents'] as $curParent)
715  {
716  if ($curParent['type'] == $oidParts['type']) {
717  $object->setValue($curParent['fk_columns'], $oidParts['id'][0]);
718  }
719  }
720  }
721  }
722  }
723  /**
724  * @see ChangeListener::stateChanged()
725  */
726  function stateChanged(&$object, $oldValue, $newValue) {}
727 
728  /**
729  * TEMPLATE METHODS
730  * Subclasses must implement this method to define their object type.
731  */
732 
733  /**
734  * Implementation of NodeRDBMapper::getObjectDefinition()
735  * @note Subclasses must implement this method to define their Node type.
736  * @note NodeUnifiedRDBMapper enhances the node definition array by optional references, which may be noted
737  * in the following key:
738  *
739  * - @em _refs: An array of assoziative arrays with the keys 'name', 'ref_type', 'ref_value', 'ref_table', 'id_column',
740  * 'fk_columns', 'ref_column' for every referenced data item.
741  * With these definitions it is possible to reference values (ref_value) from other types (ref_type)
742  * (e.g. array('name' => 'employee_name', 'ref_type' => 'Employee', 'ref_value' => 'name', 'ref_table' => 'Employee',
743  * 'id_column' => 'id', 'fk_columns' => 'fk_company_id', 'ref_colmn' => 'employee_name'))
744  *
745  * @note A type can only reference values from a type to which it is directly connected!
746  */
748  {
749  WCMFException::throwEx("getObjectDefinitionImpl() must be implemented by derived class: ".get_class($this), __FILE__, __LINE__);
750  }
751  /**
752  * Get the name of the database table, where this type is mapped to
753  * @return The name of the table
754  */
755  function getTableName()
756  {
757  WCMFException::throwEx("getTableName() must be implemented by derived class: ".get_class($this), __FILE__, __LINE__);
758  }
759  /**
760  * Get the name of the foreign key column defined in this type that connects to the primary key column
761  * of a parent type.
762  * @param parentType The parent type
763  * @return The name of the column
764  */
765  function getMyFKColumnNameImpl($parentType)
766  {
767  WCMFException::throwEx("getMyFKColumnNameImpl() must be implemented by derived class: ".get_class($this), __FILE__, __LINE__);
768  }
769 }
770 ?>
removeTrailingComma($string)
getSelectSQL($condStr, $orderStr=null, $attribs=null, $asArray=false)
getMyFKColumnName($parentType, $isRequired=true)
getColumnName($attributeName, $dataType=null)
const DATATYPE_ATTRIBUTE
valueChanged(&$object, $name, $type, $oldValue, $newValue)
getChildFKColumnName($childType, $isRequired=true)
getChildrenSelectSQL($oid, $compositionOnly=false)
throwEx($message, $file='', $line='')
getSQLForRefs($nodeDef, $attribStr, $tableStr, $condStr, $orderStr, $attribs=null)
decomposeOID($oid, $validate=true)
getChildrenDisassociateSQL($oid, $sharedOnly=false)
NodeRDBMapper maps Node objects to a relational database schema where each Node type has its own tabl...
propertyChanged(&$object, $name, $oldValue, $newValue)
getOIDParameter($oid, $param, $validate=true)
stateChanged(&$object, $oldValue, $newValue)
NodeUnifiedRDBMapper maps Node objects to a relational database schema where each Node type has its o...