Commit ceda9d08 authored by Ad Schellevis's avatar Ad Schellevis

(mvc) improve API for array type model objects

Integrate uuid generation into the core of the model system so items are uniquely identified and can be directly accessed using their uuids.

For example:
// set recurring pipe item to 1001
$model->setNodeByReference('pipes.pipe.86f19d48-bc91-4c70-a81c-e05cdd3a4372.queue', "1001");

// echo the contents back
echo $model->getNodeByReference('pipes.pipe.86f19d48-bc91-4c70-a81c-e05cdd3a4372.queue');
parent 69d4aa9f
...@@ -153,19 +153,26 @@ abstract class BaseModel ...@@ -153,19 +153,26 @@ abstract class BaseModel
if ($fieldObject instanceof ArrayField) { if ($fieldObject instanceof ArrayField) {
// handle Array types, recurring items // handle Array types, recurring items
if ($config_section_data != null) { if ($config_section_data != null) {
$counter = 0 ;
foreach ($config_section_data as $conf_section) { foreach ($config_section_data as $conf_section) {
// Array items are identified by a UUID, read from attribute or create a new one
if (isset($conf_section->attributes()->uuid)) {
$tagUUID = $conf_section->attributes()['uuid']->__toString();
} else {
$tagUUID = $internal_data->generateUUID();
}
// iterate array items from config data // iterate array items from config data
$child_node = new ContainerField($fieldObject->__reference . "." . ($counter++), $tagName); $child_node = new ContainerField($fieldObject->__reference . "." . $tagUUID, $tagName);
$this->parseXml($xmlNode, $conf_section, $child_node); $this->parseXml($xmlNode, $conf_section, $child_node);
$fieldObject->addChildNode(null, $child_node); $fieldObject->addChildNode($tagUUID, $child_node);
} }
} else { } else {
// There's no content in config.xml for this array node. // There's no content in config.xml for this array node.
$child_node = new ContainerField($fieldObject->__reference . ".0", $tagName); $tagUUID = $internal_data->generateUUID();
$child_node = new ContainerField($fieldObject->__reference . ".".$tagUUID, $tagName);
$child_node->setInternalIsVirtual(); $child_node->setInternalIsVirtual();
$this->parseXml($xmlNode, $config_section_data, $child_node); $this->parseXml($xmlNode, $config_section_data, $child_node);
$fieldObject->addChildNode(null, $child_node); $fieldObject->addChildNode($tagUUID, $child_node);
} }
} else { } else {
// All other node types (Text,Email,...) // All other node types (Text,Email,...)
......
...@@ -34,27 +34,11 @@ namespace OPNsense\Base\FieldTypes; ...@@ -34,27 +34,11 @@ namespace OPNsense\Base\FieldTypes;
*/ */
class ArrayField extends BaseField class ArrayField extends BaseField
{ {
/**
* @var int item index
*/
private $internalArrayCounter = 0 ;
/** /**
* @var null|BaseField node to use for copying * @var null|BaseField node to use for copying
*/ */
private $internalTemplateNode = null; private $internalTemplateNode = null;
/**
* add Childnode (list), ignore the name of this item
* @param string $name property name
* @param BaseField $node content (must be of type BaseField)
*/
public function addChildNode($name, $node)
{
$this->internalChildnodes[(string)$this->internalArrayCounter] = $node;
$this->internalArrayCounter++;
}
/** /**
* Copy first node pointer as template node to make sure we always have a template to create new nodes from. * Copy first node pointer as template node to make sure we always have a template to create new nodes from.
* If the first node is virtual (no source data), remove that from the list. * If the first node is virtual (no source data), remove that from the list.
...@@ -63,20 +47,13 @@ class ArrayField extends BaseField ...@@ -63,20 +47,13 @@ class ArrayField extends BaseField
{ {
// always make sure there's a node to copy our structure from // always make sure there's a node to copy our structure from
if ($this->internalTemplateNode ==null) { if ($this->internalTemplateNode ==null) {
$this->internalTemplateNode = $this->internalChildnodes["0"]; $firstKey = array_keys($this->internalChildnodes)[0];
$this->internalTemplateNode = $this->internalChildnodes[$firstKey];
/** /**
* if first node is empty, remove reference node. * if first node is empty, remove reference node.
*/ */
if ($this->internalChildnodes["0"]->getInternalIsVirtual()) { if ($this->internalChildnodes[$firstKey]->getInternalIsVirtual()) {
unset($this->internalChildnodes["0"]); unset($this->internalChildnodes[$firstKey]);
$this->internalArrayCounter--;
}
}
// check if all children have a uuid, generate one if missing
foreach ($this->internalChildnodes as $nodeKey => $node) {
if (!array_key_exists('uuid', $node->getAttributes())) {
$node->setAttributeValue("uuid", $this->generateUUID());
} }
} }
} }
...@@ -97,8 +74,9 @@ class ArrayField extends BaseField ...@@ -97,8 +74,9 @@ class ArrayField extends BaseField
$new_record[$key] = clone $node ; $new_record[$key] = clone $node ;
} }
$nodeUUID = $this->generateUUID();
$container_node = new ContainerField( $container_node = new ContainerField(
$this->__reference . "." . $this->internalArrayCounter, $this->__reference . "." . $nodeUUID,
$this->internalXMLTagName $this->internalXMLTagName
); );
...@@ -110,17 +88,17 @@ class ArrayField extends BaseField ...@@ -110,17 +88,17 @@ class ArrayField extends BaseField
} }
// make sure we have a UUID on repeating child items // make sure we have a UUID on repeating child items
$container_node->setAttributeValue("uuid", $this->generateUUID()); $container_node->setAttributeValue("uuid", $nodeUUID);
// add node to this object // add node to this object
$this->addChildNode(null, $container_node); $this->addChildNode($nodeUUID, $container_node);
return $container_node; return $container_node;
} }
/** /**
* remove item by id (number) * remove item by id (number)
* @param $index index number * @param string $index index number
*/ */
public function del($index) public function del($index)
{ {
...@@ -129,38 +107,43 @@ class ArrayField extends BaseField ...@@ -129,38 +107,43 @@ class ArrayField extends BaseField
} }
} }
/** /**
* search child item by UUID * @param string|array $fieldNames sort by fieldname
* @param $uuid item uuid * @param bool $descending sort descending
* @return BaseField|null * @return array
*/ */
public function findByUUID($uuid) public function sortedBy($fieldNames, $descending = false)
{ {
foreach ($this->internalChildnodes as $nodeKey => $node) { // reserve at least X number of characters for every field to improve sorting of multiple fields
$nodeAttr = $node->getAttributes(); $MAX_KEY_LENGTH = 30;
if (array_key_exists('uuid', $nodeAttr) && $nodeAttr['uuid'] == $uuid) {
return $node;
}
}
return null; // fieldnames may be a list or a single item, always convert to a list
if (!is_array($fieldNames)) {
$fieldNames = array($fieldNames);
} }
/**
* search child item by UUID // collect sortable data as key/value store
* @param $uuid item uuid $sortedData=array();
* @return bool
*/
public function delByUUID($uuid)
{
foreach ($this->internalChildnodes as $nodeKey => $node) { foreach ($this->internalChildnodes as $nodeKey => $node) {
$nodeAttr = $node->getAttributes(); // populate sort key
if (array_key_exists('uuid', $nodeAttr) && $nodeAttr['uuid'] == $uuid) { $sortKey = '';
unset($this->internalChildnodes[$nodeKey]); foreach ($fieldNames as $fieldName) {
return true; if (array_key_exists($fieldName, $node->internalChildnodes)) {
$sortKey .= sprintf("%".$MAX_KEY_LENGTH."s ,", $node->$fieldName) ;
}
} }
$sortKey .= $nodeKey; // prevent overwrite of duplicates
$sortedData[$sortKey] = $node ;
} }
return false;
// sort by key on ascending or descending order
if (!$descending) {
ksort($sortedData);
} else {
krsort($sortedData);
}
return array_values($sortedData);
} }
} }
...@@ -93,7 +93,7 @@ abstract class BaseField ...@@ -93,7 +93,7 @@ abstract class BaseField
/** /**
* @return string uuid v4 number * @return string uuid v4 number
*/ */
protected function generateUUID() public function generateUUID()
{ {
return sprintf( return sprintf(
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x', '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment