wCMF  3.6
 All Classes Namespaces Files Functions Variables Groups Pages
class.PersistentObject.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.PersistentObject.php 1462 2014-02-04 23:52:27Z iherwig $
18  */
19 require_once(BASE."wcmf/lib/core/class.WCMFException.php");
20 require_once(BASE."wcmf/lib/util/class.Message.php");
21 require_once(BASE."wcmf/lib/util/class.Storable.php");
22 require_once(BASE."wcmf/lib/persistence/class.PersistenceMapper.php");
23 require_once(BASE."wcmf/lib/persistence/class.LockManager.php");
24 require_once(BASE."wcmf/lib/util/class.SearchUtil.php");
25 
26 /**
27  * Some constants describing the state of the PersistentObject
28  */
29 define("STATE_CLEAN", 0);
30 define("STATE_DIRTY", 1);
31 define("STATE_NEW", 2);
32 define("STATE_DELETED", 3);
33 
34 /**
35  * Some constants describing the data types
36  */
37 define("DATATYPE_DONTCARE", 0);
38 define("DATATYPE_ATTRIBUTE", 1);
39 define("DATATYPE_ELEMENT", 2);
40 define("DATATYPE_IGNORE", 3); // all data items >= DATATYPE_IGNORE wont be shown in human readable node discriptions
41 
42 /**
43  * @class PersistentObject
44  * @ingroup Persistence
45  * @brief PersistentObject is the base class of all persistent objects.
46  * It implements the basic persistence methods (save(), delete())
47  * which will be delegated to the PersistenceMapper class that contsructed the object.
48  * The PersistentObject holds the object data in an associative array of the following structure:
49  * @verbatim
50  * datatype1---valueName1---value
51  * properties---propertyName1---value
52  * propertyName2---value
53  * ...
54  * valueName2---value
55  * properties---propertyName1---value
56  * ...
57  * ...
58  * datatype2---valueName1---value
59  * properties---propertyName1---value
60  * ...
61  * ...
62  * ...
63  *
64  * e.g.: $this->_data[DATATYPE_ATTRIBUTE]['name']['value'] gives the value of the attribute 'name' which is of app_data_type DATATYPE_ATTRIBUTE
65  * $this->_data[DATATYPE_ATTRIBUTE]['name']['properties']['visible'] gives the value of the visibility property of the
66  * attribute 'name'
67  * @endverbatim
68  * @author ingo herwig <ingo@wemove.com>
69  */
71 {
72  var $_oid = null; // object identifier
73  var $_type = ''; // the object type
74  var $_data = array(); // associative array holding the data
75  var $_properties = array(); // associative array holding the properties
76  var $_state = STATE_CLEAN; // the state of the PersistentObject
77  var $_isImmutable = false; // immutable state
78  var $_changeListeners = array(); // the change listeners
79 
80  /**
81  * Constructor. The object will be bound to the appripriate PersistenceMapper automatically, if the
82  * the PersistenceFacade knows the type.
83  * @param type The object type.
84  * @param oid The object id (, optional will be calculated if not given or not valid).
85  */
86  function PersistentObject ($type, $oid=null)
87  {
88  $this->_type = $type;
89 
90  // set oid and state
91  if (!(isset($oid)) || !PersistenceFacade::isValidOID($oid))
92  {
93  // no oid is given -> new node
94  $this->setOID(PersistenceFacade::composeOID(array('type' => $type)));
95  $this->setState(STATE_NEW);
96  }
97  else
98  {
99  // old node
100  $this->setOID($oid);
101  $this->setState(STATE_CLEAN);
102  }
103  }
104  /**
105  * Get the type of the object.
106  * @return The objects type.
107  */
108  function getType ()
109  {
110  return $this->_type;
111  }
112  /**
113  * Set the type of the object.
114  * @param type The objects type.
115  */
116  function setType ($type)
117  {
118  $this->_type = $type;
119  }
120  /**
121  * Get the base type of the object. This might differ from the type, if the object may
122  * exist in different roles and has one of these roles currently. The default implementation
123  ' returns the object's type.
124  * @return The objects base type.
125  */
126  function getBaseType ()
127  {
128  return $this->_type;
129  }
130  /**
131  * Get the object id of the PersistentObject.
132  * @return The PersistentObject's oid.
133  */
134  function getOID ()
135  {
136  return $this->_oid;
137  }
138  /**
139  * Get the base object id of the PersistentObject (see PersistentObject::getBaseType()).
140  * @return The PersistentObject's oid.
141  */
142  function getBaseOID ()
143  {
144  return PersistenceFacade::getBaseOID($this->getOID());
145  }
146  /**
147  * Set the object id of the PersistentObject.
148  * @param oid The PersistentObject's oid.
149  */
150  function setOID ($oid)
151  {
152  $mapper = &$this->getMapper();
153  if ($mapper != null)
154  {
155  // update the primary key columns
156  $oidParts = PersistenceFacade::decomposeOID($oid);
157  $pkValues = $mapper->getPkNames();
158  $pkNames = array_keys($pkValues);
159  for ($i=0; $i<sizeof($pkNames); $i++) {
160  $this->setValue($pkNames[$i], $oidParts['id'][$i], $pkValues[$pkNames[$i]], true);
161  }
162  }
163  // set this afterwards, because setValue may have triggered updateOID
164  $this->_oid = $oid;
165  }
166  /**
167  * Get the id of the object in the persistent storage.
168  * @note This is NOT a application unique id
169  * @return The id.
170  */
171  function getDBID ()
172  {
173  $id = PersistenceFacade::getOIDParameter($this->_oid, 'id');
174  return $id[0];
175  }
176  /**
177  * Set the id of the object in the persistent storage.
178  * @note This is NOT a application unique id
179  * @param id The id.
180  */
181  function setDBID ($id)
182  {
183  $this->setOID(PersistenceFacade::composeOID(array('type' => $this->_type, 'id' => array($id))));
184  }
185  /**
186  * Get the PersistenceMapper of the object.
187  * @return A reference to a PersistenceMapper class
188  */
189  function &getMapper()
190  {
191  $mapper = null;
192 
193  // set the mapper, if defined in PersistenceFacade
194  $persistenceFacade = &PersistenceFacade::getInstance();
195  if (PersistenceFacade::isKnownType($this->_type)) {
196  $mapper = &$persistenceFacade->getMapper($this->_type);
197  }
198  return $mapper;
199  }
200  /**
201  * Get the DataConverter used when loading/saving values.
202  * @return A reference to the dataConverter instance
203  */
204  function &getDataConverter()
205  {
206  $mapper = &$this->getMapper();
207  if ($mapper != null) {
208  return $mapper->getDataConverter();
209  }
210  return null;
211  }
212  /**
213  * Save data. This call will be delegated to the PersistenceFacade class.
214  */
215  function save()
216  {
217  if (!$this->_isImmutable)
218  {
219  $oldState = $this->getState();
220  // call before hook method
221  if ($oldState == STATE_NEW) {
222  $this->beforeInsert();
223  }
224  elseif ($oldState == STATE_DIRTY) {
225  $this->beforeUpdate();
226  }
227  // save the object
228  $persistenceFacade = &PersistenceFacade::getInstance();
229  $persistenceFacade->save($this);
230 
231  // update search index
233 
234  // call after hook method
235  if ($oldState == STATE_NEW) {
236  $this->afterInsert();
237  }
238  elseif ($oldState == STATE_DIRTY) {
239  $this->afterUpdate();
240  }
241  }
242  else {
243  WCMFException::throwEx(Message::get("Authorization failed for action '%1%' on '%2%'.", array(ACTION_MODIFY, $this->getOID())), __FILE__, __LINE__);
244  }
245  }
246  /**
247  * Delete data. This call will be delegated to the PersistenceFacade class.
248  * @param recursive True/False whether to physically delete it's children too [default: true]
249  */
250  function delete($recursive=true)
251  {
252  if (!$this->_isImmutable)
253  {
254  // call before hook method
255  $this->beforeDelete();
256 
257  // delete the object
258  $persistenceFacade = &PersistenceFacade::getInstance();
259  $persistenceFacade->delete($this->getOID(), $recursive);
260 
261  // remove from index
263 
264  // call after hook method
265  $this->afterDelete();
266  }
267  else {
268  WCMFException::throwEx(Message::get("Authorization failed for action '%1%' on '%2%'.", array(ACTION_DELETE, $this->getOID())), __FILE__, __LINE__);
269  }
270  }
271  /**
272  * Get the object's state:
273  * @return One of the STATE constant values:
274  */
275  function getState ()
276  {
277  return $this->_state;
278  }
279  /**
280  * Set the state of the object to one of the STATE constants.
281  * @param recursive True/False [Default: True]
282  * @note PersistentObject ignores the recursive parameter, but subclasses may use it
283  */
284  function setState ($state, $recursive=true)
285  {
286  $oldState = $this->_state;
287  switch ($this->_state)
288  {
289  // new object must stay new when it's modified
290  case STATE_NEW:
291  switch ($state)
292  {
293  case STATE_DIRTY:
294  $this->_state = STATE_NEW;
295  break;
296 
297  default:
298  $this->_state = $state;
299  }
300  break;
301 
302  // deleted object must stay deleted in every case
303  case STATE_DELETED:
304  $this->_state = STATE_DELETED;
305  break;
306 
307  default:
308  $this->_state = $state;
309  }
310  if ($oldState != $this->_state) {
311  $this->propagateStateChange($oldState, $this->_state);
312  }
313  }
314  /**
315  * Set object immutable. Sets the editable property of each value to false.
316  * and disables save/delete methods.
317  * @note This operation is not reversible (reload the object to get a mutable one)
318  */
319  function setImmutable()
320  {
321  // set editable attribute of all values to false
322  foreach($this->getDataTypes() as $type) {
323  foreach($this->getValueNames($type) as $name) {
324  $this->setValueProperty($name, 'is_editable', false, $type);
325  }
326  }
327  $this->_isImmutable = true;
328  }
329  /**
330  * Get the lock on the object.
331  * @return lock The lock as provided by LockManager::getLock() or null if not locked
332  * @note If the object is locked it's set immutable. This is not reversible
333  * (reload the object to get a mutable one)
334  */
335  function getLock ()
336  {
337  $lockManager = &LockManager::getInstance();
338  $lock = $lockManager->getLock($this->getOID());
339  if ($lock != null) {
340  $this->setImmutable();
341  }
342  return $lock;
343  }
344  /**
345  * Get a copy of the object (ChangeListeners and Lock are not copied)
346  * @return A reference to copy.
347  */
348  function &duplicate ()
349  {
350  $class = get_class($this);
351  $copy = new $class;
352  $copy->_oid = $this->_oid;
353  $copy->_type = $this->_type;
354  $copy->_data = $this->_data;
355  $copy->_properties = $this->_properties;
356  $copy->_state = $this->_state;
357  $copy->_isImmutable = $this->_isImmutable;
358 
359  return $copy;
360  }
361  /**
362  * Copy all non-empty values to a given instance (ChangeListeners are triggered)
363  * @param object A reference to the PersistentObject to copy the values to.
364  * @param dataTypes An array of datatypes. Only values of that datatypes will be copied.
365  * Empty array means all datatypes [default:empty array]
366  * @param copyPkValues True/False wether primary key values should be copied if their
367  * datatype is included or not [default: true]
368  */
369  function copyValues (&$object, $dataTypes=array(), $copyPkValues=true)
370  {
371  $valuesToIgnore = array();
372  if (!$copyPkValues) {
373  $mapper = &$this->getMapper();
374  if ($mapper) {
375  $valuesToIgnore = $mapper->getPkNames();
376  }
377  }
378  $processor = new NodeProcessor('copyValueIntern', array(&$object, $dataTypes, $valuesToIgnore), $this);
379  $processor->run($this, false);
380  }
381  /**
382  * Private callback for copying values
383  * @param targetnode The node to copy the value to
384  * @param dataTypes An array of datatypes. Only values of that datatypes will be copied.
385  * @param valuesToIgnore An associative array with the value names as keys and the types as values
386  * @see NodeProcessor
387  */
388  function copyValueIntern (&$node, $valueName, $dataType, &$targetNode, $dataTypes, $valuesToIgnore)
389  {
390  if (sizeof($dataTypes) == 0 || in_array($dataType, $dataTypes))
391  {
392  if (!isset($valuesToIgnore[$valueName]) || $valuesToIgnore[$valueName] != $dataType) {
393  $value = $node->getValue($valueName, $dataType);
394  if (strlen($value) > 0) {
395  $targetNode->setValue($valueName, $value, $dataType, true);
396  }
397  }
398  }
399  }
400  /**
401  * Clear all values. Set each value to null.
402  * @param dataTypes An array of datatypes. Only values of that datatypes will be cleared.
403  * Empty array means all datatypes [default:empty array]
404  */
405  function clearValues ($dataTypes=array())
406  {
407  $processor = new NodeProcessor('clearValueIntern', array($dataTypes), $this);
408  $processor->run($this, false);
409  }
410  /**
411  * Private callback for clearing values
412  * @see NodeProcessor
413  */
414  function clearValueIntern (&$node, $valueName, $dataType, $dataTypes)
415  {
416  if (sizeof($dataTypes) == 0 || in_array($dataType, $dataTypes)) {
417  $node->setValue($valueName, null, $dataType);
418  }
419  }
420  /**
421  * Recalculate the object id
422  */
423  function updateOID ()
424  {
425  $mapper = &$this->getMapper();
426  if ($mapper != null)
427  {
428  $oidParts = array('type' => $this->getType(), 'id' => array());
429  // collect the values of the primary keys and compose the oid from them
430  $pkValues = $mapper->getPkNames();
431  foreach (array_keys($pkValues) as $pkName) {
432  array_push($oidParts['id'], $this->getValue($pkName, $pkValues[$pkName]));
433  }
434  $this->_oid = PersistenceFacade::composeOID($oidParts);
435  }
436  }
437 
438  /**
439  * Persistence hook methods.
440  * Subclasses may override this to implement special application requirements.
441  * The default implementations do nothing.
442  */
443  /**
444  * This method is called once after creation of this object. At this time it
445  * is not known in the store.
446  */
447  function afterCreate() {}
448  /**
449  * This method is called once before inserting the newly created object into the store.
450  */
451  function beforeInsert() {}
452  /**
453  * This method is called once after inserting the newly created object into the store.
454  */
455  function afterInsert() {}
456  /**
457  * This method is called always after loading the object from the store.
458  */
459  function afterLoad() {}
460  /**
461  * This method is called always before updating the modified object in the store.
462  */
463  function beforeUpdate() {}
464  /**
465  * This method is called always after updating the modified object in the store.
466  */
467  function afterUpdate() {}
468  /**
469  * This method is called once before deleting the object from the store.
470  */
471  function beforeDelete() {}
472  /**
473  * This method is called once after deleting the object from the store.
474  */
475  function afterDelete() {}
476 
477  /**
478  * Values and Properties
479  */
480 
481  /**
482  * Check if the node has a given item.
483  * @param name The name of the item to query.
484  * @param type The type of the item as defined in the subclass [optional]
485  * (if type is omitted the first item value of any type that matches will be returned)
486  * @return True/False wether the item exists or not.
487  */
488  function hasValue($name, $type=null)
489  {
490  return in_array($name, $this->getValueNames($type));
491  }
492  /**
493  * Get the value of a named item.
494  * @param name The name of the item to query.
495  * @param type The type of the item as defined in the subclass [optional]
496  * (if type is omitted the first item value of any type that matches will be returned)
497  * @return The value of the item / null if it doesn't exits.
498  */
499  function getValue($name, $type=null)
500  {
501  if ($type != null)
502  {
503  if (is_array($this->_data[$type]) && isset($this->_data[$type][$name])) {
504  return $this->_data[$type][$name]['value'];
505  }
506  else {
507  return null;
508  }
509  }
510  else
511  {
512  foreach($this->_data as $curDataArray) {
513  if (isset($curDataArray[$name])) {
514  return $curDataArray[$name]['value'];
515  }
516  }
517  }
518  return null;
519  }
520  /**
521  * Remove a named item.
522  * @param name The name of the item to remove.
523  * @param type The type of the item as defined in the subclass [optional]
524  * (if type is omitted the first item value of any type that matches will be returned)
525  */
526  function removeValue($name, $type=null)
527  {
528  if ($type != null)
529  {
530  if (is_array($this->_data[$type]) && array_key_exists($name, $this->_data[$type])) {
531  unset($this->_data[$type][$name]);
532  }
533  }
534  else
535  {
536  foreach(array_keys($this->_data) as $type) {
537  if (is_array($this->_data[$type]) && array_key_exists($name, $this->_data[$type])) {
538  unset($this->_data[$type][$name]);
539  }
540  }
541  }
542  }
543  /**
544  * Get the unconverted value of a named item.
545  * @param name The name of the item to query.
546  * @param type The type of the item as defined in the subclass [optional]
547  * (if type is omitted the first item value of any type that matches will be returned)
548  * @return The unconverted value of the item / null if it doesn't exits.
549  */
550  function getUnconvertedValue($name, $type=null)
551  {
552  $value = $this->getValue($name, $type);
553  $dataConverter = $this->getDataConverter();
554  if (is_object($dataConverter) && $value != null) {
555  $value = $dataConverter->convertApplicationToStorage($value, $this->getValueProperty($name, 'db_data_type', $type), $name);
556  }
557  return $value;
558  }
559  /**
560  * Get the converted value of a named item.
561  * @param name The name of the item to query.
562  * @param type The type of the item as defined in the subclass [optional]
563  * (if type is omitted the first item value of any type that matches will be returned)
564  * @return The converted value of the item / null if it doesn't exits.
565  * @note The result is normally equal to that of PersistentObject::getValue() except, when the value is unconverted
566  */
567  function getConvertedValue($name, $type=null)
568  {
569  $value = $this->getValue($name, $type);
570  $dataConverter = $this->getDataConverter();
571  if (is_object($dataConverter) && $value != null) {
572  $value = $dataConverter->convertStorageToApplication($value, $this->getValueProperty($name, 'db_data_type', $type), $name);
573  }
574  return $value;
575  }
576  /**
577  * Get the type of a named item.
578  * @param name The name of the item to query.
579  * @return An array containig the types of the value (in most cases this array will have length 1).
580  */
581  function getValueTypes($name)
582  {
583  $types = array();
584  foreach(array_keys($this->_data) as $type) {
585  if (in_array($name, array_keys($this->_data[$type]))) {
586  array_push($types, $type);
587  }
588  }
589  return $types;
590  }
591  /**
592  * Validate all values
593  * @return Empty string if validation succeeded, an error string else. The default implementation returns the result of
594  * PersistentObject::validateValueAgainstRestrictions().
595  */
596  function validateValues ()
597  {
598  $result = '';
599  $processor = new NodeProcessor('validateValueIntern', array(&$result), $this);
600  $processor->run($this, false);
601  return $result;
602  }
603  /**
604  * Private callback for validating values
605  * @param errorMsg A string to append error messages to
606  * @see NodeProcessor
607  */
608  function validateValueIntern (&$node, $valueName, $dataType, &$errorMsg)
609  {
610  $error = $node->validateValue($valueName, $value, $dataType);
611  if (strlen($error) > 0) {
612  $errorMsg .= $error."\n";
613  }
614  }
615  /**
616  * Check if data may be set. The method is also called, when setting a value.
617  * Controller may call this method before setting data and saving the object.
618  * @param name The name of the item to set.
619  * @param value The value of the item.
620  * @param type The type of the item as defined in the subclass [optional]
621  * (if type is omitted the first item of any type that matches will be validated)
622  * @return Empty string if validation succeeded, an error string else. The default implementation returns the result of
623  * PersistentObject::validateValueAgainstRestrictions().
624  * @note Subclasses will override this method to implement special application requirements.
625  */
626  function validateValue($name, $value, $type=null)
627  {
628  return $this->validateValueAgainstRestrictions($name, $value, $type);
629  }
630  /**
631  * Check a value's value against the restrictions set on it. This method uses the
632  * 'restrictions_match' and 'restrictions_not_match' value properties.
633  * @param name The name of the item to set.
634  * @param value The value of the item.
635  * @param type The type of the item as defined in the subclass [optional]
636  * (if type is omitted the first item of any type that matches will be checked)
637  * @return Empty string if validation succeeded, an error string else.
638  */
639  function validateValueAgainstRestrictions($name, $value, $type=null)
640  {
641  $valueProperties = $this->getValueProperties($name, $type);
642  $restrictionsMatch = $valueProperties['restrictions_match'];
643  $restrictionsNotMatch = $valueProperties['restrictions_not_match'];
644  if (($restrictionsMatch == '' || preg_match("/".$restrictionsMatch."/m", $value)) &&
645  ($restrictionsNotMatch == '' || !preg_match("/".$restrictionsNotMatch."/m", $value))) {
646  return '';
647  }
648  // construct the error message
649  $errorMessage = Message::get("Wrong value for %1% (%2%). ", array($name, $value));
650 
651  // use configured message if existing
652  if (strlen($valueProperties['restrictions_description']) > 0) {
653  $errorMessage .= Message::get($valueProperties['restrictions_description']);
654  }
655  else
656  {
657  // construct default message
658  if (strlen($restrictionsMatch) > 0) {
659  $errorMessage .= Message::get("The value must match %1%.", array($restrictionsMatch));
660  }
661  if (strlen($restrictionsNotMatch) > 0) {
662  $errorMessage .= Message::get("The value must NOT match %1%.", array($restrictionsNotMatch));
663  }
664  }
665  return $errorMessage;
666  }
667  /**
668  * Set the value of a named item if it exists.
669  * @param name The name of the item to set.
670  * @param value The value of the item.
671  * @param type The type of the item as defined in the subclass [optional]
672  * (if type is omitted the first item of any type that matches will be modified)
673  * @param forceSet Set the value even if it is already set and validation would fail (used to notify listeners) [default: false]
674  * @return true if operation succeeds / false else
675  */
676  function setValue($name, $value, $type=null, $forceSet=false)
677  {
678  if (!$forceSet)
679  {
680  $validationResult = $this->validateValue($name, $value, $type);
681  if (strlen($validationResult) > 0) {
682  WCMFException::throwEx("Invalid value (".$value.") for ".$this->getOID().".".$name.": ".$validationResult, __FILE__, __LINE__);
683  }
684  }
685  $oldValue = $this->getValue($name, $type);
686  if ($type != null && strlen($type) > 0)
687  {
688  // construct type array if it doesn't exist
689  if (!isset($this->_data[$type]))
690  {
691  $this->_data[$type] = array();
692  }
693  // construct name array if it doesn't exist
694  if (!isset($this->_data[$type][$name]))
695  {
696  $this->_data[$type][$name] = array();
697  }
698  // set value
699  if ($this->_data[$type][$name]['value'] !== $value || $forceSet)
700  {
701  $this->_data[$type][$name]['value'] = $value;
703  $mapper = &$this->getMapper();
704  if ($mapper != null && in_array($name, array_keys($mapper->getPKNames()))) {
705  $this->updateOID();
706  }
707  $this->propagateValueChange($name, $type, $oldValue, $value);
708  return true;
709  }
710  }
711  else
712  {
713  foreach(array_keys($this->_data) as $key)
714  {
715  if (array_key_exists($name, $this->_data[$key]))
716  {
717  if ($this->_data[$key][$name] != $value || $forceSet)
718  {
719  $this->_data[$key][$name]['value'] = $value;
721  $mapper = &$this->getMapper();
722  if ($mapper != null && in_array($name, array_keys($mapper->getPKNames()))) {
723  $this->updateOID();
724  }
725  $this->propagateValueChange($name, $key, $oldValue, $value);
726  }
727  return true;
728  }
729  }
730  }
731  return false;
732  }
733  /**
734  * Get the properties of a named item.
735  * @param name The name of the item to query.
736  * @param type The type of the item as defined in the subclass [optional]
737  * (if type is omitted the first item value of any type that matches will be returned)
738  * @return An associative array holding the properties of the item / null if it doesn't exits.
739  */
740  function getValueProperties ($name, $type=null)
741  {
742  if ($type != null)
743  {
744  if (isset($this->_data[$type][$name]['properties'])) {
745  return $this->_data[$type][$name]['properties'];
746  }
747  else {
748  return null;
749  }
750  }
751  else
752  {
753  foreach($this->_data as $curDataArray)
754  {
755  if (isset($curDataArray[$name])) {
756  return $curDataArray[$name]['properties'];
757  }
758  }
759  }
760  return null;
761  }
762  /**
763  * Set the properties of a named item.
764  * @param name The name of the item to set its properties.
765  * @param properties An associative array holding the properties of the item.
766  * @param type The type of the item as defined in the subclass [optional]
767  * (if type is omitted the first item of any type that matches will be modified)
768  * @return true/false whether the value was set.
769  */
770  function setValueProperties ($name, $properties, $type=null)
771  {
772  if ($type != null)
773  {
774  // return false if type array doesn't exist
775  if (!isset($type, $this->_data)) {
776  return false;
777  }
778  // set properties if value exists
779  if (isset($name, $this->_data[$type]))
780  {
781  $this->_data[$type][$name]['properties'] = $properties;
783  return true;
784  }
785  }
786  else
787  {
788  foreach(array_keys($this->_data) as $key)
789  {
790  if (isset($name, $this->_data[$key]))
791  {
792  $this->_data[$key][$name]['properties'] = $properties;
794  return true;
795  }
796  }
797  }
798  return false;
799  }
800  /**
801  * Get the value of one property of a named item.
802  * @param name The name of the item to set its properties.
803  * @param property The name of the property to set.
804  * @param type The type of the item as defined in the subclass [optional]
805  * (if type is omitted the first item of any type that matches will be modified)
806  * @return The value property/null if not found.
807  */
808  function getValueProperty ($name, $property, $type=null)
809  {
810  $properties = $this->getValueProperties($name, $type);
811  if ($properties != null) {
812  return $properties[$property];
813  }
814  return false;
815  }
816  /**
817  * Set the value of one property of a named item.
818  * @param name The name of the item to set its properties.
819  * @param property The name of the property to set.
820  * @param value The value to set on the property.
821  * @param type The type of the item as defined in the subclass [optional]
822  * (if type is omitted the first item of any type that matches will be modified)
823  * @return true/false whether the value was set.
824  */
825  function setValueProperty ($name, $property, $value, $type=null)
826  {
827  $properties = $this->getValueProperties($name, $type);
828  if ($properties != null)
829  {
830  $properties[$property] = $value;
831  return $this->setValueProperties($name, $properties, $type);
832  }
833  return false;
834  }
835  /**
836  * Get the names of all items.
837  * @param type The type of the item as defined in the subclass [optional]
838  * (if type is omitted all names will be returned)
839  * @return array of all item names.
840  */
841  function getValueNames ($type=null)
842  {
843  $names = array();
844  if ($type == null)
845  {
846  foreach(array_keys($this->_data) as $key) {
847  $names = array_merge($names, array_keys($this->_data[$key]));
848  }
849  }
850  else
851  {
852  if (isset($this->_data[$type])) {
853  $names = array_keys($this->_data[$type]);
854  }
855  }
856  return $names;
857  }
858  /**
859  * Get all datatypes.
860  * @return array of all datatypes.
861  */
862  function getDataTypes ()
863  {
864  return array_keys($this->_data);
865  }
866  /**
867  * Get the value of a named property in the object.
868  * @param name The name of the property to query.
869  * @return The value of the property / null if it doesn't exits.
870  */
871  function getProperty ($name)
872  {
873  if (isset($this->_properties[$name])) {
874  return $this->_properties[$name];
875  }
876  else {
877  return null;
878  }
879  }
880  /**
881  * Set the value of a named property in the object.
882  * @param name The name of the property to set.
883  * @param value The value of the property to set.
884  */
885  function setProperty ($name, $value)
886  {
887  $oldValue = $this->getProperty($name);
888  $this->_properties[$name] = $value;
889  $this->propagatePropertyChange($name, $oldValue, $value);
890  }
891  /**
892  * Get the names of all properties in the object.
893  * @return An array consisting the names.
894  */
895  function getPropertyNames ()
896  {
897  return array_keys($this->_properties);
898  }
899 
900  /**
901  * ChangeListener Support
902  */
903 
904  /**
905  * Add a change listener (Must be of type ChangeListener).
906  * @param listener The ChangeListener.
907  */
908  function addChangeListener (&$listener)
909  {
910  $this->_changeListeners[sizeof($this->_changeListeners)] = &$listener;
911  }
912  /**
913  * Remove a change listener (Must be of type ChangeListener).
914  * @param listener The ChangeListener.
915  */
916  function removeChangeListener (&$listener)
917  {
918  for ($i=0, $count=sizeof($this->_changeListeners); $i<$count; $i++) {
919  if ($this->_changeListeners[$i]->getId() == $listener->getId()) {
920  unset($this->_changeListeners[$i]);
921  }
922  }
923  }
924  /**
925  * Notify ChangeListeners of value changes.
926  * @param name The name of the item that has changed.
927  * @param type The type of the item that has changed.
928  * @param oldValue The old value of the item that has changed
929  * @param newValue The new value of the item that has changed
930  */
931  function propagateValueChange ($name, $type, $oldValue, $newValue)
932  {
933  for ($i=0, $count=sizeof($this->_changeListeners); $i<$count; $i++) {
934  if(method_exists($this->_changeListeners[$i], 'valueChanged')) {
935  $this->_changeListeners[$i]->valueChanged($this, $name, $type, $oldValue, $newValue);
936  }
937  }
938  }
939  /**
940  * Notify ChangeListeners of property changes.
941  * @param name The name of the item that has changed.
942  * @param oldValue The old value of the item that has changed
943  * @param newValue The new value of the item that has changed
944  */
945  function propagatePropertyChange ($name, $oldValue, $newValue)
946  {
947  for ($i=0, $count=sizeof($this->_changeListeners); $i<$count; $i++) {
948  if(method_exists($this->_changeListeners[$i], 'propertyChanged')) {
949  $this->_changeListeners[$i]->propertyChanged($this, $name, $oldValue, $newValue);
950  }
951  }
952  }
953  /**
954  * Notify ChangeListeners of state changes.
955  * @param oldValue The old value of the item that has changed
956  * @param newValue The new value of the item that has changed
957  */
958  function propagateStateChange ($oldValue, $newValue)
959  {
960  for ($i=0, $count=sizeof($this->_changeListeners); $i<$count; $i++) {
961  if(method_exists($this->_changeListeners[$i], 'stateChanged')) {
962  $this->_changeListeners[$i]->stateChanged($this, $oldValue, $newValue);
963  }
964  }
965  }
966 
967  /**
968  * Output
969  */
970 
971  /**
972  * Get the name of the type used for display.
973  * @return The name.
974  * @note Sublasses will override this for special application requirements
975  */
977  {
978  return Message::get($this->getType());
979  }
980  /**
981  * Get the description of the type.
982  * @return The description.
983  * @note Sublasses will override this for special application requirements
984  */
986  {
987  return Message::get($this->getType());
988  }
989  /**
990  * Get the value of the object used for display.
991  * @return The value.
992  * @note Sublasses will override this for special application requirements
993  */
994  function getDisplayValue()
995  {
996  return $this->toString();
997  }
998  /**
999  * Get the name of a value used for display.
1000  * @param name The name of the value.
1001  * @param type The type of the value (not used by the default implementation) [default: null]
1002  * @return The name of the value.
1003  * @note Sublasses will override this for special application requirements
1004  */
1005  function getValueDisplayName($name, $type=null)
1006  {
1007  return Message::get($name);
1008  }
1009  /**
1010  * Get the description of a value.
1011  * @param name The name of the value.
1012  * @param type The type of the value (not used by the default implementation) [default: null]
1013  * @return The description of the value.
1014  * @note Sublasses will override this for special application requirements
1015  */
1016  function getValueDescription($name, $type=null)
1017  {
1018  return Message::get($name);
1019  }
1020  /**
1021  * Get a string representation of the PersistentObject.
1022  * @param verbose True to get a verbose output [default: false]
1023  * @return The string representation of the PersistentObject.
1024  */
1025  function toString ($verbose=false)
1026  {
1027  $str = 'type:'.$this->getType().', ';
1028  $mapper = &$this->getMapper();
1029  if ($mapper != null)
1030  $str .= 'mapper:'.get_class($mapper).', ';
1031  $str .= 'oid:'.$this->getOID().' ';
1032  $str .= 'state:'.$this->getState().' ';
1033  $str .= 'PROPERTIES ';
1034  foreach($this->getPropertyNames() as $name)
1035  {
1036  $value = $this->getProperty($name);
1037  if (is_array($value)) {
1038  $str .= $name.':'.join(',', $value).' ';
1039  }
1040  else {
1041  $str .= $name.':'.$value.' ';
1042  }
1043  }
1044  $str .= "\n";
1045  $str .= 'VALUES ';
1046  $dataTypes = $this->getDataTypes();
1047  foreach($dataTypes as $type)
1048  {
1049  $str .= $type.'->{';
1050  $valueNames = $this->getValueNames($type);
1051  foreach($valueNames as $name)
1052  {
1053  $str .= $name.':'.$this->getValue($name, $type).' ';
1054  if ($verbose)
1055  {
1056  $valueProperties = $this->getValueProperties($name, $type);
1057  if (sizeOf($valueProperties) > 0)
1058  {
1059  $str .= '[';
1060  foreach($valueProperties as $key => $value) {
1061  $str .= $key.':'.$value.' ';
1062  }
1063  $str = substr($str, 0, strlen($str)-1);
1064  $str .= '] ';
1065  }
1066  }
1067  }
1068  $str = substr($str, 0, -1).'} ';
1069  }
1070  $str = substr($str, 0, -1);
1071  $str .= "\n";
1072  return $str;
1073  }
1074  /**
1075  * Check if the instance object is contained in the search index
1076  * @return True/False wether the object is contained or not
1077  */
1078  public function isIndexInSearch()
1079  {
1080  return (boolean) $this->getProperty('is_searchable') && $this->getType() == $this->getBaseType();
1081  }
1082 }
1083 ?>
getConvertedValue($name, $type=null)
const STATE_DIRTY
const STATE_DELETED
get($message, $parameters=null, $domain='', $lang='')
static deleteFromSearch(&$obj)
const ACTION_DELETE
getValueProperty($name, $property, $type=null)
NodeProcessor is used to iterate over all values of a Node and apply a given callback function...
getValue($name, $type=null)
throwEx($message, $file='', $line='')
setValueProperty($name, $property, $value, $type=null)
const STATE_CLEAN
propagateStateChange($oldValue, $newValue)
validateValueIntern(&$node, $valueName, $dataType, &$errorMsg)
removeValue($name, $type=null)
clearValueIntern(&$node, $valueName, $dataType, $dataTypes)
const STATE_NEW
decomposeOID($oid, $validate=true)
getValueProperties($name, $type=null)
getValueDescription($name, $type=null)
const ACTION_MODIFY
setValueProperties($name, $properties, $type=null)
setState($state, $recursive=true)
validateValue($name, $value, $type=null)
getBaseOID($oid, $validate=true)
This class defines the interface for classes that can be stored in the session.
getValueDisplayName($name, $type=null)
propagateValueChange($name, $type, $oldValue, $newValue)
PersistentObject($type, $oid=null)
getOIDParameter($oid, $param, $validate=true)
setValue($name, $value, $type=null, $forceSet=false)
static indexInSearch(&$obj)
copyValueIntern(&$node, $valueName, $dataType, &$targetNode, $dataTypes, $valuesToIgnore)
validateValueAgainstRestrictions($name, $value, $type=null)
hasValue($name, $type=null)
getUnconvertedValue($name, $type=null)
propagatePropertyChange($name, $oldValue, $newValue)
PersistentObject is the base class of all persistent objects. It implements the basic persistence met...
copyValues(&$object, $dataTypes=array(), $copyPkValues=true)
clearValues($dataTypes=array())