Skip to content

Commit

Permalink
Improve Hierarchical mixin
Browse files Browse the repository at this point in the history
Changed:
- Added method `hasMasterObject()`.
- Added methods `loadHierarchy()` and `loadSiblings()`.
- Fixed type-hints related to `ModelInterface` and `HierarchicalInterface`.
  • Loading branch information
mcaskill committed Jan 29, 2024
1 parent a3b6653 commit cfead6c
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 45 deletions.
7 changes: 7 additions & 0 deletions src/Charcoal/Object/HierarchicalInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ interface HierarchicalInterface
*/
public function hasMaster();

/**
* Determine if this object's immediate parent exists.
*
* @return boolean
*/
public function hasMasterObject();

/**
* Determine if this object is the head (top-level) of its hierarchy.
*
Expand Down
114 changes: 69 additions & 45 deletions src/Charcoal/Object/HierarchicalTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ trait HierarchicalTrait
*
* @var string|integer|null
*/
protected $master;
protected $master = null;

/**
* Store a copy of the object's ancestry.
Expand All @@ -32,21 +32,21 @@ trait HierarchicalTrait
*
* @var HierarchicalInterface[]|null
*/
private $children;
private $children = null;

/**
* Store a copy of the object's siblings.
*
* @var HierarchicalInterface[]|null
*/
private $siblings;
private $siblings = null;

/**
* The object's parent object, if any, in the hierarchy.
*
* @var HierarchicalInterface|null
*/
private $masterObject;
private $masterObject = null;

/**
* A store of cached objects.
Expand Down Expand Up @@ -128,14 +128,24 @@ public function getMasterObject()
return $this->masterObject;
}

/**
* Determine if this object's immediate parent exists.
*
* @return boolean
*/
public function hasMasterObject()
{
return (bool)$this->getMasterObject();
}

/**
* Determine if this object has a direct parent.
*
* @return boolean
*/
public function hasMaster()
{
return ($this->getMaster() !== null);
return (bool)$this->getMaster();
}

/**
Expand All @@ -147,7 +157,7 @@ public function hasMaster()
*/
public function isTopLevel()
{
return ($this->getMaster() === null);
return !$this->getMaster();
}

/**
Expand Down Expand Up @@ -201,34 +211,44 @@ public function toplevelMaster()
*/
public function hasParents()
{
return !!count($this->hierarchy());
return count($this->hierarchy()) > 0;
}

/**
* Retrieve this object's ancestors (from immediate parent to top-level).
*
* @return array
* @return HierarchicalInterface[]
*/
public function hierarchy()
{
if (!isset($this->hierarchy)) {
$hierarchy = [];
$master = $this->getMasterObject();
while ($master) {
$hierarchy[] = $master;
$master = $master->getMasterObject();
}

$this->hierarchy = $hierarchy;
if ($this->hierarchy === null) {
$this->hierarchy = $this->loadHierarchy();
}

return $this->hierarchy;
}

/**
* Build this object's ancestors (from immediate parent to top-level).
*
* @return HierarchicalInterface[]
*/
public function loadHierarchy()
{
$hierarchy = [];
$master = $this->getMasterObject();
while ($master) {
$hierarchy[] = $master;
$master = $master->getMasterObject();
}

return $hierarchy;
}

/**
* Retrieve this object's ancestors, inverted from top-level to immediate.
*
* @return array
* @return HierarchicalInterface[]
*/
public function invertedHierarchy()
{
Expand Down Expand Up @@ -290,15 +310,15 @@ public function numChildren()
* Get the total number of children in the entire hierarchy.
* This method counts all children and sub-children, unlike `numChildren()` which only count 1 level.
* @return integer
* @todo Implementation needed.
*/
public function recursiveNumChildren()
{
// TODO
return 0;
}

/**
* @param array $children The children to set.
* @param mixed[] $children The children to set.
* @return HierarchicalInterface Chainable
*/
public function setChildren(array $children)
Expand Down Expand Up @@ -336,7 +356,7 @@ public function addChild($child)

/**
* Get the children directly under this object.
* @return array
* @return ModelInterface[]
*/
public function children()
{
Expand All @@ -361,11 +381,8 @@ abstract public function loadChildren();
public function isChildOf($master)
{
$master = $this->objFromIdent($master);
if ($master === null) {
return false;
}

return ($master->id() === $this->getMaster());
return ($master && $master->id() === $this->getMaster());
}

/**
Expand All @@ -378,8 +395,8 @@ public function recursiveIsChildOf($master)
return true;
}

if ($this->hasParents() && $this->getMasterObject()->recursiveIsChildOf($master)) {
return true;
if ($this->hasParents() && $this->hasMasterObject()) {
return $this->getMasterObject()->recursiveIsChildOf($master);
}

return false;
Expand Down Expand Up @@ -407,24 +424,32 @@ public function numSiblings()

/**
* Get all the objects on the same level as this one.
* @return array
* @return ModelInterface[]
*/
public function siblings()
{
if ($this->siblings !== null) {
return $this->siblings;
if ($this->siblings === null) {
$this->siblings = $this->loadSiblings();
}

return $this->siblings;
}

/**
* Get all the objects on the same level as this one.
* @return ModelInterface[]
* @todo Implementation needed.
*/
public function loadSiblings()
{
$master = $this->getMasterObject();
if ($master === null) {
// Todo: return all top-level objects.
$siblings = [];
} else {
if ($master) {
// Todo: Remove "current" object from siblings
$siblings = $master->children();
return $master->children();
}
$this->siblings = $siblings;

return $this->siblings;
// TODO: return all top-level objects.
return [];
}

/**
Expand All @@ -440,7 +465,7 @@ public function isSiblingOf($sibling)

/**
* @param mixed $ident The ident.
* @return HierarchicalInterface|null
* @return (HierarchicalInterface&ModelInterface)|null
* @throws InvalidArgumentException If the identifier is not a scalar value.
*/
private function objFromIdent($ident)
Expand Down Expand Up @@ -472,7 +497,6 @@ private function objFromIdent($ident)
}

$obj = $this->loadObjectFromSource($ident);

if ($obj !== null) {
$this->addObjectToCache($obj);
}
Expand All @@ -484,7 +508,7 @@ private function objFromIdent($ident)
* Retrieve an object from the storage source by its ID.
*
* @param mixed $id The object id.
* @return null|HierarchicalInterface
* @return (HierarchicalInterface&ModelInterface)|null
*/
private function loadObjectFromSource($id)
{
Expand All @@ -493,25 +517,25 @@ private function loadObjectFromSource($id)

if ($obj->id()) {
return $obj;
} else {
return null;
}

return null;
}

/**
* Retrieve an object from the cache store by its ID.
*
* @param mixed $id The object id.
* @return null|HierarchicalInterface
* @return (HierarchicalInterface&ModelInterface)|null
*/
private function loadObjectFromCache($id)
{
$objType = $this->objType();
if (isset(static::$objectCache[$objType][$id])) {
return static::$objectCache[$objType][$id];
} else {
return null;
}

return null;
}

/**
Expand Down

0 comments on commit cfead6c

Please sign in to comment.