I'm struggling with the following situation:
I have an object of type Category with several private properties I want to extract into a separate data structure / array through its available getters: getId() , getTitle() , getLink() ,. It stores any nested subcategories as a property as well, which can be accessed through getter getSubCats() which will return an associative array of Categories with the corresponding ID's as keys.
I need to extract some properties and create a nested array (for use with json_encode) which fits the following structure:
<ID> => array(
'title' => <title>,
'url' => <url>,
'parent' => <parentID>,
'children' => array(
<ID> => array(
'title' => <title>,
'url' => <url>,
'parent' => <parentID>,
'children' => array(
...
),
<ID> => array(
'title' => <title>,
'url' => <url>,
'parent' => <parentID>,
'children' => array(
...
),
...
)
)
Maybe this can be done by using RecursiveIteratorIterator? Below are my last two approaches:
function buildTree_v1(&$oCat, &$aOutput = [], &$oParent = null) {
$actId = $oCat->getId();
$aOutput [$actId]['title'] = $oCat->getTitle();
$aOutput [$actId]['url'] = $oCat->getLink();
$aOutput [$actId]['parent'] = $oCat->getParentCategory()->getId() ?? null;
if ( $oCat->getHasVisibleSubCats() ) {
foreach ( $oCat->getSubCats() as $sId => &$oSubCat ) {
$aOutput [$actId][$sId] = [];
foreach ( $oSubCat as &$oSubChild ) {
buildTree_v1($oCat, $aOutput, $oSubChild);
}
}
}
}
// Try to split - only build the nested object structure as first step
function buildTree_v2(&$oCat) {
$return = [];
if ( $oCat->getHasVisibleSubCats() ) {
foreach ( $oCat->getSubCats() as &$oSubCat) {
if ( $oSubCat->getHasVisibleSubCats() ) {
$return[$oSubCat->getId()] = buildTree_v2($oSubCat);
} else {
$return[] = $oSubCat->getId();
}
}
}
return empty($return) ? null : $return;
}
Thanks in advance for any advice!
//Edit:
Input structure (relevant parts):
class Category {
protected $_aSubCats = array();
protected $_blHasVisibleSubCats;
protected $_aSeoUrls = array();
protected $_oParent = null;
protected $_sId;
protected $_sTitle;
public function getSubCats(): array<Category>;
public function getHasVisibleSubCats(): bool;
public function getLink($iLang = null): string;
public function getParentCategory(): ?Category;
public function getId(): string;
public function getTitle(): string;
}
An concrete example input object might look like:
$oInputCat = {Category}
-> $_aSubCats = array(
'316' => {Category},
'23' => {Category},
'262' => {Category}
)
-> $_blHasVisibleSubCats = true
-> $_aSeoUrl = 'https://example.com/Hardware'
-> $_oParent = {Category}
-> $_sId = '5068'
-> $_sTitle = 'Hardware'
/* Entries of $_aSubCats */
// '316' = {Category}
-> $_aSubCats = array()
-> $_blHasVisibleSubCats = false
-> $_aSeoUrl = 'https://example.com/Hardware/3D-Googles'
-> $_oParent = {Category}
-> $_sId = '316'
-> $_sTitle = '3D Googles'
// '23' = {Category}
-> $_aSubCats = array(
'26' => {Category}
)
-> $_blHasVisibleSubCats = true
-> $_aSeoUrl = 'https://example.com/Hardware/CPUs-and-Cooler'
-> $_oParent = {Category}
-> $_sId = '23'
-> $_sTitle = 'CPUs & Cooler'
// '262' = {Category}
-> $_aSubCats = array()
-> $_blHasVisibleSubCats = false
-> $_aSeoUrl = 'https://example.com/Hardware/Sound-Cards'
-> $_oParent = {Category}
-> $_sId = '262'
-> $_sTitle = 'Sound Cards'
// '26' = {Category} <-- example for a 2nd level subcategory
-> $_aSubCats = array()
-> $_blHasVisibleSubCats = false
-> $_aSeoUrl = 'https://example.com/Hardware/CPUs-and-Cooler/Cooler'
-> $_oParent = {Category}
-> $_sId = '26'
-> $_sTitle = 'Cooler'
The desired result would look like:
array(
'5068' => array(
'title' => 'Hardware',
'url' => 'https://example.com/Hardware',
'parent' => '12', // not listed - parent of the input category
'children' => array(
'316' => array(
'title' => '3D Googles',
'url' => 'https://example.com/Hardware/3D-Googles',
'parent' => '5068',
'children' => array(),
'23' => array(
'title' => 'CPUs & Cooler',
'url' => 'https://example.com/Hardware/CPUs-and-Cooler',
'parent' => '5068',
'children' => array(
'26' => array(
'title' => 'Cooler',
'url' => 'https://example.com/Hardware/CPUs-and-Cooler/Cooler',
'parent' => '23',
'children' => array(),
),
'262' => array(
'title' => 'Sound Cards',
'url' => 'https://example.com/Hardware/Sound-Cards',
'parent' => '5068',
'children' => array(),
)
)
)
The real object tree has an arbitrary number of branches and dimensions, but I think this should be enough to understand, what I want to do.
As you can see, the desired target could be described as "convert the objects to arrays while filtering some specific properties". I hope this will make things easier to understand.
Finally, I was able to solve this by myself.
Only for the archive, the following code will do the task:
/**
* Extracts all relevant properties of the given base category and all of its subcategories recursively
* and assigns this data structure to the given template variable.
*
* @param Category $oCat [mandatory] Target base category
* @param array $aOutput [optional] Extraction storage
* @param ?object $oParent [internal] Latest parent
* @param bool $return [internal] Return indicator
*
* @return array|void Derived category tree structure
*/
function buildTree($oCat, &$aOutput = [], $oParent = null, $return = true) {
// scalar properties
$aOutput[$actId]['id'] = $actId = $oCat->getId();
$aOutput[$actId]['title'] = $oCat->getTitle();
$aOutput[$actId]['url'] = $oCat->getLink();
// parent property (object)
if ( $oParent !== null ) {
$aOutput[$actId]['parent'] = $oParent->getId();
} elseif ( $parent = $oCat->getParentCategory() ) {
$aOutput[$actId]['parent'] = $parent->getId();
} else {
$aOutput[$actId]['parent'] = ':globalroot'
}
// subcats property ([object])
if ( $oCat->getHasVisibleSubCats() ) {
foreach ( $oCat->getSubCats() as $sId => $oSubCat ) {
$aOutput[$actId]['children'][$sId] = [
'id' => $sId,
'title' => $oSubCat->getTitle(),
'url' => $oSubCat->getLink(),
'parent' => $actId,
'children' => null
];
if ( $oSubCat->getHasVisibleSubCats() ) {
foreach ( $oSubCat->getSubCats() as $oSubChild ) {
buildTree($oSubChild, $aOutput[$actId]['children'][$sId]['children'], $oSubCat, false);
}
}
}
} else {
$aOutput[$actId]['children'] = null;
}
if ( $return ) {
return $aOutput;
}
}
Usage:
$aTree = buildTree($oBaseCat);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.