wCMF  3.6
 All Classes Namespaces Files Functions Variables Groups Pages
class.NodeToSingleTableMapper.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.NodeToSingleTableMapper.php 1462 2014-02-04 23:52:27Z iherwig $
18  */
19 require_once(BASE."wcmf/lib/core/class.WCMFException.php");
20 require_once(BASE."wcmf/lib/persistence/class.PersistenceMapper.php");
21 require_once(BASE."wcmf/lib/persistence/converter/class.DataConverter.php");
22 require_once(BASE."wcmf/lib/model/class.Node.php");
23 require_once(BASE."wcmf/lib/util/class.InifileParser.php");
24 require_once(BASE."wcmf/3rdparty/adodb/adodb.inc.php");
25 
26 /**
27  * Some constants describing the data types
28  */
29 define("DATATYPE_DONTCARE", 0);
30 define("DATATYPE_ATTRIBUTE", 1);
31 define("DATATYPE_ELEMENT", 2);
32 define("DATATYPE_IGNORE", 3); // all data items >= DATATYPE_IGNORE wont be shown in human readable node discriptions
33 /**
34  * Some constants describing the build process
35  */
36 define("BUILDDEPTH_INFINITE", -1); // build complete tree from given root on
37 define("BUILDDEPTH_SINGLE", -2); // build only given node
38 define("BUILDDEPTH_GROUPED", -3); // build tree from given root on respecting the root property defined in element relations
39 define("BUILDDEPTH_REQUIRED", -4); // build tree from given root on respecting the required property defined in element relations
40 
41 define("BUILDTYPE_COMPLETE", -1); // build a node with all properties etc.
42 define("BUILDTYPE_NOPROPS", -2); // build a node without properties for itself and its attributes (saves memory space)
43 
44 /**
45  * @class NodeToSingleTableMapper
46  * @ingroup Mapper
47  * @brief NodeToSingleTableMapper maps nodes of different types to the database. It uses a
48  * relational database scheme that simulates a xml tree. That means all nodes share one
49  * table.
50  * @deprecated Use NodeRDBMapper or NodeUnifiedRDBMapper instead
51  *
52  * @author ingo herwig <ingo@wemove.com>
53  */
55 {
56  var $_conn = null; // database connection
57  var $_rootElementId = 0; // stores element id of root for affiliation verification of child nodes
58  var $_rootId = 0; // stores id of root
59  var $_groupMap = array(); // stores grouped property (true/false) for child root pairs (key: childElementId_rootElementId)
60  var $_type = '*'; // mapper type
61  var $_dbPrefix = ''; // table prefix in database
62 
63  /**
64  * Constructor.
65  * @param params Initialization data given in an assoziative array with the following keys:
66  * dbType, dbHostName, dbUserName, dbPassword, dbName OR dbConnection
67  * if dbPrefix is given it will be appended to every table string, which is
68  * usefull if different cms operate on the same database
69  */
70  function NodeToSingleTableMapper($params)
71  {
72  if (array_key_exists('dbType', $params) && array_key_exists('dbHostName', $params) && array_key_exists('dbUserName', $params) &&
73  array_key_exists('dbPassword', $params) && array_key_exists('dbName', $params))
74  {
75  // create new connection
76  $this->_conn = &ADONewConnection($params['dbType']);
77  $connected = $this->_conn->PConnect($params['dbHostName'],$params['dbUserName'],$params['dbPassword'],$params['dbName']);
78  if (!$connected)
79  WCMFException::throwEx($this->_conn->ErrorMsg(), __FILE__, __LINE__);
80 
81  $this->_conn->replaceQuote = "\'";
82  $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
83  define(ADODB_OUTP, "gError");
84 
85  // get database prefix if defined
86  $this->_dbPrefix = $params['dbPrefix'];
87 
88  // log sql if requested
89  $parser = &InifileParser::getInstance();
90  if (($logSQL = $parser->getValue('logSQL', 'cms')) === false)
91  $logSQL = 0;
92  $this->_conn->LogSQL($logSQL);
93  }
94  elseif (array_key_exists('dbConnection', $params))
95  {
96  // use existing connection
97  $this->_conn = $params['dbConnection'];
98  }
99  else
100  WCMFException::throwEx("Wrong parameters for constructor.", __FILE__, __LINE__);
101 
102  $this->_dataConverter = new DataConverter();
103  }
104  /**
105  * Construct a Node from the database.
106  * @param oid The object id of the Node to load
107  * @param buildDepth One of the BUILDDEPTH constants or a number describing the number of generations to build (except BUILDDEPTH_REQUIRED)
108  * @param buildType One of the BUILDTYPE constants describing the size to build
109  * @param depth Internal use
110  * @return A reference to the Node, null if oid does not exist.
111  */
112  function &load($oid, $buildDepth, $buildType=BUILDTYPE_COMPLETE, $depth=0)
113  {
114  if ($buildDepth < 0 && !in_array($buildDepth, array(BUILDDEPTH_INFINITE, BUILDDEPTH_SINGLE, BUILDDEPTH_GROUPED)))
115  WCMFException::throwEx("Build depth not supported: $buildDepth", __FILE__, __LINE__);
116 
117  $persistenceFacade = &PersistenceFacade::getInstance();
118  $nodeDef = $persistenceFacade->decomposeOID($oid);
119 
120  // select node data
121  $sqlStr = "SELECT ".$this->_dbPrefix."nodes.*, ".$this->_dbPrefix."elements.id as eid, ".$this->_dbPrefix."elements.element_name, ".$this->_dbPrefix."elements.visible,
122  ".$this->_dbPrefix."elements.editable, ".$this->_dbPrefix."elements.alt, ".$this->_dbPrefix."elements.hint, ".$this->_dbPrefix."elements.display_value,
123  ".$this->_dbPrefix."elements.input_type, ".$this->_dbPrefix."elements.restrictions, ".$this->_dbPrefix."elements.defaultval, ".$this->_dbPrefix."data_types.data_type
124  FROM ".$this->_dbPrefix."nodes, ".$this->_dbPrefix."elements LEFT JOIN ".$this->_dbPrefix."data_types
125  ON ".$this->_dbPrefix."elements.fk_data_types_id=".$this->_dbPrefix."data_types.id
126  WHERE ".$this->_dbPrefix."nodes.id=".$nodeDef['id']." AND ".$this->_dbPrefix."nodes.fk_n_elements_id=".$this->_dbPrefix."elements.id;";
127  // left join on data_type, because it may be null (node without content)
128  $rs = &$this->_conn->Execute($sqlStr);
129  if (!$rs)
130  WCMFException::throwEx($this->_conn->ErrorMsg(), __FILE__, __LINE__);
131  else
132  $nodeData = $rs->FetchRow();
133  if (!$nodeData)
134  return null;
135 
136  // store element id of root node
137  // every grouped child has this element id as grouproot in element relations
138  if ($depth == 0)
139  {
140  $this->_rootElementId = $nodeData['fk_n_elements_id'];
141  $this->_rootId = $nodeDef['id'];
142  }
143 
144  // construct node
145  $node = new Node($nodeData['element_name'], $oid);
146  $node->setMapper($this);
147  if ($buildType != BUILDTYPE_NOPROPS)
148  {
149  // set node properties
150  $node->setProperty('visible', $nodeData['visible']);
151  $node->setProperty('editable', $nodeData['editable']);
152  $node->setProperty('alt', $nodeData['alt']);
153  $node->setProperty('hint', $nodeData['hint']);
154  $node->setProperty('display_value', $nodeData['display_value']);
155  $node->setProperty('input_type', $nodeData['input_type']);
156  $node->setProperty('elementid', $nodeData['eid']);
157  if ($nodeData['fk_nodes_id'] != '')
158  $node->setProperty('parentoid', $persistenceFacade->composeOID(array('type' => $this->_type, 'id' => $nodeData['fk_nodes_id'])));
159  $node->setProperty('rootoid', null); // default value is null, if we have a grouproot it will be set afterwards
160  }
161 
162  // get node data if exists
163  if (isset($nodeData['data_type']))
164  {
165  // convert data
166  $nodeValue = $this->_dataConverter->convertStorageToApplication($nodeData[$nodeData['data_type']], $nodeData['data_type'], $nodeData['element_name']);
167  if ($nodeValue == '')
168  $nodeValue = $nodeData['defaultval'];
169  // set data
170  $node->setValue($nodeData['element_name'], $nodeValue, DATATYPE_ELEMENT);
171  if ($buildType != BUILDTYPE_NOPROPS)
172  {
173  $elementProperties = array('visible' => $nodeData['visible'],
174  'restrictions' => stripslashes($nodeData['restrictions']),
175  'defaultval' => $nodeData['defaultval'],
176  'editable' => $nodeData['editable'],
177  'alt' => $nodeData['alt'],
178  'hint' => $nodeData['hint'],
179  'input_type' => $nodeData['input_type'],
180  'data_type' => $nodeData['data_type'],
181  'id'=> $nodeData['eid']);
182  $node->setValueProperties($nodeData['element_name'], $elementProperties, DATATYPE_ELEMENT);
183  }
184  }
185 
186  // get attributes of this node
187  $sqlStr = "SELECT ".$this->_dbPrefix."attribs.*, ".$this->_dbPrefix."attrib_def.id as aid, ".$this->_dbPrefix."attrib_def.attrib_name, ".$this->_dbPrefix."attrib_def.optional,
188  ".$this->_dbPrefix."attrib_def.restrictions, ".$this->_dbPrefix."attrib_def.defaultval, ".$this->_dbPrefix."attrib_def.visible, ".$this->_dbPrefix."attrib_def.editable,
189  ".$this->_dbPrefix."attrib_def.alt, ".$this->_dbPrefix."attrib_def.hint, ".$this->_dbPrefix."attrib_def.input_type, ".$this->_dbPrefix."data_types.data_type
190  FROM ".$this->_dbPrefix."attribs, ".$this->_dbPrefix."attrib_def, ".$this->_dbPrefix."data_types
191  WHERE ".$this->_dbPrefix."attribs.fk_nodes_id=".$nodeDef['id']." AND ".$this->_dbPrefix."attribs.fk_attrib_def_id=".$this->_dbPrefix."attrib_def.id
192  AND ".$this->_dbPrefix."attrib_def.fk_data_types_id=".$this->_dbPrefix."data_types.id ORDER BY ".$this->_dbPrefix."attrib_def.sort_key;";
193  $rs = &$this->_conn->Execute($sqlStr);
194  while ($rs && $nodeAttributes = $rs->FetchRow())
195  {
196  // convert data
197  $attributeValue = $this->_dataConverter->convertStorageToApplication($nodeAttributes[$nodeAttributes['data_type']], $nodeAttributes['data_type'], $nodeAttributes['attrib_name']);
198  if ($attributeValue == '')
199  $attributeValue = $nodeAttributes['defaultval'];
200  // set data
201  $node->setValue($nodeAttributes['attrib_name'], $attributeValue, DATATYPE_ATTRIBUTE);
202  if ($buildType != BUILDTYPE_NOPROPS)
203  {
204  $attributeProperties = array('visible' => $nodeAttributes['visible'],
205  'restrictions' => $nodeAttributes['restrictions'],
206  'defaultval' => $nodeAttributes['defaultval'],
207  'editable' => $nodeAttributes['editable'],
208  'alt' => $nodeAttributes['alt'],
209  'optional' => $nodeAttributes['optional'],
210  'hint' => $nodeAttributes['hint'],
211  'input_type' => $nodeAttributes['input_type'],
212  'data_type' => $nodeAttributes['data_type'],
213  'id' => $nodeAttributes['aid']);
214  $node->setValueProperties($nodeAttributes['attrib_name'], $attributeProperties, DATATYPE_ATTRIBUTE);
215  }
216  }
217 
218  // set sortkey
219  $node->setValue('sort_key', $nodeData['sort_key'], DATATYPE_IGNORE);
220 
221  // get children of this node
222  $childoids = array();
223  $sqlStr = "SELECT ".$this->_dbPrefix."nodes.id, ".$this->_dbPrefix."nodes.fk_n_elements_id FROM ".$this->_dbPrefix."nodes
224  WHERE ".$this->_dbPrefix."nodes.fk_nodes_id=".$nodeDef['id']." ORDER BY sort_key;";
225  $rs = &$this->_conn->Execute($sqlStr);
226  while ($rs && $childData = $rs->FetchRow())
227  {
228  $childoid = $persistenceFacade->composeOID(array('type' => $this->_type, 'id' => $childData['id']));
229  array_push($childoids, $childoid);
230  if ( ($buildDepth != BUILDDEPTH_SINGLE) &&
231  ( ($depth < $buildDepth) ||
232  ($buildDepth == BUILDDEPTH_INFINITE) ||
233  ( ($buildDepth == BUILDDEPTH_GROUPED) && $this->isGroupChild($childData['fk_n_elements_id'], $nodeData['fk_n_elements_id'], $this->_rootElementId) )
234  )
235  )
236  {
237  $childNode = &$persistenceFacade->load($childoid, $buildDepth, $buildType, $depth+1);
238  $childNode->setMapper($this);
239  if ($this->isGroupChild($childData['fk_n_elements_id'], $nodeData['fk_n_elements_id'], $this->_rootElementId))
240  if ($buildType != BUILDTYPE_NOPROPS)
241  {
242  if ($this->_rootId != '')
243  {
244  $rootoid = $persistenceFacade->composeOID(array('type' => $this->_type, 'id' => $this->_rootId));
245  $childNode->setProperty('rootoid', $rootoid);
246  }
247  }
248  $node->addChild($childNode);
249  }
250  }
251  if ($buildType != BUILDTYPE_NOPROPS)
252  $node->setProperty('childoids', $childoids);
253  $node->setState(STATE_CLEAN, false);
254  return $node;
255  }
256  /**
257  * Construct the template of a Node of a given type (defined by element name).
258  * @param type The type of the Node to construct (maybe '' for an unspecified Node)
259  * @param buildDepth One of the BUILDDEPTH constants or a number describing the number of generations to build
260  * @param buildType One of the BUILDTYPE constants describing the size to build
261  * @param depth Internal use
262  * @return A reference to the Node.
263  */
264  function &create($type, $buildDepth, $buildType=BUILDTYPE_COMPLETE, $depth=0)
265  {
266  if ($buildDepth < 0 && !in_array($buildDepth, array(BUILDDEPTH_INFINITE, BUILDDEPTH_SINGLE, BUILDDEPTH_GROUPED, BUILDDEPTH_REQUIRED)))
267  WCMFException::throwEx("Build depth not supported: $buildDepth", __FILE__, __LINE__);
268 
269  $persistenceFacade = &PersistenceFacade::getInstance();
270  // map _any_ node to empty node
271  if ($type == '*') $type = '';
272 
273  $nodeData = array();
274  if ($type != '')
275  {
276  $sqlStr = "SELECT ".$this->_dbPrefix."elements.id, ".$this->_dbPrefix."elements.visible, ".$this->_dbPrefix."elements.editable, ".$this->_dbPrefix."elements.alt,
277  ".$this->_dbPrefix."elements.hint, ".$this->_dbPrefix."elements.display_value, ".$this->_dbPrefix."elements.input_type, ".$this->_dbPrefix."elements.restrictions,
278  ".$this->_dbPrefix."elements.defaultval, ".$this->_dbPrefix."data_types.data_type
279  FROM ".$this->_dbPrefix."elements LEFT JOIN ".$this->_dbPrefix."data_types ON ".$this->_dbPrefix."elements.fk_data_types_id=".$this->_dbPrefix."data_types.id
280  WHERE ".$this->_dbPrefix."elements.element_name='".$type."';"; // left join on data_type, because it may be null (node without content)
281  $rs = &$this->_conn->Execute($sqlStr);
282  if (!$rs || $rs->RecordCount() == 0)
283  WCMFException::throwEx("Can't construct template node ".$type.": unknown element", __FILE__, __LINE__);
284  else
285  $nodeData = $rs->FetchRow();
286  }
287 
288  // store element id of root node
289  // every grouped child has this element id as grouproot in element relations
290  if ($depth == 0)
291  {
292  $this->_rootElementId = $nodeData['id'];
293  $this->_rootId = $nodeData['id'];
294  }
295 
296  // construct node
297  $node = new Node($type);
298  $node->setMapper($this);
299  if ($buildType != BUILDTYPE_NOPROPS)
300  {
301  // set node properties
302  $node->setProperty('visible', $nodeData['visible']);
303  $node->setProperty('editable', $nodeData['editable']);
304  $node->setProperty('alt', $nodeData['alt']);
305  $node->setProperty('hint', $nodeData['hint']);
306  $node->setProperty('display_value', $nodeData['display_value']);
307  $node->setProperty('input_type', $nodeData['input_type']);
308  $node->setProperty('elementid', $nodeData['id']);
309  // NODE: we do not set the 'parentoid' property because it might be ambiguous (e.g. 'name' element might be used in different contexts)
310  if ($this->_rootId != '')
311  {
312  $rootoid = $persistenceFacade->composeOID(array('type' => $this->_type, 'id' => $this->_rootId));
313  $node->setProperty('rootoid', $rootoid);
314  }
315  }
316 
317  // set node data element if exists
318  if (isset($nodeData['data_type']))
319  {
320  // convert data
321  $nodeValue = $this->_dataConverter->convertStorageToApplication($nodeData['defaultval'], $nodeData['data_type'], $nodeData['element_name']);
322  // set data
323  $node->setValue($type, $nodeValue, DATATYPE_ELEMENT);
324  if ($buildType != BUILDTYPE_NOPROPS)
325  {
326  $elementProperties = array('visible' => $nodeData['visible'],
327  'restrictions' => stripslashes($nodeData['restrictions']),
328  'defaultval' => $nodeData['defaultval'],
329  'editable' => $nodeData['editable'],
330  'alt' => $nodeData['alt'],
331  'hint' => $nodeData['hint'],
332  'input_type' => $nodeData['input_type'],
333  'data_type' => $nodeData['data_type'],
334  'id'=> $nodeData['id']);
335  $node->setValueProperties($type, $elementProperties, DATATYPE_ELEMENT);
336  }
337  }
338 
339  // set attributes of this node
340  $sqlStr = "SELECT ".$this->_dbPrefix."attrib_def.id, ".$this->_dbPrefix."attrib_def.attrib_name, ".$this->_dbPrefix."attrib_def.optional, ".$this->_dbPrefix."attrib_def.restrictions,
341  ".$this->_dbPrefix."attrib_def.defaultval, ".$this->_dbPrefix."attrib_def.visible, ".$this->_dbPrefix."attrib_def.editable, ".$this->_dbPrefix."attrib_def.hint,
342  ".$this->_dbPrefix."attrib_def.alt, ".$this->_dbPrefix."attrib_def.input_type, ".$this->_dbPrefix."data_types.data_type
343  FROM ".$this->_dbPrefix."attrib_def, ".$this->_dbPrefix."data_types
344  WHERE ".$this->_dbPrefix."attrib_def.fk_elements_id=".$nodeData['id']." AND ".$this->_dbPrefix."attrib_def.fk_data_types_id=".$this->_dbPrefix."data_types.id
345  ORDER BY ".$this->_dbPrefix."attrib_def.sort_key;";
346  $rs = &$this->_conn->Execute($sqlStr);
347  while ($rs && $nodeAttributes = $rs->FetchRow())
348  {
349  // convert data
350  $attributeValue = $this->_dataConverter->convertStorageToApplication($nodeAttributes['defaultval'], $nodeAttributes['data_type'], $nodeAttributes['attrib_name']);
351  // set data
352  $node->setValue($nodeAttributes['attrib_name'], $attributeValue, DATATYPE_ATTRIBUTE);
353  if ($buildType != BUILDTYPE_NOPROPS)
354  {
355  $attributeProperties = array('visible' => $nodeAttributes['visible'],
356  'restrictions' => $nodeAttributes['restrictions'],
357  'defaultval' => $nodeAttributes['defaultval'],
358  'editable' => $nodeAttributes['editable'],
359  'alt' => $nodeAttributes['alt'],
360  'optional' => $nodeAttributes['optional'],
361  'hint' => $nodeAttributes['hint'],
362  'input_type' => $nodeAttributes['input_type'],
363  'data_type' => $nodeAttributes['data_type'],
364  'id' => $nodeAttributes['id']);
365  $node->setValueProperties($nodeAttributes['attrib_name'], $attributeProperties, DATATYPE_ATTRIBUTE);
366  }
367  }
368 
369  // set sortkey
370  $node->setValue('sort_key', 0, DATATYPE_IGNORE);
371 
372  // set children of this node
373  $childoids = array();
374  $sqlStr = "SELECT ".$this->_dbPrefix."elements.element_name, ".$this->_dbPrefix."element_relations.fk_elements_child_id, ".$this->_dbPrefix."element_relations.repetitive,
375  ".$this->_dbPrefix."element_relations.optional
376  FROM ".$this->_dbPrefix."element_relations, ".$this->_dbPrefix."elements
377  WHERE ".$this->_dbPrefix."element_relations.fk_elements_id=".$nodeData['id']." AND ".$this->_dbPrefix."element_relations.fk_elements_child_id=".$this->_dbPrefix."elements.id
378  ORDER BY ".$this->_dbPrefix."element_relations.sort_key;";
379  $rs = &$this->_conn->Execute($sqlStr);
380  while ($rs && $childData = $rs->FetchRow())
381  {
382  $childoid = $persistenceFacade->composeOID(array('type' => $childData['element_name'], 'id' => $childData['fk_elements_child_id']));
383  array_push($childoids, $childoid);
384 
385  // map 'repetitive', 'optional' to 'minOccurs', 'maxOccurs'
386  $childData['minOccurs'] = 1; // default value
387  $childData['maxOccurs'] = 1; // default value
388  if ($childData['repetitive'] == 1)
389  $childData['maxOccurs'] = 'unbounded';
390  if ($childData['optional'] == 1)
391  $childData['minOccurs'] = 0;
392 
393  if ( ($buildDepth != BUILDDEPTH_SINGLE) &&
394  ( ($depth < $buildDepth) ||
395  ($buildDepth == BUILDDEPTH_INFINITE) ||
396  ( ($buildDepth == BUILDDEPTH_GROUPED) && $this->isGroupChild($childData['fk_elements_child_id'], $nodeData['id'], $this->_rootElementId) ) ||
397  ( ($buildDepth == BUILDDEPTH_REQUIRED) && $childData['minOccurs'] > 0 )
398  )
399  )
400  {
401  $childNode = &$persistenceFacade->create($childData['element_name'], $buildDepth, $buildType, $depth+1);
402  $childNode->setMapper($this);
403  if ($buildType != BUILDTYPE_NOPROPS)
404  {
405  $childNode->setProperty('minOccurs', $childData['minOccurs']);
406  $childNode->setProperty('maxOccurs', $childData['maxOccurs']);
407  }
408  $node->addChild($childNode);
409  }
410  }
411  if ($buildType != BUILDTYPE_NOPROPS)
412  $node->setProperty('childoids', $childoids);
413  return $node;
414  }
415  /**
416  * Save a Node to the database (inserted if it is new).
417  * @param node A reference to the Node to save
418  * @return True/False depending on success of operation
419  */
420  function save(&$node)
421  {
422  // prepare node data
423  // escape all values
424  foreach ($node->getDataTypes() as $type)
425  foreach ($node->getValueNames($type) as $valueName)
426  {
427  $properties = $node->getValueProperties($valueName, $type);
428  // NOTE: strip slashes from "'" and """ first because on INSERT/UPDATE we use ADODB's qstr
429  // with second parameter false which will add slashes again
430  // (we do this manually because we can't rely on get_magic_quotes_gpc())
431  $value = str_replace(array("\'","\\\""), array("'", "\""), $node->getValue($valueName, $type));
432  $node->setValue($valueName, $this->_dataConverter->convertApplicationToStorage($value, $properties['data_type'], $valueName), $type);
433  }
434 
435  $persistenceFacade = &PersistenceFacade::getInstance();
436  $nodeDef = $persistenceFacade->decomposeOID($node->getOID());
437  if ($node->getState() == STATE_NEW)
438  {
439  // insert new node
440  // precondition: the node has a parent and its id exists in the database
441  $parent = &$node->getParent();
442  $parentDef = $persistenceFacade->decomposeOID($parent->getOID());
443  if ($parent != null)
444  {
445  // save node
446  $sortkey = $node->getValue('sort_key', DATATYPE_IGNORE);
447  if (!$sortkey) $sortkey = 0;
448  // save node element if exist
449  if (in_array(DATATYPE_ELEMENT, $node->getDataTypes()))
450  {
451  $elementValue = $node->getValue($node->getType(), DATATYPE_ELEMENT);
452  $properties = $node->getValueProperties($node->getType(), DATATYPE_ELEMENT);
453  if ($properties['data_type'] != '')
454  $sqlStr = "INSERT INTO ".$this->_dbPrefix."nodes (fk_nodes_id, fk_n_elements_id, ".$properties['data_type'].", sort_key)
455  VALUES (".$parentDef['id'].", ".$node->getProperty('elementid').", ".$this->_conn->qstr($elementValue).", ".$sortkey.");";
456  }
457  else
458  $sqlStr = "INSERT INTO ".$this->_dbPrefix."nodes (fk_nodes_id, fk_n_elements_id, sort_key)
459  VALUES (".$parentDef['id'].", ".$node->getProperty('elementid').", ".$sortkey.");";
460  if ($this->_conn->Execute($sqlStr) === false)
461  WCMFException::throwEx("Error inserting node ".$node->getType().": ".$this->_conn->ErrorMsg(), __FILE__, __LINE__);
462 
463  // update node oid
464  $node->setOID($persistenceFacade->composeOID(array('type' => $nodeDef['type'], 'id' => $this->_conn->Insert_ID())));
465  $nodeDef = $persistenceFacade->decomposeOID($node->getOID());
466 
467  // save attributes
468  $attributeNames = $node->getValueNames(DATATYPE_ATTRIBUTE);
469  foreach($attributeNames as $attributeName)
470  {
471  // prepare attribute data
472  $attributeValue = $node->getValue($attributeName, DATATYPE_ATTRIBUTE);
473  $attributeProperties = $node->getValueProperties($attributeName, DATATYPE_ATTRIBUTE);
474  if ($attributeProperties == null)
475  WCMFException::throwEx("Can't save ".$node->getType().".".$attributeName.": No properties found.", __FILE__, __LINE__);
476 
477  if ($attributeValue != '')
478  {
479  // save attribute data, if not empty
480  $sqlStr = "INSERT INTO ".$this->_dbPrefix."attribs (fk_nodes_id, fk_attrib_def_id, ".$attributeProperties['data_type'].")
481  VALUES (".$nodeDef['id'].", ".$attributeProperties['id'].", ".$this->_conn->qstr($attributeValue).");";
482  if ($this->_conn->Execute($sqlStr) === false)
483  WCMFException::throwEx("Error inserting node ".$node->getType().": ".$this->_conn->ErrorMsg(), __FILE__, __LINE__);
484  }
485  }
486 
487  // log action
488  $this->logAction(&$node);
489  }
490  else
491  return false;
492  }
493  else if ($node->getState() == STATE_DIRTY)
494  {
495  // save existing node
496  // precondition: the node exists in the database
497 
498  // log action
499  $this->logAction(&$node);
500 
501  // save node
502  $sortkey = $node->getValue('sort_key', DATATYPE_IGNORE);
503  if (!$sortkey) $sortkey = 0;
504  // save node element if exist
505  if (in_array(DATATYPE_ELEMENT, $node->getDataTypes()))
506  {
507  $elementValue = $node->getValue($node->getType(), DATATYPE_ELEMENT);
508  $properties = $node->getValueProperties($node->getType(), DATATYPE_ELEMENT);
509  if ($properties['data_type'] != '')
510  $sqlStr = "UPDATE ".$this->_dbPrefix."nodes SET ".$properties['data_type']."=".$this->_conn->qstr($elementValue).", sort_key=".$sortkey." WHERE id=".$nodeDef['id'].";";
511  }
512  else
513  $sqlStr = "UPDATE ".$this->_dbPrefix."nodes SET sort_key=".$sortkey." WHERE id=".$nodeDef['id'].";";
514  if ($this->_conn->Execute($sqlStr) === false)
515  WCMFException::throwEx("Error updating node ".$node->getType().": ".$this->_conn->ErrorMsg(), __FILE__, __LINE__);
516 
517  // save attributes
518  $attributeNames = $node->getValueNames(DATATYPE_ATTRIBUTE);
519  foreach($attributeNames as $attributeName)
520  {
521  // prepare attribute data
522  $attributeValue = $node->getValue($attributeName, DATATYPE_ATTRIBUTE);
523  $attributeProperties = $node->getValueProperties($attributeName, DATATYPE_ATTRIBUTE);
524  if ($attributeProperties == null)
525  WCMFException::throwEx("Can't save ".$node->getType().".".$attributeName.": No properties found.", __FILE__, __LINE__);
526 
527  // see if attribute exists
528  $sqlStr = "SELECT ".$this->_dbPrefix."attribs.id FROM ".$this->_dbPrefix."attribs, ".$this->_dbPrefix."nodes, ".$this->_dbPrefix."attrib_def
529  WHERE ".$this->_dbPrefix."attribs.fk_nodes_id=".$nodeDef['id']." AND ".$this->_dbPrefix."attribs.fk_attrib_def_id=".$this->_dbPrefix."attrib_def.id
530  AND ".$this->_dbPrefix."attrib_def.attrib_name='".$attributeName."';";
531  $attribId = null;
532  if ($rs = &$this->_conn->Execute($sqlStr))
533  {
534  $attribute = $rs->FetchRow();
535  $attribId = $attribute['id'];
536  }
537 
538  if (!$attribId)
539  {
540  if ($attributeValue != '')
541  {
542  // insert attribute data, if not empty
543  $sqlStr = "INSERT INTO ".$this->_dbPrefix."attribs (fk_nodes_id, fk_attrib_def_id, ".$attributeProperties['data_type'].")
544  VALUES (".$nodeDef['id'].", ".$attributeProperties['id'].", ".$this->_conn->qstr($attributeValue).");";
545  if ($this->_conn->Execute($sqlStr) === false)
546  WCMFException::throwEx("Error updating node ".$node->getType().": ".$this->_conn->ErrorMsg(), __FILE__, __LINE__);
547  }
548  }
549  else
550  {
551  // save attribute data
552  $sqlStr = "UPDATE ".$this->_dbPrefix."attribs SET ".$attributeProperties['data_type']."=".$this->_conn->qstr($attributeValue)."
553  WHERE id=".$attribId.";";
554  if ($this->_conn->Execute($sqlStr) === false)
555  WCMFException::throwEx("Error updating node ".$node->getType().": ".$this->_conn->ErrorMsg(), __FILE__, __LINE__);
556  }
557  }
558  }
559  $node->setState(STATE_CLEAN, false);
560  // postcondition: the node is saved to the db
561  // the node oid is updated
562  // the node state is STATE_CLEAN
563  // attributes are only inserted if their values differ from ''
564  return true;
565  }
566  /**
567  * Delete a Node from the database (together with all of its children).
568  * @param oid The object id of the Node to delete
569  * @param recursive True/False whether to physically delete it's children too [default: true]
570  * @return True/False depending on success of operation
571  */
572  function delete($oid, $recursive=true)
573  {
574  $persistenceFacade = &PersistenceFacade::getInstance();
575  $nodeDef = $persistenceFacade->decomposeOID($oid);
576 
577  // log action
578  if ($this->isLogging())
579  {
580  $obj = &$persistenceFacade->load($oid, BUILDDEPTH_SINGLE);
581  if ($obj)
582  $this->logAction(&$obj);
583  }
584  // delete node
585  $sqlStr = "DELETE FROM ".$this->_dbPrefix."nodes WHERE id=".$nodeDef['id'].";";
586  if ($this->_conn->Execute($sqlStr) === false)
587  WCMFException::throwEx("Error deleting node ".$oid.": ".$this->_conn->ErrorMsg(), __FILE__, __LINE__);
588  $sqlStr = "DELETE FROM ".$this->_dbPrefix."attribs WHERE fk_nodes_id=".$nodeDef['id'].";";
589  if ($this->_conn->Execute($sqlStr) === false)
590  WCMFException::throwEx("Error deleting node ".$oid.": ".$this->_conn->ErrorMsg(), __FILE__, __LINE__);
591 
592  // delete children
593  if ($recursive)
594  {
595  $sqlStr = "SELECT ".$this->_dbPrefix."nodes.id, element_name FROM ".$this->_dbPrefix."nodes, ".$this->_dbPrefix."elements
596  WHERE ".$this->_dbPrefix."nodes.fk_n_elements_id=".$this->_dbPrefix."elements.id AND ".$this->_dbPrefix."nodes.fk_nodes_id=".$nodeDef['id'].";";
597  $rs = &$this->_conn->Execute($sqlStr);
598  while ($rs && $childData = $rs->FetchRow())
599  {
600  $childoid = $persistenceFacade->composeOID(array('type' => $childData['element_name'], 'id' => $childData['id']));
601  $persistenceFacade->delete($childoid, $recursive);
602  }
603  }
604  // postcondition: the node and all of its children are deleted from db
605  return true;
606  }
607  /**
608  * Get the database connection.
609  * @return A reference to the ADONewConnection object
610  */
611  function &getConnection()
612  {
613  return $this->_conn;
614  }
615  /**
616  * See if a child Node is grouped with a root Node.
617  * @param childElementId The element id of the child Node
618  * @param parentElementId The element id of the parent Node (necessary to identify relation)
619  * @param rootElementId The element id of the root Node
620  * @return true/false
621  */
622  function isGroupChild($childElementId, $parentElementId, $rootElementId)
623  {
624  $key = $childElementId.'_'.$parentElementId.'_'.$rootElementId;
625  if (isset($this->_groupMap[$key]))
626  return $this->_groupMap[$key];
627  else
628  {
629  $groupped = false;
630  $sqlStr = "SELECT fk_elements_id, grouproot FROM ".$this->_dbPrefix."element_relations WHERE fk_elements_child_id=".$childElementId.";";
631  $rs = &$this->_conn->Execute($sqlStr);
632  while ($rs && $relation = $rs->FetchRow())
633  if ($relation['grouproot'] == $rootElementId && $relation['fk_elements_id'] == $parentElementId)
634  $grouped = true;
635  $this->_groupMap[$key] = $grouped;
636  return $grouped;
637  }
638  }
639  /**
640  * Get the object ids of nodes matching a given criteria.
641  * @param type The type of the object
642  * @param criteria An assoziative array holding name value pairs of attributes (maybe null). Values maybe substrings of values to search for. [default: null]
643  * @return An array containing the node oids
644  */
645  function getOIDs($type, $criteria=null)
646  {
647  $persistenceFacade = &PersistenceFacade::getInstance();
648  $oids = array();
649 
650  // use criteria condition if criteria is given
651  $attribTableStr = '';
652  $attribCondStr = '';
653  if ($criteria != null)
654  {
655  $i = 1;
656  foreach($criteria as $name => $value)
657  {
658  $attribTableStr .= ", ".$this->_dbPrefix."attribs as attribs".$i.", ".$this->_dbPrefix."attrib_def as attrib_def".$i." ";
659  $attribCondStr .= "AND attribs".$i.".fk_nodes_id=".$this->_dbPrefix."nodes.id AND attribs".$i.".fk_attrib_def_id=attrib_def".$i.".id AND attrib_def".$i.".attrib_name='".$name."' AND
660  (attribs".$i.".data_txt LIKE '%".$value."%' OR attribs".$i.".data_blob LIKE '%".$value."%' OR attribs".$i.".data_date LIKE '%".$value."%' OR attribs".$i.".data_float LIKE '%".$value."%' OR attribs".$i.".data_int LIKE '%".$value."%' OR attribs".$i.".data_boolean LIKE '%".$value."%') ";
661  $i++;
662  }
663  }
664 
665  $sqlStr = "SELECT ".$this->_dbPrefix."nodes.id FROM ".$this->_dbPrefix."nodes, ".$this->_dbPrefix."elements ".$attribTableStr."
666  WHERE ".$this->_dbPrefix."nodes.fk_n_elements_id=".$this->_dbPrefix."elements.id AND ".$this->_dbPrefix."elements.element_name='".$type."' ".$attribCondStr;
667  $rs = &$this->_conn->Execute($sqlStr);
668  if (!$rs)
669  WCMFException::throwEx($this->_conn->ErrorMsg(), __FILE__, __LINE__);
670  else
671  {
672  while ($row = $rs->FetchRow())
673  array_push($oids, $persistenceFacade->composeOID(array('type' => $type, 'id' => $row['id'])));
674  }
675  return $oids;
676  }
677  /**
678  * Get a property (element of the node or element of a child node) of all nodes of a given type (e.g. shortnames of all contractors).
679  * If attribute is given only properties with the given value of this attribute are returned (e.g. shortnames which language attribute is 'en').
680  * @note The properties database type MUST be 'data_txt'.
681  * @param type The type of the node (e.g. 'contractor')
682  * @param property The type of the child node (e.g. 'shortname'). If property is null the element of the type itself will be taken
683  * @param attribute An assoziative array holding name value pairs of attributes (maybe null). Values maybe substrings of values to search for. [default: null]
684  * @param oid If oid is given the property map will only be build for this single node [default: null]
685  * @return An assoziative array containing the ids of the type nodes as keys and the element values of the property nodes as values
686  */
687  function getPropertyMap($type, $property, $attribute=null, $oid=null)
688  {
689  $map = array();
690  $persistenceFacade = &PersistenceFacade::getInstance();
691 
692  // use id condition if id is given
693  $idCondition = '';
694  if ($oid != null)
695  {
696  $nodeDef = $persistenceFacade->decomposeOID($oid);
697  $idCondition = $this->_dbPrefix.'nodes.id='.$nodeDef['id'].' AND';
698  }
699 
700  // use attribute condition if attribute is given
701  $attribTableStr = '';
702  $attribCondStr = '';
703  if ($attribute != null)
704  {
705  $i = 1;
706  foreach($attribute as $name => $value)
707  {
708  $attribTableStr .= ", ".$this->_dbPrefix."attribs as attribs".$i.", ".$this->_dbPrefix."attrib_def as attrib_def".$i." ";
709  $attribCondStr .= "AND attribs".$i.".fk_nodes_id=nodes2.id AND attribs".$i.".fk_attrib_def_id=attrib_def".$i.".id AND attrib_def".$i.".attrib_name='".$name."' AND
710  (attribs".$i.".data_txt LIKE '%".$value."%' OR attribs".$i.".data_blob LIKE '%".$value."%' OR attribs".$i.".data_date LIKE '%".$value."%' OR attribs".$i.".data_float LIKE '%".$value."%' OR attribs".$i.".data_int LIKE '%".$value."%' OR attribs".$i.".data_boolean LIKE '%".$value."%') ";
711  $i++;
712  }
713  }
714 
715  // get properties
716  if ($property != null)
717  $sqlStr = "SELECT ".$this->_dbPrefix."nodes.id, nodes2.data_txt
718  FROM ".$this->_dbPrefix."nodes, ".$this->_dbPrefix."nodes as nodes2, ".$this->_dbPrefix."elements, ".$this->_dbPrefix."elements as elements2 ".$attribTableStr."
719  WHERE ".$idCondition." ".$this->_dbPrefix."nodes.id=nodes2.fk_nodes_id AND ".$this->_dbPrefix."nodes.fk_n_elements_id=".$this->_dbPrefix."elements.id
720  AND ".$this->_dbPrefix."elements.element_name='".$type."' AND nodes2.fk_n_elements_id=elements2.id AND elements2.element_name='".$property."' ".$attribCondStr.";";
721  else
722  $sqlStr = "SELECT ".$this->_dbPrefix."nodes.id, ".$this->_dbPrefix."nodes.data_txt FROM ".$this->_dbPrefix."nodes, ".$this->_dbPrefix."nodes as nodes2,
723  ".$this->_dbPrefix."elements ".$attribTableStr."
724  WHERE ".$idCondition." ".$this->_dbPrefix."nodes.id=nodes2.id AND ".$this->_dbPrefix."nodes.fk_n_elements_id=".$this->_dbPrefix."elements.id
725  AND ".$this->_dbPrefix."elements.element_name='".$type."' ".$attribCondStr.";";
726  $rs = &$this->_conn->Execute($sqlStr);
727  if (!$rs)
728  WCMFException::throwEx($this->_conn->ErrorMsg(), __FILE__, __LINE__);
729  else
730  {
731  while ($row = $rs->FetchRow())
732  {
733  $oid = $persistenceFacade->composeOID(array('type' => $type, 'id' => $row['id']));
734  $map[$oid] = $this->_dataConverter->convertStorageToApplication($row['data_txt'], 'data_txt', '');
735  }
736  }
737  return $map;
738  }
739  /**
740  * Get the object id of the (next) parent (of a given type) of a node.
741  * @param oid The oid of the node to find the parent for
742  * @param type The type of the parent to find (if null the next parent of any type is returned)
743  * @param buildDepth One of the constants BUILDDEPTH_SINGLE (direct parent), BUILDDEPTH_GROUPED (group parent) [default: BUILDDEPTH_SINGLE]
744  * @return The oid of the found parent Node / null if not found
745  */
746  function getParentOID($oid, $type=null, $buildDepth=BUILDDEPTH_SINGLE)
747  {
748  $parentOID = null;
749  $persistenceFacade = &PersistenceFacade::getInstance();
750  $nodeDef = $persistenceFacade->decomposeOID($oid);
751 
752  if ($buildDepth == BUILDDEPTH_GROUPED)
753  {
754  // grouped mode
755  // get the element type of the grouproot
756  $sqlStr = "SELECT ".$this->_dbPrefix."elements.element_name FROM ".$this->_dbPrefix."nodes, ".$this->_dbPrefix."nodes as parentnode, ".$this->_dbPrefix."elements,
757  ".$this->_dbPrefix."element_relations
758  WHERE ".$this->_dbPrefix."nodes.fk_n_elements_id=".$this->_dbPrefix."element_relations.fk_elements_child_id
759  AND parentnode.fk_n_elements_id=".$this->_dbPrefix."element_relations.fk_elements_id AND ".$this->_dbPrefix."nodes.fk_nodes_id=parentnode.id
760  AND ".$this->_dbPrefix."element_relations.grouproot=".$this->_dbPrefix."elements.id AND ".$this->_dbPrefix."nodes.id=".$nodeDef['id'];
761  /*
762  $sqlStr = "SELECT elements.element_name FROM nodes, elements, element_relations
763  WHERE nodes.fk_n_elements_id=element_relations.fk_elements_child_id AND element_relations.grouproot=elements.id AND nodes.id=".$nodeDef['id'];
764  */
765  $rs = &$this->_conn->Execute($sqlStr);
766  if (!$rs)
767  WCMFException::throwEx($this->_conn->ErrorMsg(), __FILE__, __LINE__);
768  else
769  {
770  $row = $rs->FetchRow();
771  // get next parent with grouproots element name
772  return $this->getParentOID($oid, $row['element_name'], BUILDDEPTH_SINGLE);
773  }
774  }
775  else
776  {
777  // ungrouped mode
778  $sqlStr = "SELECT ".$this->_dbPrefix."nodes.id, ".$this->_dbPrefix."nodes.fk_nodes_id, ".$this->_dbPrefix."elements.element_name
779  FROM ".$this->_dbPrefix."nodes, ".$this->_dbPrefix."elements WHERE ".$this->_dbPrefix."nodes.fk_n_elements_id=".$this->_dbPrefix."elements.id
780  AND ".$this->_dbPrefix."nodes.id=".$nodeDef['id'].";";
781  $rs = &$this->_conn->Execute($sqlStr);
782  if (!$rs)
783  WCMFException::throwEx($this->_conn->ErrorMsg(), __FILE__, __LINE__);
784  else
785  {
786  $row = $rs->FetchRow();
787  $parentOID = $persistenceFacade->composeOID(array('type' => $row['element_name'], 'id' => $row['fk_nodes_id']));
788  // the parent id is $row['fk_nodes_id']
789  if ($type != null)
790  {
791  // find parent of given type
792  while ($row['element_name'] != $type && $row['fk_nodes_id'] != '')
793  {
794  $sqlStr = "SELECT ".$this->_dbPrefix."nodes.id, ".$this->_dbPrefix."nodes.fk_nodes_id, ".$this->_dbPrefix."elements.element_name
795  FROM ".$this->_dbPrefix."nodes, ".$this->_dbPrefix."elements WHERE ".$this->_dbPrefix."nodes.fk_n_elements_id=".$this->_dbPrefix."elements.id
796  AND ".$this->_dbPrefix."nodes.id=".$row['fk_nodes_id'].";";
797  $rs = &$this->_conn->Execute($sqlStr);
798  if (!$rs)
799  WCMFException::throwEx($this->_conn->ErrorMsg(), __FILE__, __LINE__);
800  else
801  $row = $rs->FetchRow();
802  if ($row['element_name'] != '' || $row['id'] != '')
803  $parentOID = $persistenceFacade->composeOID(array('type' => $row['element_name'], 'id' => $row['id']));
804  else
805  $parentOID = null;
806  }
807  }
808  }
809  }
810  return $parentOID;
811  }
812 }
813 ?>
NodeToSingleTableMapper maps nodes of different types to the database. It uses a relational database ...
const STATE_DIRTY
Node is the basic component for building trees (although a Node can have one than more parents)...
Definition: class.Node.php:118
const BUILDTYPE_COMPLETE
& load($oid, $buildDepth, $buildType=BUILDTYPE_COMPLETE, $depth=0)
getParentOID($oid, $type=null, $buildDepth=BUILDDEPTH_SINGLE)
throwEx($message, $file='', $line='')
const BUILDDEPTH_INFINITE
const STATE_CLEAN
isGroupChild($childElementId, $parentElementId, $rootElementId)
const STATE_NEW
const BUILDDEPTH_REQUIRED
DataConverter is the base class for all converter classes. It defines the interface for converting da...
getPropertyMap($type, $property, $attribute=null, $oid=null)
const BUILDDEPTH_GROUPED
PersistenceMapper is the base class for all mapper classes.
& create($type, $buildDepth, $buildType=BUILDTYPE_COMPLETE, $depth=0)
const DATATYPE_ATTRIBUTE