NodeUtil.php
1 <?php
2 /**
3  * wCMF - wemove Content Management Framework
4  * Copyright (C) 2005-2020 wemove digital solutions GmbH
5  *
6  * Licensed under the terms of the MIT License.
7  *
8  * See the LICENSE file distributed with this work for
9  * additional information.
10  */
11 namespace wcmf\lib\model;
12 
21 use wcmf\lib\util\StringUtil;
22 
23 /**
24  * NodeUtil provides services for the Node class. All methods are static.
25  *
26  * @author ingo herwig <ingo@wemove.com>
27  */
28 class NodeUtil {
29 
30  /**
31  * Get the shortest paths that connect a type to another type.
32  * @param $type The type to start from
33  * @param $otherRole The role of the type at the other end (maybe null, if only type shoudl match)
34  * @param $otherType The type at the other end (maybe null, if only role shoudl match)
35  * @param $hierarchyType The hierarchy type that the other type has in relation to this type
36  * 'parent', 'child', 'undefined' or 'all' to get all relations (default: 'all')
37  * @return An array of PathDescription instances
38  */
39  public static function getConnections($type, $otherRole, $otherType, $hierarchyType='all') {
40  $paths = [];
41  self::getConnectionsImpl($type, $otherRole, $otherType, $hierarchyType, $paths);
42  $minLength = -1;
43  $shortestPaths = [];
44  foreach ($paths as $curPath) {
45  $curLength = $curPath->getPathLength();
46  if ($minLength == -1 || $minLength > $curLength) {
47  $minLength = $curLength;
48  $shortestPaths = [$curPath];
49  }
50  elseif ($curLength == $minLength) {
51  $shortestPaths[] = $curPath;
52  }
53  }
54  return $shortestPaths;
55  }
56 
57  /**
58  * Get the relations that connect a type to another type.
59  * @param $type The type to start from
60  * @param $otherRole The role of the type at the other end (maybe null, if only type shoudl match)
61  * @param $otherType The type at the other end (maybe null, if only role shoudl match)
62  * @param $hierarchyType The hierarchy type that the other type has in relation to this type
63  * 'parent', 'child', 'undefined' or 'all' to get all relations (default: 'all')
64  * @param $result Array of PathDescriptions after execution
65  * @param $currentPath Internal use only
66  */
67  protected static function getConnectionsImpl($type, $otherRole, $otherType,
68  $hierarchyType, array &$result=[], array $currentPath=[]) {
69  $persistenceFacade = ObjectFactory::getInstance('persistenceFacade');
70  $mapper = $persistenceFacade->getMapper($type);
71 
72  // check relations
73  $relationDescs = $mapper->getRelations($hierarchyType);
74  foreach ($relationDescs as $relationDesc) {
75  // loop detection
76  $loopDetected = false;
77  foreach ($currentPath as $pathPart) {
78  if ($relationDesc->isSameRelation($pathPart)) {
79  $loopDetected = true;
80  break;
81  }
82  }
83  if ($loopDetected) {
84  // continue with next relation
85  continue;
86  }
87 
88  $pathFound = null;
89  $nextType = $relationDesc->getOtherType();
90  $nextRole = $relationDesc->getOtherRole();
91  $otherTypeFq = $otherType != null ? $persistenceFacade->getFullyQualifiedType($otherType) : null;
92  if (($otherRole != null && $nextRole == $otherRole) || ($otherType != null && $nextType == $otherTypeFq)) {
93  // other end found -> terminate
94  $pathFound = $currentPath;
95  $pathFound[] = $relationDesc;
96  }
97  else {
98  // nothing found -> proceed with next generation
99  $nextCurrentPath = $currentPath;
100  $nextCurrentPath[] = $relationDesc;
101  self::getConnectionsImpl($nextType, $otherRole, $otherType, $hierarchyType, $result, $nextCurrentPath);
102  }
103 
104  // if a path is found, add it to the result
105  if ($pathFound) {
106  $result[] = new PathDescription($pathFound);
107  }
108  }
109  }
110 
111  /**
112  * Get the query condition used to select all related Nodes of a given role.
113  * @param $node The Node to select the relatives for
114  * @param $otherRole The role of the other nodes
115  * @return The condition string to be used with StringQuery.
116  */
117  public static function getRelationQueryCondition($node, $otherRole) {
118  $mapper = $node->getMapper();
119  $relationDescription = $mapper->getRelation($otherRole);
120  $otherType = $relationDescription->getOtherType();
121 
122  $query = new ObjectQuery($otherType, __CLASS__.__METHOD__.$node->getType().$otherRole);
123  // add the primary keys of the node
124  // using the role name as alias (avoids ambiguous paths)
125  $nodeTpl = $query->getObjectTemplate($node->getType(), $relationDescription->getThisRole());
126  $oid = $node->getOID();
127  $ids = $oid->getId();
128  $i = 0;
129  foreach ($mapper->getPkNames() as $pkName) {
130  $nodeTpl->setValue($pkName, Criteria::asValue("=", $ids[$i++]));
131  }
132  // add the other type in the given relation
133  $otherTpl = $query->getObjectTemplate($otherType);
134  $nodeTpl->addNode($otherTpl, $otherRole);
135  $condition = $query->getQueryCondition();
136  // prevent selecting all objects, if the condition is empty
137  if (strlen($condition) == 0) {
138  $condition = 0;
139  }
140  return $condition;
141  }
142 
143  /**
144  * Get the display value for a Node defined by the 'displayValues' property.
145  * If the 'displayValues' property is an array the items will be put together with ' - '.
146  * If the 'displayValues' property is empty the function returns an empty string.
147  * Example: 'name,text' shows the name of the Node together with the content of the text attribute
148  * @param $node Node instance to display
149  * @param $language The language if values should be localized. Optional, default is Localization::getDefaultLanguage()
150  * @return String
151  */
152  public static function getDisplayValue(Node $node, $language=null) {
153  return join(' - ', array_values(self::getDisplayValues($node, $language)));
154  }
155 
156  /**
157  * Does the same as NodeUtil::getDisplayValue but returns the display values as associative array
158  * @param $node Node instance to display
159  * @param $language The language if values should be localized. Optional, default is Localization::getDefaultLanguage()
160  * @return Array of strings
161  */
162  public static function getDisplayValues(Node $node, $language=null) {
163  // localize node if requested
164  $localization = ObjectFactory::getInstance('localization');
165  if ($language != null) {
166  $node = $localization->loadTranslation($node, $language);
167  }
168 
169  $displayArray = [];
170  $displayValuesNames = $node->getProperty('displayValues');
171  if (sizeof($displayValuesNames) > 0) {
172  $mapper = $node->getMapper();
173  foreach($displayValuesNames as $displayValueName) {
174  $inputType = ''; // needed for the translation of a list value
175  if ($displayValueName != '') {
176  if ($mapper->hasAttribute($displayValueName)) {
177  $attribute = $mapper->getAttribute($displayValueName);
178  $inputType = $attribute->getInputType();
179  }
180  $tmpDisplay = $node->getValue($displayValueName);
181  }
182 
183  // translate any list value
184  $tmpDisplay = ValueListProvider::translateValue($tmpDisplay, $inputType, $language);
185  if (strlen($tmpDisplay) == 0) {
186  // fallback to oid
187  $tmpDisplay = $node->getOID();
188  }
189 
190  $displayArray[$displayValueName] = $tmpDisplay;
191  }
192  }
193  return $displayArray;
194  }
195 
196  /**
197  * Make all urls matching a given base url in a Node relative.
198  * @param $node Node instance that holds the value
199  * @param $baseUrl The baseUrl to which matching urls will be made relative
200  * @param $recursive Boolean whether to recurse into child Nodes or not (default: true)
201  */
202  public static function makeNodeUrlsRelative(Node $node, $baseUrl, $recursive=true) {
203  // use NodeValueIterator to iterate over all Node values
204  // and call the global convert function on each
205  $iter = new NodeValueIterator($node, $recursive);
206  for($iter->rewind(); $iter->valid(); $iter->next()) {
207  self::makeValueUrlsRelative($iter->currentNode(), $iter->key(), $baseUrl);
208  }
209  }
210 
211  /**
212  * Make the urls matching a given base url in a PersistentObject value relative.
213  * @param $node Node instance that holds the value
214  * @param $valueName The name of the value
215  * @param $baseUrl The baseUrl to which matching urls will be made relative
216  */
217  private static function makeValueUrlsRelative(PersistentObject $object, $valueName, $baseUrl) {
218  $value = $object->getValue($valueName);
219 
220  // find urls in texts
221  $urls = StringUtil::getUrls($value);
222  // find direct attribute urls
223  if (strpos($value, 'http://') === 0 || strpos($value, 'https://') === 0) {
224  $urls[] = $value;
225  }
226  // process urls
227  foreach ($urls as $url) {
228  // convert absolute urls matching baseUrl
229  $urlConv = $url;
230  if (strpos($url, $baseUrl) === 0) {
231  $urlConv = str_replace($baseUrl, '', $url);
232  }
233  // replace url
234  $value = str_replace($url, $urlConv, $value);
235  }
236  $object->setValue($valueName, $value);
237  }
238 
239  /**
240  * Translate all list values in a list of Nodes.
241  * @note Translation in this case refers to mapping list values from the key to the value
242  * and should not be confused with localization, although values maybe localized using the
243  * language parameter.
244  * @param $nodes A reference to the array of Node instances
245  * @param $language The language code, if the translated values should be localized.
246  * Optional, default is Localizat$objectgetDefaultLanguage()
247  * @param $itemDelim Delimiter string for array values (optional, default: ", ")
248  */
249  public static function translateValues(&$nodes, $language=null, $itemDelim=", ") {
250  // translate the node values
251  for($i=0; $i<sizeof($nodes); $i++) {
252  $iter = new NodeValueIterator($nodes[$i], false);
253  for($iter->rewind(); $iter->valid(); $iter->next()) {
254  self::translateValue($iter->currentNode(), $iter->key(), $language, $itemDelim);
255  }
256  }
257  }
258 
259  /**
260  * Translate a PersistentObject list value.
261  * @param $object The object whose value to translate
262  * @param $valueName The name of the value to translate
263  * @param $language The language to use
264  * @param $itemDelim Delimiter string for array values (optional, default: ", ")
265  */
266  public static function translateValue(PersistentObject $object, $valueName, $language, $itemDelim=", ") {
267  $value = $object->getValue($valueName);
268  // translate list values
269  $value = ValueListProvider::translateValue($value, $object->getValueProperty($valueName, 'input_type'), $language, $itemDelim);
270  // force set (the rendered value may not be satisfy validation rules)
271  $object->setValue($valueName, $value, true);
272  }
273 
274  /**
275  * Remove all values from a Node that are not a display value.
276  * @param $node The Node instance
277  */
278  public static function removeNonDisplayValues(Node $node) {
279  $displayValues = $node->getProperty('displayValues');
280  $valueNames = $node->getValueNames();
281  foreach($valueNames as $name) {
282  if (!in_array($name, $displayValues)) {
283  $node->removeValue($name);
284  }
285  }
286  }
287 
288  /**
289  * Remove all values from a Node that are not a primary key value.
290  * @param $node The Node instance
291  */
292  public static function removeNonPkValues(Node $node) {
293  $mapper = $node->getMapper();
294  $pkValues = $mapper->getPkNames();
295  $valueNames = $node->getValueNames();
296  foreach($valueNames as $name) {
297  if (!in_array($name, $pkValues)) {
298  $node->removeValue($name);
299  }
300  }
301  }
302 }
303 ?>
static makeNodeUrlsRelative(Node $node, $baseUrl, $recursive=true)
Make all urls matching a given base url in a Node relative.
Definition: NodeUtil.php:202
static getConnections($type, $otherRole, $otherType, $hierarchyType='all')
Get the shortest paths that connect a type to another type.
Definition: NodeUtil.php:39
getObjectTemplate($type, $alias=null, $combineOperator=Criteria::OPERATOR_AND)
Get an object template for a given type.
static getDisplayValue(Node $node, $language=null)
Get the display value for a Node defined by the 'displayValues' property.
Definition: NodeUtil.php:152
getValueNames($excludeTransient=false)
Definition: Node.php:81
NodeValueIterator is used to iterate over all persistent values of a Node (not including relations).
static getConnectionsImpl($type, $otherRole, $otherType, $hierarchyType, array &$result=[], array $currentPath=[])
Get the relations that connect a type to another type.
Definition: NodeUtil.php:67
getValue($name)
Definition: Node.php:97
static getDisplayValues(Node $node, $language=null)
Does the same as NodeUtil::getDisplayValue but returns the display values as associative array.
Definition: NodeUtil.php:162
static asValue($operator, $value)
Factory method for constructing a Criteria that may be used as value on a PersistentObject's attribut...
Definition: Criteria.php:58
static removeNonPkValues(Node $node)
Remove all values from a Node that are not a primary key value.
Definition: NodeUtil.php:292
setValue($name, $value, $forceSet=false, $trackChange=true)
Set the value of an attribute if it exists.
Criteria defines a condition on a PersistentObject's attribute used to select specific instances.
Definition: Criteria.php:21
getValue($name)
Get the value of an attribute.
removeValue($name)
Definition: Node.php:152
static translateValues(&$nodes, $language=null, $itemDelim=", ")
Translate all list values in a list of Nodes.
Definition: NodeUtil.php:249
getValueProperty($name, $property)
Get the value of one property of an attribute.
ValueListProvider provides lists of key/values to be used with list input controls.
static translateValue(PersistentObject $object, $valueName, $language, $itemDelim=", ")
Translate a PersistentObject list value.
Definition: NodeUtil.php:266
static removeNonDisplayValues(Node $node)
Remove all values from a Node that are not a display value.
Definition: NodeUtil.php:278
Node adds the concept of relations to PersistentObject.
Definition: Node.php:34
static getInstance($name, $dynamicConfiguration=[])
getQueryCondition()
Get the condition part of the query.
PersistentObject defines the interface of all persistent objects.
static getRelationQueryCondition($node, $otherRole)
Get the query condition used to select all related Nodes of a given role.
Definition: NodeUtil.php:117
Node related interfaces and classes.
Definition: namespaces.php:26
NodeUtil provides services for the Node class.
Definition: NodeUtil.php:28
ObjectFactory implements the service locator pattern by wrapping a Factory instance and providing sta...
PathDescription describes a path between two types.
static translateValue($value, $inputType, $language=null, $itemDelim=", ")
Translate a value with use of it's assoziated input type e.g.
ObjectQuery implements a template based object query.