File: D:/HostingSpaces/SBogers10/shop.komma.nl/app/Categories/Kms/CategoryTreeService.php
<?php declare(strict_types=1);
namespace App\Categories\Kms;
use App\Categories\Models\Category;
use App\Http\Middleware\WildcardResolver;
use Illuminate\Database\Eloquent\Collection;
use Komma\KMS\Core\AbstractTranslatableModel;
use Komma\KMS\Core\Tree\NestedSets\Nodes\TreeModelInterface;
use Komma\KMS\Core\Tree\TreeService;
class CategoryTreeService extends TreeService
{
public function __construct()
{
$this->classModelName = Category::class;
}
/**
* Returns a root category with all children till the given depth.
* And every category will have a transient "is_current" attribute set to true if it matches the tail of the url.
* They will also have a transient attribute called "link" which refers to the category detail page.
*
* @param int $depth
* @return Category|TreeModelInterface|mixed
*/
public function treeWithTranslationsLinksAndActiveStates($depth = 0)
{
/** @var TreeModelInterface|Category $rootCategory */
$rootCategory = $this->classModelName::where('lft', '=', 1)->first();
$rootCategory->findChildren($depth);
$allIds = $this->getAllIdsFromCategoryTree($rootCategory);
$eagerLoadedCategoriesById = Category::whereIn('id', $allIds)->with('translation')->get()->keyBy('id');
$rootCategory = $this->remapCategoriesWithEagerLoadedOnes($rootCategory, $eagerLoadedCategoriesById);
$rootCategory = $this->addLinkInformation($rootCategory);
if(WildcardResolver::getLatestResolvedWildCardRoute()) {
$breadcrumbCategories = $this->breadcrumbToStack($rootCategory, WildcardResolver::getLatestResolvedWildCardRoute()->tail);
foreach ($breadcrumbCategories as $category) $category->is_current = true;
}
return $rootCategory;
}
/**
* Returns all ids of categories in the tree of categories
*
* @param Category $category
* @param array $accumulatorArray
* @return array
*/
private function getAllIdsFromCategoryTree(Category $category, $accumulatorArray = []) {
$accumulatorArray[] = $category->id;
foreach($category->getChildren() as $childCategory) {
/** @var Category $childCategory */
$childCategoryChildren = $this->getAllIdsFromCategoryTree($childCategory);
$accumulatorArray = array_merge($accumulatorArray, $childCategoryChildren);
}
return $accumulatorArray;
}
/**
* Receives a category that may have children, loops over all it's children and replaces those children
* with the same categories from the eager loaded categories. So that we have an eager loaded tree!
*
* @param Category $category
* @param Collection $eagerLoadedCategories
* @return mixed
*/
private function remapCategoriesWithEagerLoadedOnes(Category $category, Collection &$eagerLoadedCategories) {
$eagerLoadedCategory = $eagerLoadedCategories->get($category->id);
$eagerLoadedCategory->setChildren($category->getChildren());
$eagerLoadedCategories = $eagerLoadedCategories->forget($category->id);
$remappedChildren = [];
foreach($eagerLoadedCategory->getChildren() as $childCategory) {
/** @var Category $childCategory */
$remappedChildren[] = $this->remapCategoriesWithEagerLoadedOnes($childCategory, $eagerLoadedCategories);
}
$eagerLoadedCategory->setChildren($remappedChildren);
return $eagerLoadedCategory;
}
/**
* Gives each category a link to its wildcard route.
* And determines if the request url matches the current categorie and thus if it is active. It stores that result in the is_current transient property.
*
* @param Category $category
* @return Category
*/
private function addLinkInformation(Category $category) {
if(!$category->link) $category->link = WildcardResolver::getWildCardIndexRouteForPageWithCodeName('categories')->alias;
if($category->translation && !empty($category->translation->slug)) $category->link .= '/'.$category->translation->slug;
foreach($category->getChildren() as $childCategory) {
$childCategory->link = $category->link;
$this->addLinkInformation($childCategory);
}
return $category;
}
/**
* TODO May be a candidate to put in the tree service directly
*
* @param TreeModelInterface $category
* @param string $breadcrumb a/string/that/looks/like/this/and/represents/a/breadcrumb
* @param array $stack Variable used for recursion. Not to be used by callers of this function, except this function.
* @return array
*/
public function breadcrumbToStack(TreeModelInterface $category, string $breadcrumb, $stack = []) {
$parts = explode('/', $breadcrumb);
$currentPart = array_shift($parts);
$stack[] = $category;
foreach ($category->getChildren() as $child)
{
/** @var TreeModelInterface|AbstractTranslatableModel $child */
if($child->translation->slug == $currentPart) {
if(count($parts) > 0) $stack = $this->breadcrumbToStack($child, implode('/', $parts), $stack);
else $stack[] = $child;
break;
}
}
return $stack;
}
/**
* TODO may be a candidate to put in the tree service directly
*
* @param TreeModelInterface|AbstractTranslatableModel $category
* @param array $stack
* @return array
*/
public function flatten(TreeModelInterface $category, array $stack = []) {
$stack[] = $category;
foreach($category->getChildren() as $childCategory) {
$stack = $this->flatten($childCategory, $stack);
}
return $stack;
}
}